Attribute VB_Name = "GameServer"
Option Explicit

'Packet filters

Private Enum O2_TYPES
    SessionRequest = 1
    KeyNotify = 2
    ACKRequired = 3
    ACKMessage = 4
    SyncClient = 5
    SyncServer = 6
    Disconnect = 7
    PacketCluster = 14
End Enum

Private Enum PACKET_TYPES
    SendingFile = 0
    YouAreHere = 1
    StartPeriodic = 2
    PlayersEntering = 3
    PlayerQuit = 4
    WeaponsFire = 5
    KillMessage = 6
    Chat_Message = 7
    LagUpdate = 8
    ScoreUpdate = 9
    PasswordAccept = 10
    SoccerGoal = 11
    SetFreq = 13
    MakeTurret = 14
    FlagClaim = 19
    FlagVictory = 20
    SecurityChecksum = 24
    AllowSend = 25
    ScoreReset = 26
    SetFreqAndShip = 29
    GotPrize = 32
    DoorFlip = 33
    FlagStatus = 34
    FlagReward = 35
    NothingNew = 39
    PositionData = 40
    MapFilename = 41
    KoTHReset = 44
    BallPositionData = 46
    ArenaList = 47
    FileDone = 49
End Enum

Private Enum CHAT_TYPES
    Arena_Message = 0
    Public_Message = 2
    Team_Message = 3
    TeamPrivate_Message = 4
    Private_Message = 5
    RemotePrivate_Message = 7
    ServerError_Message = 8
    Channel_Message = 9
End Enum

'Position and Weapon -packet related

Public Enum WEAPON_TYPES
    NoProjectile = 0
    Repel = 5
    Decoy = 6
    Burst = 7
    Thor = 8
    Bullet = 34
    Bomb = 420
End Enum

Public Const MAX_X = 16384
Public Const MAX_Y = 16384

'FTP session flags

Public Enum FTP_Types
    Body = 8
    Done = 9
    BodyExtended = 10
End Enum

'Connection failures returned by server at login (of the known varieties)

Public Enum FAILURES
    None = 0
    NEW_USER = 1
    BAD_PW = 2
    IP_BLOCK = 3
    ID_BLOCK = 4
    PERMISSION_ZONE = 5
    OBSCENE_NAME = 12
    NO_SCORES = 13
    ADVANCED_ONLY = 15
    NAME_MISSING = 16
End Enum

'Offset 2 handlers

Private Sub HandlerO2_02(UnencryptedPacket As String)
    'Server sent a key
    SendLogin BotList(CurrentBot).MyName, GetProfileString("Passwords", BotList(CurrentBot).MyName), BanID, ZoneBias, 5454, 0
    SendSync CurrentBot
End Sub

Private Sub HandlerO2_03(UnencryptedPacket As String)
On Error GoTo FlyPaper

    'Server sent a message requiring an ACK in response
    UDPSend CurrentBot, Chr(0) + Chr(4) + Mid(UnencryptedPacket, 3, 4), False

    Dim I As Integer, L As Long

    I = GetInteger(Mid(UnencryptedPacket, 3, 2))
    If I = -1 Then
        ClearACKs CurrentBot
    ElseIf ACKs(CurrentBot, I) = 1 Then
        Exit Sub
    Else
        ACKs(CurrentBot, I) = 1
    End If

    Call CheckBacklog(CurrentBot, UnencryptedPacket, I)

Exit Sub
FlyPaper:
    AddDebug CurrentBot, "Error detected in HandlerO2_03(" + PHex(Mid(UnencryptedPacket, 3, 4)) + "): " + Err.Description, RED
    Resume Next
End Sub

Private Sub HandlerO2_06(UnencryptedPacket As String)
    'Server accepted the synchronization packet
End Sub

Private Sub HandlerO2_07(UnencryptedPacket As String)
    'D/C
    AddDebug CurrentBot, "DISCONNECTED", RED
    UnloadBot CurrentBot, False
End Sub

Private Sub HandlerO2_0E(UnencryptedPacket As String)
    'Server sent a cluster of packets
    Dim O As String, I As Integer

    O = Mid(UnencryptedPacket, 3)
    Do
        I = Asc(Left(O, 1))
        Call HandlePacket(Mid(O, 2, I))
        O = Mid(O, I + 2)
    Loop Until O = ""
End Sub

Private Sub HandlerO2_04(UnencryptedPacket As String)
    'Server sent an ACK
    ReceivedACK UnencryptedPacket
End Sub

'In-game event packets

Public Sub HandleGamePacket(ByVal UnencryptedPacket As String)

On Error GoTo FlyPaper

Dim Team As Integer, Points As Long, n(0 To 1) As Integer, P(0 To 1) As Long
Dim I As Integer, L As Long, O As String, B As Byte

With BotList(CurrentBot)
    'Server wants to tell me about the game events
    Select Case Asc(Left(UnencryptedPacket, 1))
        Case PACKET_TYPES.SendingFile
            Select Case Asc(Mid(UnencryptedPacket, 2, 1))
                Case FTP_Types.Body
                    If FTPList(CurrentBot).FTP_Name = "" Then
                        FTPList(CurrentBot).FTP_Type = Asc(Mid(UnencryptedPacket, 3, 1))
                        O = TrimZero(Mid(UnencryptedPacket, 4, 16))
                        If O = "" Then
                            FTPList(CurrentBot).FTP_Name = "Arena settings"
                        Else
                            FTPList(CurrentBot).FTP_Name = O
                            AddDebug CurrentBot, "Receiving file(0): " + FTPList(CurrentBot).FTP_Name, BLUE
                        End If
                        FTPList(CurrentBot).FTP_Data = Mid(UnencryptedPacket, 20)
                    Else
                        FTPList(CurrentBot).FTP_Data = FTPList(CurrentBot).FTP_Data + Mid(UnencryptedPacket, 3)
                    End If
                Case FTP_Types.Done
                    If FTPList(CurrentBot).FTP_Type <> SimpleSettings Then
                        FTPList(CurrentBot).FTP_Data = FTPList(CurrentBot).FTP_Data + Mid(UnencryptedPacket, 3)
                        PutBytes FTPList(CurrentBot).FTP_Name, FTPList(CurrentBot).FTP_Data
                        AddDebug CurrentBot, "File received(0): " + FTPList(CurrentBot).FTP_Name, BLUE
                    End If
                    FTPList(CurrentBot).FTP_Name = ""
                Case FTP_Types.BodyExtended
                    If FTPList(CurrentBot).FTP_Name = "" Then
                        FTPList(CurrentBot).FTP_Name = TrimZero(Mid(UnencryptedPacket, 8, 16))
                        FTPList(CurrentBot).FTP_Data = Mid(UnencryptedPacket, 24)
                        FTPList(CurrentBot).FTP_FileLen = GetLong(Mid(UnencryptedPacket, 3, 4))
                        FTPList(CurrentBot).FTP_Recved = Len(FTPList(CurrentBot).FTP_Data)
                        AddDebug CurrentBot, "Receiving file(1): " + FTPList(CurrentBot).FTP_Name + " ( " + CStr(FTPList(CurrentBot).FTP_FileLen) + " bytes )", BLUE
                    Else
                        O = Mid(UnencryptedPacket, 7)
                        FTPList(CurrentBot).FTP_Recved = FTPList(CurrentBot).FTP_Recved + Len(O)
                        FTPList(CurrentBot).FTP_Data = FTPList(CurrentBot).FTP_Data + O

                        If FTPList(CurrentBot).FTP_Recved + 17 >= FTPList(CurrentBot).FTP_FileLen Then
                            PutBytes FTPList(CurrentBot).FTP_Name, FTPList(CurrentBot).FTP_Data
                            If .DownloadingSettings = 1 Then GotSettings CurrentBot, FTPList(CurrentBot).FTP_Name
                            AddDebug CurrentBot, "File received(1): " + FTPList(CurrentBot).FTP_Name, BLUE
                            FTPList(CurrentBot).FTP_Name = ""
                        End If
                    End If
            End Select

        Case PACKET_TYPES.AllowSend
            UDPSend CurrentBot, Chr(0) + Chr(10) + GetString(Len(Outgoing)) + Chr(16) + Mid(UnencryptedPacket, 264, 16) + Left(Outgoing, 463), True
            For I = 464 To Len(Outgoing) Step 482
                UDPSend CurrentBot, Chr(0) + Chr(10) + GetString(Len(Outgoing)) + Mid(Outgoing, I, 482), True
            Next
            Outgoing = ""

        Case PACKET_TYPES.MapFilename
            AddDebug CurrentBot, "Map filename: " + TrimZero(Mid(UnencryptedPacket, 2, 16)), BLACK

        Case PACKET_TYPES.YouAreHere
            .MyId = Asc(Mid(UnencryptedPacket, 2, 1))

        Case PACKET_TYPES.StartPeriodic
            .Periodic = 1
            PublicMessage CurrentBot, "*" + GetProfileString("Login", "AppendPassword"), 0
            AddDebug CurrentBot, "SPAWN CONNECTED", GREEN

        Case PACKET_TYPES.PlayersEntering
            For L = 1 To Len(UnencryptedPacket) Step 64
                If Asc(Mid(UnencryptedPacket, L, 1)) = 3 Then
                    AddUser CurrentBot, Asc(Mid(UnencryptedPacket, 51 + L, 1)), TrimZero(Mid(UnencryptedPacket, 3 + L, 20)), TrimZero(Mid(UnencryptedPacket, 23 + L, 19)), GetInteger(Mid(UnencryptedPacket, 53 + L, 2)), Asc(Mid(UnencryptedPacket, 1 + L, 1)), GetInteger(Mid(UnencryptedPacket, 55 + L, 2)), GetInteger(Mid(UnencryptedPacket, 57 + L, 2)), GetLong(Mid(UnencryptedPacket, 43 + L, 4)) + GetLong(Mid(UnencryptedPacket, 47 + L, 4))
                End If
            Next

        Case PACKET_TYPES.PlayerQuit
            I = Asc(Mid(UnencryptedPacket, 2, 1))
            B = FindItem(CurrentBot, .pList(I).SSName)
            If B > 0 Then
                DelItem CurrentBot, B
                If .Bot_Type = ELIM_REF And .Bot_Enabled = 1 And .Bot_Status = STARTED Then
                    UpdateRank .pList(I).SSName, False, .pList(I).Wins, .pList(I).Losses
                End If
            End If
            If .Bot_Enabled = 1 Then
                If .Bot_Type = CONQUER_REF And .Bot_Status = STARTED Then
                    For B = 1 To TotalFreqs(CurrentBot)
                        For L = 1 To Conquer(CurrentBot, B).TotalCurrent
                            If Conquer(CurrentBot, B).CurrentPlayers(L) = I Then
                                KillConquerPlayer CurrentBot, CInt(B), CInt(L)
                                Exit Sub
                            End If
                        Next
                    Next
                ElseIf .Bot_Type = BOUNTYGAME_REF And I = .BountyID Then
                    NewRabbit CurrentBot, True
                End If
            End If
            DelUser CurrentBot, I
            If I = .AttachedTo Then .AttachedTo = -1

        Case PACKET_TYPES.ArenaList
            O = Mid(UnencryptedPacket, 2)
            AddDebug CurrentBot, "-=Arena list=-", BLACK
            Do While O <> ""
                I = I + 1
                AddDebug CurrentBot, CStr(I) + " -> '" + TrimZero(O) + "' (" + CStr(Asc(Mid(O, Len(TrimZero(O)) + 2, 1))) + " players)", BLACK
                O = Mid(O, Len(TrimZero(O)) + 4)
            Loop

        Case PACKET_TYPES.KoTHReset
            UDPSend CurrentBot, Chr(30), True

        Case PACKET_TYPES.KillMessage
            n(0) = Asc(Mid(UnencryptedPacket, 5, 1))
            n(1) = Asc(Mid(UnencryptedPacket, 3, 1))
            I = GetInteger(Mid(UnencryptedPacket, 7, 2))
            .pList(n(0)).Losses = .pList(n(0)).Losses + 1
            .pList(n(1)).Wins = .pList(n(1)).Wins + 1
            .pList(n(1)).Points = .pList(n(1)).Points + I
            HandleScoreChanges CurrentBot, n(0), n(1)
            HandleScoreChanges CurrentBot, n(1), -1
            AddLoss CurrentBot, .pList(n(0)).Frequency
            AddWin CurrentBot, .pList(n(1)).Frequency
            AddDebug CurrentBot, .pList(n(0)).SSName + "(" + CStr(I) + ") killed by: " + .pList(n(1)).SSName, GREEN

        Case PACKET_TYPES.Chat_Message
            O = TrimZero(Mid(UnencryptedPacket, 6))
            I = Asc(Mid(UnencryptedPacket, 4, 1))
            If I <> -1 And I <> .MyId Then
                Select Case Asc(Mid(UnencryptedPacket, 2, 1))
                    Case CHAT_TYPES.Private_Message
                        HandleCommand CurrentBot, I, O
                        AddDebug CurrentBot, "(" + .pList(I).SSName + ")> " + O, GREEN
                    Case CHAT_TYPES.TeamPrivate_Message
                        HandleCommand CurrentBot, I, O
                        AddDebug CurrentBot, "(" + .pList(I).SSName + ")> " + O, YELLOW
                    Case CHAT_TYPES.Team_Message
                        HandleCommand CurrentBot, I, O
                        AddDebug CurrentBot, .pList(I).SSName + "> " + O, YELLOW
                    Case CHAT_TYPES.Public_Message
                        If .Bot_Status = VOTE_DEATHS Then
                            If IsNumeric(O) And Len(O) <= 2 Then
                                L = Val(O)
                                If L >= 1 And L <= 10 Then
                                    .pList(I).Vote = CByte(L)
                                End If
                            End If
                        ElseIf .Bot_Status = VOTE_GAME Then
                            If IsNumeric(O) And Len(O) = 1 Then
                                L = Val(O)
                                If L >= 1 And L <= 8 Then
                                    .pList(I).Vote = CByte(L)
                                End If
                            End If
                        End If
                        AddDebug CurrentBot, .pList(I).SSName + "> " + O, BLUE
                End Select
            End If
            If Asc(Mid(UnencryptedPacket, 2, 1)) = CHAT_TYPES.ServerError_Message Then
                If BotList(CurrentBot).ErrorChannel <> "" Then
                    ChannelMessage CurrentBot, "1;" + O, 0
                End If
                AddDebug CurrentBot, O, RED
            ElseIf Asc(Mid(UnencryptedPacket, 2, 1)) = CHAT_TYPES.Arena_Message Then
                If .Bot_Type = BOUNTYGAME_REF And .Bot_Enabled = 1 Then
                    Select Case O
                        Case "NOTICE: 5 minutes remaining.": PublicMessage CurrentBot, "*arena " + .pList(.BountyID).SSName + "(bounty rabbit) last seen at " + GetPlayerCoords(CurrentBot, .BountyID) + ". 5 minutes until he wins", 1
                        Case "NOTICE: 1 minute remaining.":  PublicMessage CurrentBot, "*arena " + .pList(.BountyID).SSName + "(bounty rabbit) last seen at " + GetPlayerCoords(CurrentBot, .BountyID) + ". 1 minute until he wins", 1
                        Case "NOTICE: Game over":            BountyGameOver CurrentBot
                    End Select
                ElseIf .Bot_Type = CONQUER_REF Or .Bot_Type = ELIM_REF Then
                    If .Bot_Status = STARTED Then
                        If O = "Arena UNLOCKED" Then PublicMessage CurrentBot, "*lock", 0
                    ElseIf O = "Arena LOCKED" Then
                        PublicMessage CurrentBot, "*lock", 0
                    End If
                End If
                Collect O
                AddDebug CurrentBot, O, GREEN
            ElseIf Asc(Mid(UnencryptedPacket, 2, 1)) = CHAT_TYPES.RemotePrivate_Message Then
                AddDebug CurrentBot, O, GREEN
            ElseIf Asc(Mid(UnencryptedPacket, 2, 1)) = CHAT_TYPES.Channel_Message Then
                AddDebug CurrentBot, O, RED
            End If

        Case PACKET_TYPES.PasswordAccept
            O = "Password not accepted: "
            Select Case Asc(Mid(UnencryptedPacket, 2, 1))
                Case FAILURES.None
                    AddDebug CurrentBot, "Password packet accepted", YELLOW
                    EnterArena CurrentBot, 8, .ArenaName, 1600, 1280
                Case FAILURES.NO_SCORES
                    AddDebug CurrentBot, "Password packet accepted. The billing server is down", YELLOW
                    EnterArena CurrentBot, 8, .ArenaName, 1600, 1280
                Case FAILURES.NEW_USER
                    AddDebug CurrentBot, "Unknown user. Creating a new account", YELLOW
                    SendLogin .MyName, GetProfileString("Passwords", .MyName), BanID, ZoneBias, 5454, 1
                    SendSync CurrentBot
                Case FAILURES.BAD_PW
                    AddDebug CurrentBot, O + "Incorrect password", RED
                    UnloadBot CurrentBot, False
                Case FAILURES.IP_BLOCK
                    AddDebug CurrentBot, O + "SSBot is locked out of this zone (IP) or the zone is full", RED
                    UnloadBot CurrentBot, False
                Case FAILURES.ID_BLOCK
                    AddDebug CurrentBot, O + "SSBot is locked out of this zone (Machine ID)", RED
                    UnloadBot CurrentBot, False
                Case FAILURES.PERMISSION_ZONE
                    AddDebug CurrentBot, "This zone is permission-only. Going to try to log in regardless..", RED
                    SendLogin .MyName, GetProfileString("Passwords", .MyName), BanID, ZoneBias, 5454, 1
                    SendSync CurrentBot
                Case FAILURES.NAME_MISSING
                    AddDebug CurrentBot, O + "Name is missing", RED
                    UnloadBot CurrentBot, False
                Case FAILURES.ADVANCED_ONLY
                    AddDebug CurrentBot, O + "Not enough logged hours. (Advanced users only)", RED
                    UnloadBot CurrentBot, False
                Case FAILURES.OBSCENE_NAME
                    AddDebug CurrentBot, O + "Username is in obscene.txt", RED
                    UnloadBot CurrentBot, False
                Case Else
                    AddDebug CurrentBot, O + "Reason #" + CStr(Asc(Mid(UnencryptedPacket, 2, 1))), RED
                    UnloadBot CurrentBot, False
            End Select

        Case PACKET_TYPES.ScoreUpdate
            I = Asc(Mid(UnencryptedPacket, 2, 1))
            .pList(I).Points = GetLong(Mid(UnencryptedPacket, 4, 4)) + GetLong(Mid(UnencryptedPacket, 8, 4))
            .pList(I).Wins = GetInteger(Mid(UnencryptedPacket, 12, 2))
            .pList(I).Losses = GetInteger(Mid(UnencryptedPacket, 14, 2))
            HandleScoreChanges CurrentBot, I, -1

        Case PACKET_TYPES.FlagReward
            Team = GetInteger(Mid(UnencryptedPacket, 12, 2)): Points = CLng(GetInteger(Mid(UnencryptedPacket, 8, 2)))

            For I = 0 To MAX_PLAYERS
                If .pList(I).SSName <> "" And .pList(I).Frequency = Team Then .pList(I).Points = .pList(I).Points + Points
            Next

        Case PACKET_TYPES.SoccerGoal
            Team = GetInteger(Mid(UnencryptedPacket, 2, 2)): Points = GetLong(Mid(UnencryptedPacket, 4, 4))

            For I = 0 To MAX_PLAYERS
                If .pList(I).SSName <> "" And .pList(I).Frequency = Team Then .pList(I).Points = .pList(I).Points + Points
            Next

        Case PACKET_TYPES.FlagVictory
            Team = GetInteger(Mid(UnencryptedPacket, 2, 2)): Points = GetLong(Mid(UnencryptedPacket, 4, 4))

            For I = 0 To MAX_PLAYERS
                If .pList(I).SSName <> "" And .pList(I).Frequency = Team Then .pList(I).Points = .pList(I).Points + Points
            Next

            If .Bot_Type = SWZ_REF And .Bot_Enabled = 1 Then
                TotalItems(CurrentBot) = 0
                For I = 0 To MAX_PLAYERS
                    Select Case .pList(I).ShipType
                        Case 6: PrivateMessage CurrentBot, I, "*setship 5", 0
                        Case 7: PrivateMessage CurrentBot, I, "*setship 1", 0
                    End Select
                    If .pList(I).SSName <> "" And .pList(I).ShipType < 8 Then
                        Select Case .pList(I).Frequency
                            Case 0: If .pList(I).Points > P(0) Then n(0) = I: P(0) = .pList(I).Points
                            Case 1: If .pList(I).Points > P(1) Then n(1) = I: P(1) = .pList(I).Points
                        End Select
                        L = L + 1
                    End If
                Next
                Select Case L
                    Case Is >= 72
                        PublicMessage CurrentBot, "*arena " + .pList(n(0)).SSName + "(Imperials) and " + .pList(n(1)).SSName + "(Rebels) are high scorers", 0
                        PublicMessage CurrentBot, "*arena Please announce two capital ships for your team  -" + .MyName, 2
                    Case Is >= 36
                        PublicMessage CurrentBot, "*arena " + .pList(n(0)).SSName + "(Imperials) and " + .pList(n(1)).SSName + "(Rebels) are high scorers", 0
                        PublicMessage CurrentBot, "*arena Please announce your team's legal capital ship  -" + .MyName, 2
                    Case Else
                        PublicMessage CurrentBot, "*arena " + .pList(n(0)).SSName + "(Imperials) and " + .pList(n(1)).SSName + "(Rebels) are high scorers", 0
                        PublicMessage CurrentBot, "*arena No capital ships this game. Have " + CStr(L) + ":36 players required  -" + .MyName, 1
                End Select
            End If

            AddFlagVictory CurrentBot, Team

        Case PACKET_TYPES.WeaponsFire
            I = Asc(Mid(UnencryptedPacket, 9, 1))
            If I <= MAX_PLAYERS Then
                .pList(I).X = GetInteger(Mid(UnencryptedPacket, 5, 2))
                .pList(I).Y = GetInteger(Mid(UnencryptedPacket, 16, 2))
                .pList(I).D = Asc(Mid(UnencryptedPacket, 2, 1))
                '.AttachedWeapon = GetInteger(Right(UnencryptedPacket, 2))
                'If .AttachedWeapon = &H20 Then .AttachedWeapon = 0 (Alt+Tab
            End If

        Case PACKET_TYPES.PositionData
            I = Asc(Mid(UnencryptedPacket, 9, 1))
            If I <= MAX_PLAYERS Then
                .pList(I).X = GetInteger(Mid(UnencryptedPacket, 5, 2))
                .pList(I).Y = GetInteger(Mid(UnencryptedPacket, 13, 2))
                .pList(I).D = Asc(Mid(UnencryptedPacket, 2, 1))
            End If

        Case PACKET_TYPES.SetFreqAndShip
            I = Asc(Mid(UnencryptedPacket, 3, 1))
            .pList(I).ShipType = Asc(Mid(UnencryptedPacket, 2, 1))
            .pList(I).Frequency = GetInteger(Mid(UnencryptedPacket, 5, 2))
            HandleShipChanges CurrentBot, I

        Case PACKET_TYPES.SetFreq
            I = Asc(Mid(UnencryptedPacket, 2, 1))
            .pList(I).Frequency = GetInteger(Mid(UnencryptedPacket, 4, 2))
            HandleShipChanges CurrentBot, I

        Case PACKET_TYPES.ScoreReset
            n(0) = GetInteger(Mid(UnencryptedPacket, 2, 2))
            If n(0) = -1 Then
                For n(1) = 0 To MAX_PLAYERS
                    If BotList(CurrentBot).pList(n(1)).SSName <> "" Then
                        BotList(CurrentBot).pList(n(1)).Points = 0
                        BotList(CurrentBot).pList(n(1)).Wins = 0
                        BotList(CurrentBot).pList(n(1)).Losses = 0
                    End If
                Next
            Else
                n(0) = Asc(Mid(UnencryptedPacket, 2, 1))
                BotList(CurrentBot).pList(n(0)).Points = 0
                BotList(CurrentBot).pList(n(0)).Wins = 0
                BotList(CurrentBot).pList(n(0)).Losses = 0
            End If

    End Select
End With

Exit Sub
FlyPaper:
    AddDebug CurrentBot, "Error detected in HandleGamePacket(" + CStr(Asc(Left(UnencryptedPacket, 1))) + "): " + Err.Description, RED
End Sub

'Handler entry point

Public Sub HandlePacket(UnencryptedPacket As String)
    If Asc(Left(UnencryptedPacket, 1)) = 0 Then
        Select Case Asc(Mid(UnencryptedPacket, 2, 1))
            Case O2_TYPES.KeyNotify:     Call HandlerO2_02(UnencryptedPacket)
            Case O2_TYPES.ACKRequired:   Call HandlerO2_03(UnencryptedPacket)
            Case O2_TYPES.ACKMessage:    Call HandlerO2_04(UnencryptedPacket)
            Case O2_TYPES.SyncServer:    Call HandlerO2_06(UnencryptedPacket)
            Case O2_TYPES.Disconnect:    Call HandlerO2_07(UnencryptedPacket)
            Case O2_TYPES.PacketCluster: Call HandlerO2_0E(UnencryptedPacket)
        End Select
    Else
        Call HandleGamePacket(UnencryptedPacket)
    End If
End Sub

'CONNECT/DISCONNECT/CLEANUP

Public Sub SendLogin(User As String, Password As String, MachineID As Long, TimeZoneBias As Integer, PermissionID As Integer, NewUser As Byte)
    UDPSend CurrentBot, Chr(9) + Chr(NewUser) + FillZero(User, 32) + FillZero(Password, 32) + GetString(MachineID) + Chr(3) + GetString2(TimeZoneBias) + GetString2(PermissionID) + GetString2(&H86) + GetString(&H1BC) + GetString(&H22B) + GetString(MachineID) + String(12, Chr(0)), True
    AddDebug CurrentBot, "Sending password packet", YELLOW
End Sub

Public Sub SendSync(BotID As Integer)
    UDPSend BotID, Chr(0) + Chr(5) + GetString(GetTickCount \ 10) + GetString(BotList(BotID).TotalSent + 1) + GetString(BotList(BotID).TotalReceived), False
End Sub

Public Sub LeaveServer(BotID As Integer)
    LeaveArena BotID
    UDPSend BotID, Chr(0) + Chr(7), False

    AddDebug BotID, "Leaving", RED
End Sub

Public Function Checksum(mStr As String) As Byte
    Dim I As Integer, B As Byte

    For I = 1 To Len(mStr)
        B = B Xor Asc(Mid(mStr, I, 1))
    Next

    Checksum = B
End Function

Public Sub SendPosition(BotID As Integer, X As Integer, mX As Integer, Y As Integer, mY As Integer, D As Byte, Bounty As Integer, Weapon As WEAPON_TYPES, CurrentEnergy As Integer, Blink As Byte)
    Dim S As String

    S = Chr(3) + Chr(D) + GetString(GetTickCount \ 10) + GetString2(mY) + GetString2(Y) + Chr(0) + Chr(Blink) + GetString2(X) + GetString2(mX)

    InsertByte S, 11, Checksum(S)

    UDPSend BotID, S + GetString2(Bounty) + GetString2(CurrentEnergy) + GetString2(CInt(Weapon)), False
End Sub

'Mainfrm.ARENA

Public Sub EnterArena(BotID As Integer, ShipType As Byte, ArenaName As String, X_Resolution As Integer, Y_Resolution As Integer)
    BotList(BotID).Bot_Enabled = 0

    If IsPublicArena(ArenaName) Then
        UDPSend BotID, Chr(1) + Chr(ShipType) + Chr(1) + Chr(0) + GetString2(X_Resolution) + GetString2(Y_Resolution) + GetString2(&HFFFF) + String(16, Chr(0)), True
        BotList(BotID).ArenaName = ""
        AddDebug BotID, "Entering public arena", YELLOW
    Else
        UDPSend BotID, Chr(1) + Chr(ShipType) + Chr(1) + Chr(0) + GetString2(X_Resolution) + GetString2(Y_Resolution) + GetString2(&HFFFD) + FillZero(ArenaName, 16), True
        BotList(BotID).ArenaName = ArenaName
        AddDebug BotID, "Entering arena '" + ArenaName + "'", YELLOW
    End If
End Sub

Public Sub LeaveArena(BotID As Integer)
    UDPSend BotID, Chr(2), True
    DeleteAllUsers BotID
End Sub

'Mainfrm.CHAT

Public Sub PrivateMessage(BotID As Integer, PIndex As Integer, Message As String, SoundCode As Byte)
    If PIndex = BotList(BotID).MyId Then Exit Sub

    If Left(Message, 1) = "*" Then
        UDPSend BotID, Chr(6) + Chr(5) + Chr(SoundCode) + GetString2(PIndex) + Message + Chr(0), True
    Else
        AddQueue BotID, Chr(6) + Chr(7) + Chr(SoundCode) + GetString2(0) + ":" + BotList(BotID).pList(PIndex).SSName + ": " + Message + Chr(0)
    End If
End Sub

Public Sub PublicMessage(BotID As Integer, Message As String, SoundCode As Byte)
    If Left(Message, 1) = "*" Or Left(Message, 1) = "?" Then
        UDPSend BotID, Chr(6) + Chr(2) + Chr(SoundCode) + GetString2(0) + Message + Chr(0), True
    Else
        AddQueue BotID, Chr(6) + Chr(2) + Chr(SoundCode) + GetString2(0) + Message + Chr(0)
    End If
End Sub

Public Sub TeamMessage(BotID As Integer, Message As String, SoundCode As Byte)
    AddQueue BotID, Chr(6) + Chr(3) + Chr(SoundCode) + GetString2(0) + Message + Chr(0)
End Sub

'PlayerAndMessage format = ":" + Player + ":" + Message
Public Sub RemoteMessage(BotID As Integer, PlayerAndMessage As String, SoundCode As Byte)
    AddQueue BotID, Chr(6) + Chr(7) + Chr(SoundCode) + GetString2(0) + PlayerAndMessage + Chr(0)
End Sub

Public Sub ChannelMessage(BotID As Integer, Message As String, SoundCode As Byte)
    AddQueue BotID, Chr(6) + Chr(9) + Chr(SoundCode) + GetString2(0) + Message + Chr(0)
End Sub

'Other commands

Public Sub GetBall(BotID As Integer, BallNumber As Byte)
    UDPSend BotID, Chr(32) + Chr(BallNumber) + GetString(GetTickCount \ 10), True
End Sub

Public Sub FireBall(BotID As Integer, BallNumber As Byte, X As Integer, Y As Integer)
    UDPSend BotID, Chr(31) + Chr(BallNumber) + GetString2(X) + GetString2(Y) + String(6, 0) + GetString(GetTickCount \ 10), True
End Sub

Public Sub GetFlag(BotID As Integer, FlagNumber As Byte)
    UDPSend BotID, Chr(34) + Chr(FlagNumber) + GetString(GetTickCount \ 10), True
End Sub
