Skip to content

Feat/error logging system#11

Draft
ammodev wants to merge 55 commits intoversion/1.21.11from
feat/error-logging-system
Draft

Feat/error logging system#11
ammodev wants to merge 55 commits intoversion/1.21.11from
feat/error-logging-system

Conversation

@ammodev
Copy link
Contributor

@ammodev ammodev commented Feb 8, 2026

No description provided.

TheBjoRedCraft and others added 27 commits February 8, 2026 02:11
…AuthenticationListener and PlayerConnectListener
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…anup

Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…error handling

Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…structure' into copilot/implement-error-logging-structure
…gin check to listener

Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…logging-structure

Implement system-wide error logging with automatic deduplication and MCCoroutine integration
Copilot AI review requested due to automatic review settings February 8, 2026 15:19
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a cross-platform (Paper + Velocity) error logging system that persists player-facing error codes and system exceptions to the backend database, and adds admin commands to inspect logged errors.

Changes:

  • Added global + coroutine exception handling that logs “system errors” to the database (with occurrence aggregation).
  • Added player error-code logging via SurfCoreApi.logError(...), plus shared disconnect message rendering that includes a copyable error code.
  • Added backend DB tables/repositories/services and Paper commands to view player/system error logs.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/listener/MCCoroutineExceptionListener.kt New Velocity listener to capture MCCoroutine exceptions and persist them as system errors
surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/auth/AuthenticationService.kt Renames authentication service and logs failed authentication attempts via SurfCore API
surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/auth/AuthenticationListener.kt Adds blocked-domain error-code logging + new disconnect message rendering
surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/api/SurfCoreApiVelocityImpl.kt Implements new SurfCore API error logging methods on Velocity
surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/VelocityMain.kt Installs global error handler and registers new exception listener
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/permission/PermissionRegistry.kt Adds permission for new error-related commands
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/listener/PlayerConnectListener.kt Logs player-data load failures and kicks with error-code disconnect message
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/listener/MCCoroutineExceptionListener.kt New Paper listener to capture MCCoroutine exceptions and persist as system errors
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/command/TestErrorCommand.kt New command to trigger test errors for validating the logging pipeline
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/command/SurfCoreSystemErrorCommand.kt New command to list/view aggregated system errors
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/command/CoreErrorCommand.kt New command to view player error-code logs
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/api/SurfCoreApiPaperImpl.kt Implements new SurfCore API error logging methods on Paper
surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/PaperMain.kt Installs global error handler, registers new listener, and registers new commands
surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/util/core-common-util.kt Adds reusable disconnect message builders including error-code rendering
surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/player/SurfCoreErrorLoggingService.kt New common service interface for persisting player error-code logs
surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/error/SurfCoreSystemErrorService.kt New common service interface for persisting aggregated system errors + location extraction
surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/error/GlobalErrorHandler.kt New global uncaught exception handler that persists system errors asynchronously
surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/SurfCoreApiImpl.kt Adds shared SurfCore API “awaiting” error logging methods + code generation passthrough
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/table/SurfCoreSystemErrorTable.kt New DB table for aggregated system errors (message/location/server unique key)
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/table/SurfCoreErrorLogsTable.kt New DB table for player error-code logs
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/service/SurfCoreSystemErrorServiceImpl.kt Backend implementation of system error persistence service
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/service/SurfCoreErrorLoggingServiceImpl.kt Backend implementation of player error-code logging service
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/repository/SurfCoreSystemErrorRepository.kt Repository for upserting system errors and incrementing occurrence counts
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/repository/SurfCoreErrorLoggingRepository.kt Repository for inserting and retrieving player error-code logs
surf-core-backend/src/main/kotlin/dev/slne/surf/core/fallback/DatabaseLoaderImpl.kt Registers new tables for schema creation/migration
surf-core-api/surf-core-api-common/src/main/kotlin/dev/slne/surf/core/api/common/error/SurfCoreSystemError.kt New API DTO for system errors
surf-core-api/surf-core-api-common/src/main/kotlin/dev/slne/surf/core/api/common/error/SurfCoreError.kt New API DTO for player error-code logs
surf-core-api/surf-core-api-common/src/main/kotlin/dev/slne/surf/core/api/common/SurfCoreApi.kt Adds new error logging API surface (sync + awaiting variants)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 49 to 53
fun shutdown() {
runBlocking {
try {
errorScope.coroutineContext.job.cancelAndJoin()
logger.info("Global error handler shutdown complete")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shutdown() cancels the entire errorScope job (cancelAndJoin()), which also cancels any in-flight DB logging coroutines and may drop errors during shutdown. Consider awaiting completion of existing children (optionally with a timeout) before cancelling the scope, if you want best-effort persistence.

Copilot uses AI. Check for mistakes.
if (row.location.length > 50) {
spacer("...")
}
clickRunsCommand("/systemerror view ${row.id}")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The click action runs /systemerror view <id>, but the registered command is surfcoresystemerror view <id>. Update the click command (or add an alias) so clicking a row opens the details view.

Suggested change
clickRunsCommand("/systemerror view ${row.id}")
clickRunsCommand("/surfcoresystemerror view ${row.id}")

Copilot uses AI. Check for mistakes.
it[SurfCoreSystemErrorTable.server] = server
it[SurfCoreSystemErrorTable.firstOccurred] = existingError?.firstOccurred ?: now
it[SurfCoreSystemErrorTable.lastOccurred] = now
it[SurfCoreSystemErrorTable.occurrenceCount] = (existingError?.occurrenceCount ?: 0) + 1
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

occurrenceCount is computed from a previously read existingError and then written back as existing + 1. Under concurrent logging of the same error this can lose increments (two transactions can both read N and write N+1). Prefer an atomic DB-side increment in the upsert conflict update (e.g., occurrence_count = occurrence_count + 1).

Suggested change
it[SurfCoreSystemErrorTable.occurrenceCount] = (existingError?.occurrenceCount ?: 0) + 1
it[SurfCoreSystemErrorTable.occurrenceCount] =
if (existingError == null) 1 else SurfCoreSystemErrorTable.occurrenceCount + 1

Copilot uses AI. Check for mistakes.
Comment on lines 17 to 20
fun onMCCoroutineException(event: MCCoroutineExceptionEvent) {
event.result = ResultedEvent.GenericResult.denied()

logger.log(Level.SEVERE, "MCCoroutine exception occurred", event.exception)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Velocity MCCoroutine exception handler logs/persists all exceptions. Consider ignoring CancellationException (as done in the Paper listener) to avoid noisy logs/DB writes when coroutines are cancelled as part of normal control flow.

Copilot uses AI. Check for mistakes.
val errorCode = varchar("error_code", 7)
val errorMessage = text("error_message")
val server = varchar("server", 255)
val timestamp = offsetDateTime("timestamp")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table is queried by error_code (getError(code)) and by player_uuid (getErrors(uuid)), but defines no indexes/uniqueness. Consider adding indexes (and likely a uniqueness constraint on error_code if codes must be unambiguous for support).

Suggested change
val timestamp = offsetDateTime("timestamp")
val timestamp = offsetDateTime("timestamp")
init {
// Index for lookups by player UUID (e.g., getErrors(uuid))
index(isUnique = false, columns = arrayOf(playerUuid))
// Index for lookups by error code (e.g., getError(code))
index(isUnique = false, columns = arrayOf(errorCode))
}

Copilot uses AI. Check for mistakes.
Comment on lines 23 to 27
runBlocking {
launch {
try {
val surfCoreSystemError = surfCoreSystemErrorService.logError(
throwable = event.exception,
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runBlocking inside an event callback can block the Velocity event thread if the DB is slow/unavailable, potentially stalling the proxy. Prefer launching the DB logging asynchronously on a plugin/IO scope and remove the redundant launch nested inside runBlocking.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +49
val errorCode = surfCoreApi.logError(
event.player.uniqueId,
code,
"Blocked connection from domain: $domain"
)
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surfCoreApi.logError(playerUuid, code, message) returns Unit, so val errorCode = ... is always Unit and unused. Either remove the variable assignment, or use the 2-arg overload (logError(uuid, message)) that returns the generated code and reuse it in the disconnect message.

Copilot uses AI. Check for mistakes.
Comment on lines 33 to 38
override fun logError(playerUUid: UUID, message: String): String {
val code = surfCoreErrorLoggingService.generateCode()

plugin.launch {
surfCoreErrorLoggingService.logError(
playerUUid,
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter name playerUUid is inconsistent with the API (playerUuid). Rename it to playerUuid for consistency/readability across implementations.

Suggested change
override fun logError(playerUUid: UUID, message: String): String {
val code = surfCoreErrorLoggingService.generateCode()
plugin.launch {
surfCoreErrorLoggingService.logError(
playerUUid,
override fun logError(playerUuid: UUID, message: String): String {
val code = surfCoreErrorLoggingService.generateCode()
plugin.launch {
surfCoreErrorLoggingService.logError(
playerUuid,

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +33
val surfCoreSystemError = surfCoreSystemErrorService.logError(
throwable = throwable,
server = SurfServer.current().name
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleError uses SurfServer.current().name, which can throw if the current server hasn't been registered in surfServerService yet (e.g., very early startup). Use a non-throwing source for the server name (like surfCoreApi.getCurrentServerName() or a safe fallback) so the global handler can still persist errors during initialization.

Suggested change
val surfCoreSystemError = surfCoreSystemErrorService.logError(
throwable = throwable,
server = SurfServer.current().name
val serverName = try {
SurfServer.current().name
} catch (e: Exception) {
logger.log(
Level.WARNING,
"Failed to resolve current SurfServer name while handling an error; using fallback name.",
e
)
"unknown"
}
val surfCoreSystemError = surfCoreSystemErrorService.logError(
throwable = throwable,
server = serverName

Copilot uses AI. Check for mistakes.
TheBjoRedCraft and others added 7 commits February 8, 2026 17:37
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…or-system-errors

Add 10-character error codes to system errors
Copilot AI review requested due to automatic review settings February 8, 2026 16:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

success("Triggering exception with custom context...")

plugin.launch(plugin.globalRegionDispatcher) {
error("Test exception with custom context from /testerror customContext")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown test exception message references /testerror customContext, but the actual command path is /coreerror system test customContext. Update the string to match so logs are easier to correlate.

Suggested change
error("Test exception with custom context from /testerror customContext")
error("Test exception with custom context from /coreerror system test customContext")

Copilot uses AI. Check for mistakes.
Comment on lines 33 to 36
(SurfCoreSystemErrorTable.errorMessage eq message) and
(SurfCoreSystemErrorTable.location eq location) and
(SurfCoreSystemErrorTable.server eq server) and
(SurfCoreSystemErrorTable.lastOccurred lessEq now.minusDays(1))
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existingError lookup uses lastOccurred <= now.minusDays(1), which matches errors last seen more than 24h ago. That inverts typical “dedupe/count within last 24h” behavior and will create new rows for duplicates happening within 24h; adjust the comparison/window logic.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +17
@Subscribe
fun onMCCoroutineException(event: MCCoroutineExceptionEvent) {
event.result = ResultedEvent.GenericResult.denied()
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the Paper listener, this Velocity listener does not filter out cancellation-related exceptions (e.g., CancellationException). Those are typically expected and can produce noisy severe logs; consider ignoring them early in the handler.

Copilot uses AI. Check for mistakes.
success("Triggering coroutine exception...")
}

error("Test coroutine exception from /testerror coroutine")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown test exception message references /testerror coroutine, but the actual command path is /coreerror system test coroutine. Update the string to match so logs are easier to correlate.

Suggested change
error("Test coroutine exception from /testerror coroutine")
error("Test coroutine exception from /coreerror system test coroutine")

Copilot uses AI. Check for mistakes.
}

plugin.launch {
error("Test exception from plugin.launch in /testerror launch")
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown test exception message references /testerror launch, but the actual command path is /coreerror system test launch. Update the string to match so logs are easier to correlate.

Suggested change
error("Test exception from plugin.launch in /testerror launch")
error("Test exception from plugin.launch in /coreerror system test launch")

Copilot uses AI. Check for mistakes.
}

val code = surfCoreErrorLoggingService.generateCode()
val errorCode = surfCoreApi.logError(
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surfCoreApi.logError(playerUuid, code, message) returns Unit, but its result is assigned to val errorCode. This will not compile (type mismatch) and the variable is unused; remove the assignment or call the String-returning overload if you need a code.

Suggested change
val errorCode = surfCoreApi.logError(
surfCoreApi.logError(

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +28
runCatching {
runBlocking {
launch {
runCatching {
val surfCoreSystemError = surfCoreSystemErrorService.logError(
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runBlocking inside this event handler will block the Velocity event thread until the DB logging completes (and the nested launch is redundant because runBlocking waits for its children). Prefer launching the logging work on a background scope/dispatcher without blocking this handler.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +110
Thread {
error("Test thread exception from /testerror thread")
}.start()
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown test exception message references /testerror thread, but the actual command path is /coreerror system test thread. Update the string to match so logs are easier to correlate.

Copilot uses AI. Check for mistakes.
TheBjoRedCraft and others added 7 commits February 8, 2026 17:59
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
Co-authored-by: TheBjoRedCraft <143264463+TheBjoRedCraft@users.noreply.github.com>
…st-command

Add filtering support to error list commands
Copilot AI review requested due to automatic review settings February 8, 2026 17:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +71 to +75
.map { row ->
SurfCoreSystemError(
uuid = row[SurfCoreSystemErrorTable.uuid],
errorCode = row[SurfCoreSystemErrorTable.errorCode],
errorMessage = row[SurfCoreSystemErrorTable.errorMessage],
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the upsert, the code re-selects by (errorMessage, location, server) without ordering or a time window. If multiple rows exist for the same triple (e.g., after the 24h window where a new row is inserted), firstOrNull() can return an older record and report the wrong uuid/errorCode. Select deterministically (e.g., by the uuid/errorCode you just wrote, or order by lastOccurred desc and limit 1).

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +42
)
.map { row ->
SurfCoreSystemError(
uuid = row[SurfCoreSystemErrorTable.uuid],
errorCode = row[SurfCoreSystemErrorTable.errorCode],
errorMessage = row[SurfCoreSystemErrorTable.errorMessage],
stacktrace = row[SurfCoreSystemErrorTable.stacktrace],
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “dedupe within 24h” logic does a SELECT then an UPSERT, but there is no uniqueness constraint on (errorMessage, location, server) to prevent concurrent log calls from inserting multiple rows for the same error. If you need deduplication, consider enforcing a unique key that matches the dedupe criteria (or otherwise serialize updates, e.g., via an explicit UPDATE/INSERT strategy).

Copilot uses AI. Check for mistakes.
Comment on lines +97 to +106
suspend fun getError(code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable.selectAll().where(SurfCoreErrorLogsTable.errorCode eq code).map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.firstOrNull()
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getError(code) assumes error_code uniquely identifies a single player error log, but the table schema does not enforce uniqueness on error_code. Even if collisions are rare, this can return the wrong player's error. Either enforce uniqueness at the DB level (and retry on conflict when generating codes) or change the lookup API to include a disambiguator (e.g., player UUID + code, or code + timestamp).

Suggested change
suspend fun getError(code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable.selectAll().where(SurfCoreErrorLogsTable.errorCode eq code).map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.firstOrNull()
suspend fun getError(playerUuid: UUID, code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable
.selectAll()
.where(
SqlExpressionBuilder.run {
(SurfCoreErrorLogsTable.playerUuid eq playerUuid) and
(SurfCoreErrorLogsTable.errorCode eq code)
}
)
.map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}
.firstOrNull()

Copilot uses AI. Check for mistakes.
TheBjoRedCraft and others added 6 commits February 8, 2026 18:23
…d-fix-server-info-command' into feat/error-logging-system

# Conflicts:
#	surf-core-api/surf-core-api-common/src/main/kotlin/dev/slne/surf/core/api/common/SurfCoreApi.kt
#	surf-core-core/surf-core-core-common/src/main/kotlin/dev/slne/surf/core/core/common/SurfCoreApiImpl.kt
#	surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/PaperMain.kt
#	surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/api/SurfCoreApiPaperImpl.kt
#	surf-core-paper/src/main/kotlin/dev/slne/surf/core/paper/listener/PlayerConnectListener.kt
#	surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/VelocityMain.kt
#	surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/api/SurfCoreApiVelocityImpl.kt
#	surf-core-velocity/src/main/kotlin/dev/slne/surf/core/velocity/auth/AuthenticationListener.kt
Copilot AI review requested due to automatic review settings February 22, 2026 09:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +71 to +76
authenticationService.continuations[event.player.uniqueId] = continuation
event.player.requestCookie(authenticationService.key)
transfers.add(event.player.uniqueId)

authentificationService.continuations[event.player.uniqueId] = continuation
event.player.requestCookie(authentificationService.key)
authenticationService.continuations[event.player.uniqueId] = continuation
event.player.requestCookie(authenticationService.key)
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authenticationService.continuations[...] = continuation and requestCookie(...) are executed twice for the same login (lines 71–76). This will trigger duplicate cookie requests and can lead to hard-to-debug auth/continuation behavior. Remove the duplicated block so it only runs once.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +86
runBlocking {
val code = surfCoreApi.logError(
event.connection.audience.uuid(),
"Failed to load player data on AsyncPlayerPreLoginEvent"
)
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runBlocking inside AsyncPlayerConnectionConfigureEvent will block the async login/config thread even though surfCoreApi.logError(uuid, message) already returns a code immediately and logs asynchronously. Prefer calling logError(...) directly (no runBlocking) and disconnect right away with the returned code.

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +223

plugin.launch(plugin.globalRegionDispatcher) {
error("Test exception with custom context from /testerror customContext")
}
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the customContext test command, plugin.launch(...) is executed inside the sendText { ... } builder. Mixing side effects into component-building makes the flow harder to reason about and can cause unexpected behavior if the builder is reused; move the launch call outside the message-building lambda.

Suggested change
plugin.launch(plugin.globalRegionDispatcher) {
error("Test exception with custom context from /testerror customContext")
}
}
plugin.launch(plugin.globalRegionDispatcher) {
error("Test exception with custom context from /testerror customContext")

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +195
Thread {
error("Test thread exception from /testerror thread")
}.start()
}
}

literalArgument("coroutine") {
anyExecutorSuspend { executor, _ ->
executor.sendText {
appendSuccessPrefix()
success("Triggering coroutine exception...")
}

error("Test coroutine exception from /testerror coroutine")
}
}

literalArgument("launch") {
anyExecutor { executor, _ ->
executor.sendText {
appendSuccessPrefix()
success("Triggering exception in launch...")
}

plugin.launch {
error("Test exception from plugin.launch in /testerror launch")
}
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown test exceptions reference /testerror ... in their messages, but the command registered here is /coreerror .... This makes the log output misleading when operators try to reproduce an error; update the strings to match the actual command path.

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +52
val code = surfCoreErrorLoggingService.generateCode()
val errorCode = surfCoreApi.logError(
event.player.uniqueId,
code,
"Blocked connection from domain: $domain"
)
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the blocked-domain branch, the result of surfCoreApi.logError(...) is assigned to val errorCode, but this overload returns Unit and the variable is never used. This is confusing and looks like a leftover from an earlier API design—either remove the variable or switch to the logError(uuid, message): String overload if you actually need a code.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +72
val resultError = SurfCoreSystemErrorTable.selectAll()
.where(
(SurfCoreSystemErrorTable.errorMessage eq message) and
(SurfCoreSystemErrorTable.location eq location) and
(SurfCoreSystemErrorTable.server eq server)
)
.map { row ->
SurfCoreSystemError(
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the upsert, the code re-selects by (message, location, server) without ordering. If there are multiple rows with the same triple (e.g., older than 24h plus a new one), firstOrNull() may return an arbitrary/old row instead of the one just inserted/updated. Re-select by the uuid/errorCode you upserted (or return values from the upsert) to make the result deterministic.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants