diff --git a/.gitignore b/.gitignore index 98b6eef0..1d00fc9e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ feature_toggle.ini Logs/numusers.log Database.db AoServerTools.dll +AOACServer.pdb diff --git a/AOACServer.dll b/AOACServer.dll new file mode 100644 index 00000000..3633eee6 Binary files /dev/null and b/AOACServer.dll differ diff --git a/Codigo/AntiCheat.bas b/Codigo/AntiCheat.bas new file mode 100644 index 00000000..e2c281ca --- /dev/null +++ b/Codigo/AntiCheat.bas @@ -0,0 +1,177 @@ +Attribute VB_Name = "AntiCheat" +Option Explicit + + +Public Enum e_ACInitResult + eOk = 0 + eFailedPlatform + eFAiledConnectAC +End Enum + +Private Type SINGLESTRINGPARAM + Ptr As Long + Len As Long +End Type + +Public Type t_AntiCheatCallbacks + SendToClient As Long + LogMessage As Long + RegisterRemoteUserId As Long + ActionRequired As Long +End Type + +Public Enum e_ActionRequiredType + eEOS_ACCCA_Invalid = 0 + eEOS_ACCCA_RemovePlayer = 1 +End Enum + +Public Enum e_ActionRequiredReason + eEOS_ACCCAR_Invalid = 0 + eEOS_ACCCAR_InternalError = 1 + eEOS_ACCCAR_InvalidMessage = 2 + eEOS_ACCCAR_AuthenticationFailed = 3 + eEOS_ACCCAR_NullClient = 4 + eEOS_ACCCAR_HeartbeatTimeout = 5 + eEOS_ACCCAR_ClientViolation = 6 + eEOS_ACCCAR_BackendViolation = 7 + eEOS_ACCCAR_TemporaryCooldown = 8 + eEOS_ACCCAR_TemporaryBanned = 9 + eEOS_ACCCAR_PermanentBanned = 10 +End Enum + +Public Enum EOS_ELogLevel + EOS_LOG_Off = 0 + EOS_LOG_Fatal = 100 + EOS_LOG_Error = 200 + EOS_LOG_Warning = 300 + EOS_LOG_Info = 400 + EOS_LOG_Verbose = 500 + EOS_LOG_VeryVerbose = 600 +End Enum + +Private Declare Function InitializeAC Lib "AOACServer.dll" (ByRef Callbacks As t_AntiCheatCallbacks) As Long +Private Declare Sub UnloadAC Lib "AOACServer.dll" () +Private Declare Sub Update Lib "AOACServer.dll" () +Private Declare Sub AddPendingRegister Lib "AOACServer.dll" (ByRef UserReference As t_UserReference) +Private Declare Function QueryAndRemoveOldPendingRegistey Lib "AOACServer.dll" (ByRef UserReference As t_UserReference, ByVal ElapsedThreshold As Long) As Long +Private Declare Sub UnRegisterClient Lib "AOACServer.dll" (ByVal UserIndex As Integer) +Private Declare Sub HandleRemoteMessage Lib "AOACServer.dll" (ByRef UserReference As t_UserReference, ByRef Data As Byte, ByVal DataSize As Integer) + +Dim EnableAnticheat As Boolean + +Private Function GetStringFromPtr(ByVal Ptr As Long, ByVal size As Long) As String + Dim Buffer() As Byte + ReDim Buffer(0 To (size - 1)) As Byte + CopyMemory Buffer(0), ByVal Ptr, size + GetStringFromPtr = StrConv(Buffer, vbUnicode) +End Function + +Private Function FARPROC(pfn As Long) As Long + FARPROC = pfn +End Function + +Public Sub InitializeAntiCheat() +On Error GoTo InitializeAC_Err + EnableAnticheat = IsFeatureEnabled("anti-cheat") + If EnableAnticheat Then + Dim InitResult As e_ACInitResult + Dim Callbacks As t_AntiCheatCallbacks + Callbacks.SendToClient = FARPROC(AddressOf SendToClientCB) + Callbacks.LogMessage = FARPROC(AddressOf LogMessageCB) + Callbacks.RegisterRemoteUserId = FARPROC(AddressOf RegisterRemoteUserIdCb) + Callbacks.ActionRequired = FARPROC(AddressOf ClientActionRequired) + InitResult = InitializeAC(Callbacks) + If InitResult <> eOk Then + Call MsgBox("El juego se inicio sin activar el anti cheat, debe activarlo para poder conectarse a los servidores.") + End If + End If + Exit Sub +InitializeAC_Err: + Call TraceError(Err.Number, Err.Description, "AOAC.InitializeAC", Erl) +End Sub + +Public Sub OnNewPlayerConnect(ByVal UserIndex As Integer) + If EnableAnticheat Then + Dim UserRef As t_UserReference + Call SetUserRef(UserRef, UserIndex) + Call AddPendingRegister(UserRef) + Call WriteAntiCheatStartSeassion(UserIndex) + End If +End Sub + +Public Sub KickUnregisteredPlayers() + If EnableAnticheat Then + Dim UserRef As t_UserReference + Dim Result As Long + Result = QueryAndRemoveOldPendingRegistey(UserRef, 10000) + If Result > 0 And IsValidUserRef(UserRef) Then + Call modNetwork.Kick(UserList(UserRef.ArrayIndex).ConnectionDetails.ConnID, "Anticheat detection timeout") + End If + End If +End Sub + +Public Sub AntiCheatUpdate() + If EnableAnticheat Then + Call Update + Call KickUnregisteredPlayers + End If +End Sub + +Public Sub UnloadAntiCheat() + If EnableAnticheat Then + Call UnloadAC + End If +End Sub +Public Sub SendToClientCB(ByRef TargetUser As t_UserReference, ByVal Data As Long, ByVal DataSize As Long) + If EnableAnticheat Then + If IsValidUserRef(TargetUser) Then + Call WriteAntiCheatMessage(TargetUser.ArrayIndex, Data, DataSize) + End If + End If +End Sub + +Public Sub HandleAntiCheatServerMessage(ByVal UserIndex As Integer, ByRef Data() As Byte) + If EnableAnticheat Then + Dim UserRef As t_UserReference + Call SetUserRef(UserRef, UserIndex) + Call HandleRemoteMessage(UserRef, Data(0), UBound(Data)) + End If +End Sub + +Public Sub LogMessageCB(ByRef Message As SINGLESTRINGPARAM, ByVal LogLevel As Long) + Dim MessageStr As String + If Message.Len > 0 Then + MessageStr = GetStringFromPtr(Message.Ptr, Message.Len) + End If + If LogLevel Then + End If + If LogLevel < EOS_LOG_Warning Then + Call LogThis(0, "Anticheat: " & MessageStr, vbLogEventTypeError) + End If +End Sub + +Public Sub RegisterRemoteUserIdCb(ByRef UserRef As t_UserReference, ByRef Id As SINGLESTRINGPARAM) + Dim IdStr As String + If Id.Len > 0 Then + IdStr = GetStringFromPtr(Id.Ptr, Id.Len) + End If + If IsValidUserRef(UserRef) Then + Call SaveEpicLogin(IdStr, UserRef.ArrayIndex) + End If +End Sub + +Public Sub ClientActionRequired(ByRef UserRef As t_UserReference, ByVal Action As Long, ByVal ReasonCode As Long, ByRef ReasonString As SINGLESTRINGPARAM) + Dim ReasonStr As String + If ReasonString.Len > 0 Then + ReasonStr = GetStringFromPtr(ReasonString.Ptr, ReasonString.Len) + End If + If Action = eEOS_ACCCA_RemovePlayer And IsValidUserRef(UserRef) Then + Call modNetwork.Kick(UserList(UserRef.ArrayIndex).ConnectionDetails.ConnID, ReasonStr) + End If +End Sub + +Public Sub OnPlayerDisconnect(ByVal UserIndex As Integer) + If EnableAnticheat Then + Call UnRegisterClient(UserIndex) + End If +End Sub diff --git a/Codigo/General.bas b/Codigo/General.bas index 79d0baa6..c7e8fd39 100644 --- a/Codigo/General.bas +++ b/Codigo/General.bas @@ -737,9 +737,7 @@ Sub Main() 'Ocultar 330 Call frmMain.InitMain(HideMe) - - - + Call InitializeAntiCheat 332 tInicioServer = GetTickCount() @@ -775,6 +773,8 @@ Sub Main() Call PerformTimeLimitCheck(PerformanceTimer, "General Update Effects over time") DoEvents Call PerformTimeLimitCheck(PerformanceTimer, "Do events") + Call AntiCheatUpdate + Call PerformTimeLimitCheck(PerformanceTimer, "Update anti cheat") ' Unlock main loop for maximum throughput but it can hog weak CPUs. #If UNLOCK_CPU = 0 Then Call Sleep(1) @@ -784,7 +784,6 @@ Sub Main() Call UnitClient.Poll #End If Wend - Call LogThis(0, "Closing the server " & Now, vbLogEventTypeInformation) Exit Sub Handler: @@ -2243,7 +2242,7 @@ Public Sub CerrarServidor() 110 Call CloseSocket(LoopC) End If Next - + Call UnloadAntiCheat 112 If Database_Enabled Then Database_Close 124 End diff --git a/Codigo/Modulo_UsUaRiOs.bas b/Codigo/Modulo_UsUaRiOs.bas index 7e986739..a5346b64 100644 --- a/Codigo/Modulo_UsUaRiOs.bas +++ b/Codigo/Modulo_UsUaRiOs.bas @@ -836,6 +836,7 @@ On Error GoTo Complete_ConnectUser_Err End If Call RestoreDCUserCache(UserIndex) Call CustomScenarios.UserConnected(userIndex) + Call AntiCheat.OnNewPlayerConnect(UserIndex) End With diff --git a/Codigo/PacketId.bas b/Codigo/PacketId.bas index 2727d62b..fcd7678b 100644 --- a/Codigo/PacketId.bas +++ b/Codigo/PacketId.bas @@ -202,6 +202,8 @@ Public Enum ServerPacketID eRequestTelemetry eUpdateCharValue 'updates some char index value based on enum eSendClientToggles 'Get active feature Toggles from server + eAntiCheatMessage + eAntiCheatStartSession eReportLobbyList #If PYMMO = 0 Then eAccountCharacterList @@ -533,6 +535,7 @@ Public Enum ClientPacketID eSendTelemetry eSetHotkeySlot eUseHKeySlot + eAntiCheatMessage eRequestLobbyList #If PYMMO = 0 Then eCreateAccount diff --git a/Codigo/Protocol.bas b/Codigo/Protocol.bas index b7e98c38..d0f94cec 100644 --- a/Codigo/Protocol.bas +++ b/Codigo/Protocol.bas @@ -871,6 +871,8 @@ On Error Resume Next Call HandleSetHotkeySlot(UserIndex) Case ClientPacketID.eUseHKeySlot Call HandleUseHKeySlot(UserIndex) + Case ClientPacketID.eAntiCheatMessage + Call HandleAntiCheatMessage(UserIndex) #If PYMMO = 0 Then Case ClientPacketID.eCreateAccount Call HandleCreateAccount(ConnectionId) @@ -10725,9 +10727,19 @@ HandleUseHKeySlot_Err: Call TraceError(Err.Number, Err.Description, "Protocol.HandleUseHKeySlot", Erl) End Sub + +Public Sub HandleAntiCheatMessage(ByVal UserIndex As Integer) +On Error GoTo AntiCheatMessage_Err: + Dim Data() As Byte + Call Reader.ReadSafeArrayInt8(Data) + Call HandleAntiCheatServerMessage(UserIndex, Data) + Exit Sub +AntiCheatMessage_Err: + Call TraceError(Err.Number, Err.Description, "Protocol.AntiCheatMessage", Erl) +End Sub + Public Sub HendleRequestLobbyList(ByVal UserIndex As Integer) On Error GoTo HendleRequestLobbyList_Err: - Call WriteUpdateLobbyList(UserIndex) Exit Sub HendleRequestLobbyList_Err: diff --git a/Codigo/Protocol_Writes.bas b/Codigo/Protocol_Writes.bas index 6f1f3d1f..3c5fef61 100644 --- a/Codigo/Protocol_Writes.bas +++ b/Codigo/Protocol_Writes.bas @@ -5854,6 +5854,30 @@ PrepareActiveToggles_Err: Call TraceError(Err.Number, Err.Description, "Argentum20Server.Protocol_Writes.PrepareActiveToggles", Erl) End Function +Public Sub WriteAntiCheatMessage(ByVal UserIndex As Integer, ByVal Data As Long, ByVal DataSize As Long) + On Error GoTo WriteAntiCheatMessage_Err + Dim Buffer() As Byte + ReDim Buffer(0 To (DataSize - 1)) As Byte + CopyMemory Buffer(0), ByVal Data, DataSize + Call Writer.WriteInt16(ServerPacketID.eAntiCheatMessage) + Call Writer.WriteSafeArrayInt8(Buffer) + Call modSendData.SendData(ToIndex, UserIndex) + Exit Sub +WriteAntiCheatMessage_Err: + Call Writer.Clear + Call TraceError(Err.Number, Err.Description, "Argentum20.Protocol_Writes.WriteAntiCheatMessage", Erl) +End Sub + +Public Sub WriteAntiCheatStartSeassion(ByVal UserIndex As Integer) + On Error GoTo WriteAntiStartSeassion_Err + Call Writer.WriteInt16(ServerPacketID.eAntiCheatStartSession) + Call modSendData.SendData(ToIndex, UserIndex) + Exit Sub +WriteAntiStartSeassion_Err: + Call Writer.Clear + Call TraceError(Err.Number, Err.Description, "Argentum20.Protocol_Writes.WriteAntiStartSeassion", Erl) +End Sub + Public Sub WriteUpdateLobbyList(ByVal UserIndex As Integer) On Error GoTo WriteUpdateLobbyList_Err Dim IdList() As Integer diff --git a/Codigo/TCP.bas b/Codigo/TCP.bas index 70c05538..7c586e8c 100644 --- a/Codigo/TCP.bas +++ b/Codigo/TCP.bas @@ -1451,6 +1451,7 @@ End Sub Sub ResetUserSlot(ByVal UserIndex As Integer) On Error GoTo ResetUserSlot_Err Call SaveDCUserCache(UserIndex) + Call AntiCheat.OnPlayerDisconnect(UserIndex) With UserList(UserIndex) 100 .ConnectionDetails.ConnIDValida = False 102 .ConnectionDetails.ConnID = 0 diff --git a/Codigo/modDatabase.bas b/Codigo/modDatabase.bas index 8450a926..759d405d 100644 --- a/Codigo/modDatabase.bas +++ b/Codigo/modDatabase.bas @@ -1081,3 +1081,7 @@ End Sub Public Sub ResetLastLogoutAndIsLogged() Call Execute("Update user set last_logout = 0, is_logged = 0") End Sub + +Public Sub SaveEpicLogin(ByVal Id As String, ByVal UserIndex As Integer) + Call Query("insert or replace into epic_id_mapping (epic_id, user_id, last_login) values ( ?, ?, strftime('%s','now'))", Id, UserList(UserIndex).Id) +End Sub diff --git a/Codigo/modNetwork.bas b/Codigo/modNetwork.bas index af6c943f..d18ca15c 100644 --- a/Codigo/modNetwork.bas +++ b/Codigo/modNetwork.bas @@ -183,7 +183,6 @@ Private Sub OnServerConnect(ByVal Connection As Long, ByVal Address As String) End With 136 Call PendingConnections.Add(Connection, Connection) 138 Call modSendData.SendToConnection(Connection, PrepareConnected()) -140 Debug.Print "Handle new connection" Else 142 Call Kick(Connection, "El server se encuentra lleno en este momento. Disculpe las molestias ocasionadas.") End If diff --git a/EOSSDK-Win32-Shipping.dll b/EOSSDK-Win32-Shipping.dll new file mode 100644 index 00000000..dd7a0d40 Binary files /dev/null and b/EOSSDK-Win32-Shipping.dll differ diff --git a/ScriptsDB/20231101-01- add epic support.sql b/ScriptsDB/20231101-01- add epic support.sql new file mode 100644 index 00000000..a7f89ebe --- /dev/null +++ b/ScriptsDB/20231101-01- add epic support.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS "epic_id_mapping" ( + "epic_id" varchar(64) NOT NULL, + "user_id" integer NOT NULL, + "last_login" integer NOT NULL, + CONSTRAINT "fk_epic_id_mapping" FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE, + CREATE UNIQUE INDEX epic_id_mapping_idx ON data(epic_id, user_id); +); \ No newline at end of file diff --git a/Server.VBP b/Server.VBP index 5e295c65..c7095b31 100644 --- a/Server.VBP +++ b/Server.VBP @@ -121,6 +121,7 @@ Class=BonusDamageEffect; Codigo\EffectOverTime\BonusDamageEffect.cls Class=NavalBoarding; Codigo\Scenearios\NavalBoarding.cls Class=IInventoryInterface; Codigo\Scenearios\IInventoryInterface.cls Module=PacketId; Codigo\PacketId.bas +Module=AntiCheat; Codigo\AntiCheat.bas Module=InstanceManager; Codigo\InstanceManager.bas IconForm="frmMain" Startup="Sub Main"