Files
Legacy-Projects/BlitzMax/Sirius Online Server/BlitzNet.bmx
T
2014-11-24 18:18:24 +01:00

1140 lines
33 KiB
Plaintext

SuperStrict
Import BRL.Socket
Import BRL.LinkedList
Import BRL.Stream
Import BRL.Bank
Import Xaymar.IOQueue
'----------------------------------------------------------------
'-- Packet Descriptors
'----------------------------------------------------------------
'-- Any Packet
'Off Size Desc
' 0 1 Packet Id
'-- Login
'Off Size Desc
' 1 2 Unique Id (Server) / UDP Port (Client)
' 3 2 Version
' 5 16 Name (Always 16B, Only allows bytes above 32)
' 21 4F Initial Position X
' 25 4F Initial Position Y
' 29 4F Initial Position Z
' 33 4F Initial Rotation X
' 37 4F Initial Rotation Y
' 41 4F Initial Rotation Z
'-- Logout
'Off Size Desc
' 1 2 Unique Id
'-- Kick
'Off Size Desc
' 1 2 Reason Length
' 3 ^ Reason
'-- Data
' 1 2 Unique Id
' 3 1 Data Flags (See BNET_DATAFLAG_*)
'... 4F Position X
'... 4F Position Y
'... 4F Position Z
'... 2 Rotation X
'... 2 Rotation Y
'... 2 Rotation Z
'... 2 Velocity X
'... 2 Velocity Y
'... 2 Velocity Z
'... 2 Aim X
'... 2 Aim Y
'-- Action (Yet Unsupported)
'----------------------------------------------------------------
'-- Constants
'----------------------------------------------------------------
' Library Version
Const BNET_VERSION_MAJOR:Byte = 0
Const BNET_VERSION_MINOR:Byte = 1
' Value Thresholds
Const BNET_THRESHOLD_POSITION:Float = 1.0
Const BNET_THRESHOLD_ROTATION:Float = 8.0
Const BNET_THRESHOLD_VELOCITY:Float = 5.0
Const BNET_THRESHOLD_AIM:Float = 1.0
' Data Flags, used in BNetPacketData
Const BNET_DATAFLAG_POSITIONX:Byte = $00000001
Const BNET_DATAFLAG_POSITIONY:Byte = $00000010
Const BNET_DATAFLAG_POSITIONZ:Byte = $00000100
Const BNET_DATAFLAG_ROTATIONX:Byte = $00001000
Const BNET_DATAFLAG_ROTATIONY:Byte = $00010000
Const BNET_DATAFLAG_ROTATIONZ:Byte = $00100000
Const BNET_DATAFLAG_VELOCITY:Byte = $01000000
Const BNET_DATAFLAG_AIM:Byte = $10000000
' Translateable Strings
Const BNET_KICK_SHUTDOWN:String = "~r~rKICK_SHUTDOWN"
Const BNET_KICK_SERVERFULL:String = "~r~rKICK_SERVERFULL"
'----------------------------------------------------------------
'-- Types
'----------------------------------------------------------------
Type BNetServer
' Size of Buffer for Network Packets, 64KB should be enough for now.
Const BUFFER_SIZE:Int = (64 * 1024)
' How many updates should we send out each minute?
Const DATA_COUNT:Int = 150
' How many keyframes should we send out each minute?
Const DATA_COUNT_KEYFRAME:Int = 3
'----------------------------------------------------------------
'-- Globals
'----------------------------------------------------------------
Global Data_Time:Int = 60000 / BNetServer.DATA_COUNT
Global Data_Time_KeyFrame:Int = 60000 / BNetServer.DATA_COUNT_KEYFRAME
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
' Sockets
Field m_TCPSocket:TSocket = Null
Field m_UDPSocket:TSocket = Null
' Queue
Field m_TCPQueue:TIOQueue = Null
Field m_UDPQueue:TIOQueue = Null
' Packet Buffer
Field m_Buffer:TBank = Null
' Players
Field m_Players:TList = Null'New TList
Field m_UniqueIdToPlayer:BNetPlayer[] = Null'New BNetPlayer[65536]
Field m_UDPPortToPlayers:TList[] = Null'New TList[65536]
' Temporary Variables
Field mt_LastUpdateTime:Long = 0
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetServer()
Return (New BNetServer)
End Function
' Constructor
Method New()
' Create packet queues.
m_TCPQueue = New TIOQueue
m_UDPQueue = New TIOQueue
' Create Buffer
m_Buffer = CreateBank(BUFFER_SIZE)
' Create Player list.
m_Players = New TList
' Create UID to Player mapping.
m_UniqueIdToPlayer = New BNetPlayer[65536]
' Create UDP Port to Players list.
m_UDPPortToPlayers = New TList[65536]
EndMethod
' Destructor
Method Destroy()
' Close sockets and delete remaining Data.
Close()
' Destroy packet queues.
If m_UDPQueue Then
m_UDPQueue.Destroy()
m_UDPQueue = Null
EndIf
If m_TCPQueue Then
m_TCPQueue.Destroy()
m_TCPQueue = Null
EndIf
' Destroy buffer
m_Buffer.Delete()
m_Buffer = Null
' Destroy UDP-Port to players list.
If m_UDPPortToPlayers Then
For Local l_UDPPort:Short = 0 To 65535
If m_UDPPortToPlayers[l_UDPPort] Then
m_UDPPortToPlayers[l_UDPPort].Clear()
m_UDPPortToPlayers[l_UDPPort] = Null
EndIf
Next
m_UDPPortToPlayers = Null
EndIf
' Destroy UID to Player mapping.
If m_UniqueIdToPlayer Then
m_UniqueIdToPlayer = Null
EndIf
' Destroy Player list.
If m_Players Then
m_Players.Clear()
m_Players = Null
EndIf
EndMethod
' Open the Server so that players can connect.
' @return: <Bool> True for success, otherwise False.
Method Open:Int(p_TCPPort:Short, p_UDPPort:Short, p_Backlog:Int = 8)
' Create missing Sockets.
If Not m_TCPSocket Then m_TCPSocket = TSocket.CreateTCP()
If Not m_UDPSocket Then m_UDPSocket = TSocket.CreateUDP()
' Try binding the TCP Socket to the given port.
If m_TCPSocket.Bind(p_TCPPort) Then
m_TCPSocket.SetTCPNoDelay(True)
m_TCPSocket.Listen(p_Backlog)
' Try binding the UDP Socket to the given port.
If m_UDPSocket.Bind(p_UDPPort) Then
Return True
EndIf
EndIf
' Otherwise just undo everything.
Close()
Return False
EndMethod
' Close the Server and disconnect all players.
Method Close()
' Kick all remaining players and destroy their data.
For Local l_Player:BNetPlayer = EachIn m_Players
l_Player.Kick("Shutting Down")
l_Player.Destroy()
l_Player = Null
Next
m_Players.Clear()
' Clear Packet queues.
m_TCPQueue.Clear()
m_UDPQueue.Clear()
' Close and destroy sockets.
If m_UDPSocket Then
m_UDPSocket.Close()
m_UDPSocket = Null
EndIf
If m_TCPSocket Then
m_TCPSocket.Close()
m_TCPSocket = Null
EndIf
EndMethod
' Update everything.
Method Update()
If m_TCPSocket And m_UDPSocket Then
' Try to accept new players.
Local l_NewPlayer:TSocket = m_TCPSocket.Accept(0)
While l_NewPlayer <> Null
Local l_UniqueId:Short
If GetUnusedUniqueId(l_UniqueId) = True Then
Local l_Player:BNetPlayer = BNetPlayer.Create(l_NewPlayer, l_UniqueId)
m_UniqueIdToPlayer[lt_UniqueId] = l_Player
m_Players.AddLast(l_Player)
Else
l_NewPlayer.Close()
EndIf
' Get next player.
l_NewPlayer = m_TCPSocket.Accept(0)
Wend
' Retrieve UDP Packets and assign them to Players. (UDP is connectionless)
Local l_Packet:BNetPacket = Null
While Local l_RecvSize:Int = m_UDPSocket.Recv(m_Buffer._buf, m_Buffer._size)
' Update all players.
For Local l_Player:BNetPlayer = EachIn m_Players
l_Player.Update()
Next
EndIf
EndMethod
Rem
bbdoc: Tries to find an unused UniqueId and writes it into p_UniqueId.
returns: <Byte> Successful
EndRem
Method GetUnusedUniqueId:Byte(p_UniqueId:Short Var)
For Local l_UniqueId:Short = 0 To 65535
If m_UniqueIdToPlayer[l_UniqueId] = Null Then
p_UniqueId = l_UniqueId
Return True
EndIf
Next
Return False
EndMethod
EndType
Type BNetPlayer
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
' Sockets
Field m_TCPSocket:TSocket = Null
Field m_UDPSocket:TSocket = Null
' Queue
Field m_TCPQueue:TIOQueue = Null
Field m_UDPQueue:TIOQueue = Null
' Player Information
Field m_UniqueId:Short = 0
Field m_Name:String = Null
Field m_Version:Short = 0
Field m_Position:Vector3F = Null
Field m_Rotation:Vector3F = Null
Field m_Velocity:Vector3F = Null
Field m_Aim:Vector2F = Null
' Historical Player Information (Used to calculate Data Flags)
Field m_OldPosition:Vector3F = Null
Field m_OldRotation:Vector3F = Null
Field m_OldVelocity:Vector3F = Null
Field m_OldAim:Vector2F = Null
' Boolean Information
Field mt_IsConnected:Byte = False
Field mt_IsLoggedIn:Byte = False
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetPlayer()
Return (New BNetPlayer)
End Function
' Constructor
Method New()
' Create packet queues.
m_TCPQueue = TIOQueue.Create()
m_UDPQueue = TIOQueue.Create()
' Create player information.
m_Position = New Vector3F
m_Rotation = New Vector3F
m_Velocity = New Vector3F
m_Aim = New Vector3F
' Create historical player information.
m_OldPosition = New Vector3F
m_OldRotation = New Vector3F
m_OldVelocity = New Vector3F
m_OldAim = New Vector2F
EndMethod
' Destructor
Method Destroy()
' Destroy packet queues.
If m_UDPQueue Then
m_UDPQueue.Clear()
m_UDPQueue = Null
EndIf
If m_TCPQueue Then
For Local p_Packet:BNetPacket = EachIn m_TCPQueue.GetInQueue()
Next
m_TCPQueue.Clear()
m_TCPQueue = Null
EndIf
' Destroy player information.
m_Aim = Null
m_Velocity = Null
m_Rotation = Null
m_Position = Null
' Destroy historical player information.
m_OldAim = Null
m_OldVelocity = Null
m_OldRotation = Null
m_OldPosition = Null
' Disconnect the player.
Disconnect()
EndMethod
Method Connect(p_TCPSocket:TSocket, p_UniqueId:Short)
m_TCPSocket = p_TCPSocket
m_UniqueId = p_UniqueId
m_IsConnected = True
EndMethod
Method Disconnect()
' Close and destroy sockets.
If m_UDPSocket Then
m_UDPSocket.Close()
m_UDPSocket = Null
EndIf
If m_TCPSocket Then
m_TCPSocket.Close()
m_TCPSocket = Null
EndIf
m_IsConnected = False
EndMethod
Method Login(p_LoginPacket:BNetPacketLogin)
If m_TCPSocket Then
' Create UDP Socket so that we can send data, and not only retrieve.
m_UDPSocket = TSocket.CreateUDP()
m_UDPSocket.Connect(m_TCPSocket.RemoteIp(), p_LoginPacket.m_UDPPort)
m_IsLoggedIn = True
EndIf
EndMethod
Method Logout()
If m_TCPSocket Then
' Create Logout Packet and add it to the TCPQueue
Local l_Packet:BNetPacketLogout = BNetPacketLogout.Create()
l_Packet.setUniqueId(m_UniqueId)
m_TCPQueue.PushOut(l_Packet)
m_UDPSocket.Close()
m_UDPSocket = Null
m_IsLoggedIn = False
EndIf
EndMethod
Method Kick(p_Reason:String)
If m_TCPSocket Then
Local l_Packet:BNetPacketKick = BNetPacketKick.Create()
l_Packet.setReason(p_Reason)
m_TCPQueue.PushOut(l_Packet)
EndIf
EndMethod
Method Update()
EndMethod
'----------------------------------------------------------------
'-- Getters / Setters
'----------------------------------------------------------------
Method getPosition:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Position:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Position), Byte Ptr(m_Position), SizeOf(m_Position))
Return l_Position
EndMethod
Method setPosition(p_Position:Vector3F)
MemCopy(Byte Ptr(m_OldPosition), Byte Ptr(m_Position), SizeOf(m_Position))
MemCopy(Byte Ptr(m_Position), Byte Ptr(p_Position), SizeOf(m_Position))
EndMethod
Method getRotation:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Rotation:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Rotation), Byte Ptr(m_Rotation), SizeOf(m_Rotation))
Return l_Rotation
EndMethod
Method setRotation(p_Rotation:Vector3F)
MemCopy(Byte Ptr(m_OldRotation), Byte Ptr(m_Rotation), SizeOf(m_Rotation))
MemCopy(Byte Ptr(m_Rotation), Byte Ptr(p_Rotation), SizeOf(m_Rotation))
EndMethod
Method getVelocity:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Velocity:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Velocity), Byte Ptr(m_Velocity), SizeOf(m_Velocity))
Return l_Velocity
EndMethod
Method setVelocity(p_Velocity:Vector3F)
MemCopy(Byte Ptr(m_OldVelocity), Byte Ptr(m_Velocity), SizeOf(m_Velocity))
MemCopy(Byte Ptr(m_Velocity), Byte Ptr(p_Velocity), SizeOf(m_Velocity))
EndMethod
Method getAim:Vector2F()
' Return a clone of this Vector without bypassing GC.
Local l_Aim:Vector3F = New Vector2F
MemCopy(Byte Ptr(l_Aim), Byte Ptr(m_Aim), SizeOf(m_Aim))
Return l_Aim
EndMethod
Method setAim(p_Velocity:Vector2F)
MemCopy(Byte Ptr(m_OldAim), Byte Ptr(m_Aim), SizeOf(m_Aim))
MemCopy(Byte Ptr(m_Aim), Byte Ptr(p_Aim), SizeOf(m_Aim))
EndMethod
End Type
'----------------------------------------------------------------
'-- Packet Types
'----------------------------------------------------------------
Type BNetPacket
'----------------------------------------------------------------
'-- Constants
'----------------------------------------------------------------
' Packet Ids, for identification of each one.
Const ID_LOGIN:Byte = 0
Const ID_LOGOUT:Byte = 1
Const ID_KICK:Byte = 2
Const ID_DATA:Byte = 3
Const ID_ACTION:Byte = 4
Const ID_INVALID:Byte = 255
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
Field m_Id:Byte
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function CreateFromId:BNetPacket(l_PacketId:Byte)
' Need to know the type of the packet to create
Select l_PacketId
Case ID_LOGIN
Return BNetPacketLogin.Create(p_Buffer)
Case ID_LOGOUT
Return BNetPacketLogout.Create(p_Buffer)
Case ID_KICK
Return BNetPacketKick.Create(p_Buffer)
Case ID_DATA
Return BNetPacketData.Create(p_Buffer)
Case ID_ACTION
Return BNetPacketAction.Create(p_Buffer)
End Select
' If we don't have such a type, just do nothing.
Return Null
End Function
Function CreateFromBuffer:BNetPacket(p_Buffer:TBank)
Local l_PacketId:Byte = p_Buffer.PeekByte(0)
Return Create(l_PacketId)
End Function
' Constructor
Method New()
' This Packet is Invalid.
m_Id = ID_INVALID
EndMethod
' Destructor
Method Destroy()
EndMethod
' Size of Packet
Method GetSize:Int()
Return 1
EndMethod
' Read the packet data from the buffer.
Method Read:Int(p_Buffer:TBank)
Return GetSize()
EndMethod
' Write the Packet data to the Buffer.
Method Write:Int(p_Buffer:TBank)
' Write Packet Id
p_Buffer.PokeByte(0, m_Id)
Return GetSize()
EndMethod
End Type
Type BNetPacketLogin Extends BNetPacket
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
' UniqueId is sent by Server, UDPPort is sent by Client
Field m_UnqiueId:Short, m_UDPPort:Short Ptr
' Version the Client sent us.
Field m_Version:Short
' Name of the Client, truncated.
Field m_Name:String
' Initial Position and Rotation.
Field m_Position:Vector3F
Field m_Rotation:Vector3F
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetPacket(p_Buffer:TBank)
Local l_Packet:BNetPacketLogin = (New BNetPacketLogin)
l_Packet.Read(p_Buffer)
Return l_Packet
End Function
' Constructor
Method New()
' This is a Login packet.
m_Id = BNetPacket.ID_LOGIN
' UDPPort and UniqueId share the same Space in the Packet.
m_UDPPort = VarPtr(m_UniqueId)
' Create vector objects for position and rotation.
m_Position = New Vector3F
m_Rotation = New Vector3F
EndMethod
' Destructor
Method Destroy()
' Destroy vector objects.
m_Rotation = Null
m_Position = Null
' Remove memory link for UDPPort so GC doesn't get confused.
m_UDPPort = Null
EndMethod
' Size of Packet
Method GetSize:Int()
Return Super.GetSize() + 44
EndMethod
' Read the packet data from the buffer.
Method Read:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Read(p_Buffer)
' Read UDPPort for punchthrough.
m_UDPPort = p_Buffer.PeekShort(l_Offset)
m_Version = p_Buffer.PeekShort(l_Offset + 2)
' Read Player Name
m_Name = ""
For Local l_NamePos:Int = 0 Until 16
Local l_Char:Byte = p_Buffer.PeekByte(l_Offset + 4 + l_NamePos)
If l_Char < 32 Then Exit
m_Name :+ Chr(l_Char)
Next
' Read initial position and rotation
m_Position.X = p_Buffer.PeekFloat(l_Offset + 20)
m_Position.Y = p_Buffer.PeekFloat(l_Offset + 24)
m_Position.Z = p_Buffer.PeekFloat(l_Offset + 28)
m_Rotation.X = p_Buffer.PeekFloat(l_Offset + 32)
m_Rotation.Y = p_Buffer.PeekFloat(l_Offset + 36)
m_Rotation.Z = p_Buffer.PeekFloat(l_Offset + 40)
Return GetSize()
EndMethod
' Write the packet data to the buffer.
Method Write:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Write(p_Buffer)
' Write chosen UniqueId
p_Buffer.PokeShort(l_Offset, m_UniqueId)
p_Buffer.PokeShort(l_Offset + 2, m_Version)
' Write Player Name
Local l_NameLength:Int = m_Name.length
Local l_NameBytes:Byte Ptr = m_Name.ToCString()
For Local l_NamePos:Int = 0 Until 16
' Write name and automatically write 0 to the unused space.
p_Buffer.PokeByte(l_Offset + 4 + l_NamePos, l_NameBytes[Min(l_NamePos, l_NameLength)])
Next
MemFree l_NameBytes
' Write initial position and rotation
p_Buffer.PokeFloat(l_Offset + 20, m_Position.X)
p_Buffer.PokeFloat(l_Offset + 24, m_Position.Y)
p_Buffer.PokeFloat(l_Offset + 28, m_Position.Z)
p_Buffer.PokeFloat(l_Offset + 32, m_Rotation.X)
p_Buffer.PokeFloat(l_Offset + 36, m_Rotation.Y)
p_Buffer.PokeFloat(l_Offset + 40, m_Rotation.Z)
Return GetSize()
EndMethod
'----------------------------------------------------------------
'-- Getters / Setters
'----------------------------------------------------------------
Method getUniqueId:Short()
Return m_UniqueId
EndMethod
Method setUniqueId(p_UniqueId:Short)
m_UniqueId = p_UniqueId
EndMethod
Method getUDPPort:Short()
Return m_UDPPort
EndMethod
Method setUDPPort(p_UDPPort:Short)
m_UDPPort = p_UDPPort
EndMethod
Method getVersion:Byte[]()
Return Byte[][m_Version Shr 8 & 255, m_Version & 255]
EndMethod
Method setVersion(p_Major:Byte, p_Minor:Byte)
m_Version = p_Major Shl 8 + p_Minor
EndMethod
Method getName:String()
Return m_Name
EndMethod
Method setName(p_Name:String)
m_Name = p_Name
EndMethod
Method getPosition:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Position:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Position), Byte Ptr(m_Position), SizeOf(m_Position))
Return l_Position
EndMethod
Method setPosition(p_Position:Vector3F)
MemCopy(Byte Ptr(m_Position), Byte Ptr(p_Position), SizeOf(m_Position))
EndMethod
Method getRotation:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Rotation:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Rotation), Byte Ptr(m_Rotation), SizeOf(m_Rotation))
Return l_Rotation
EndMethod
Method setRotation(p_Rotation:Vector3F)
MemCopy(Byte Ptr(m_Rotation), Byte Ptr(p_Rotation), SizeOf(m_Rotation))
EndMethod
End Type
Type BNetPacketLogout Extends BNetPacket
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
' UniqueId of the Client logging out.
Field m_UnqiueId:Short
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetPacket(p_Buffer:TBank)
Return (New BNetPacketLogin).Read(p_Buffer)
End Function
' Constructor
Method New()
' This is a Login packet.
m_Id = BNetPacket.ID_LOGIN
' UDPPort and UniqueId share the same Space in the Packet.
m_UDPPort = VarPtr(m_UniqueId)
' Create vector objects for position and rotation.
m_Position = New Vector3F
m_Rotation = New Vector3F
EndMethod
' Destructor
Method Destroy()
' Destroy vector objects.
m_Rotation = Null
m_Position = Null
' Remove memory link for UDPPort so GC doesn't get confused.
m_UDPPort = Null
EndMethod
' Size of Packet
Method GetSize:Int()
Return Super.GetSize() + 44
EndMethod
' Read the packet data from the buffer.
Method Read:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Read(p_Buffer)
m_UniqueId = p_Buffer.PeekShort(l_Offset)
Return GetSize()
EndMethod
' Write the Packet data to the Buffer.
Method Write:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Write(p_Buffer)
' Write UniqueId
p_Buffer.PokeShort(l_Offset, m_UniqueId)
Return GetSize()
EndMethod
'----------------------------------------------------------------
'-- Getters / Setters
'----------------------------------------------------------------
Method getUniqueId:Short()
Return m_UniqueId
EndMethod
Method setUniqueId(p_UniqueId:Short)
m_UniqueId = p_UniqueId
EndMethod
End Type
Type BNetPacketKick Extends BNetPacket
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
Field m_Reason:String
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetPacket(p_Buffer:TBank)
Return (New BNetPacketKick).Read(p_Buffer)
End Function
' Constructor
Method New()
' This is a Login packet.
m_Id = BNetPacket.ID_KICK
' Create String object for Reason.
m_Reason = ""
EndMethod
' Destructor
Method Destroy()
' Remove String object for Reason
m_Reason = Null
EndMethod
' Size of Packet
Method GetSize:Int()
Return Super.GetSize() + 2 + m_Reason.length
EndMethod
' Read the packet data from the buffer.
Method Read:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Read(p_Buffer)
Local l_ReasonLength:Short = p_Buffer.PeekShort(l_Offset)
For Local l_ReasonPos:Short = 0 Until l_ReasonLength
m_Reason = Chr(p_Buffer.PeekByte(l_Offset + 2 + l_ReasonPos))
Next
Return GetSize()
EndMethod
' Write the Packet data to the Buffer.
Method Write:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Write(p_Buffer)
' Write reason length
Local l_ReasonLength:Short = m_Reason.length
p_Buffer.PokeShort(l_Offset, l_ReasonLength)
' Write kick reason.
Local l_ReasonBytes:Byte Ptr = m_Reason.ToCString()
For Local l_ReasonPos:Short = 0 Until l_ReasonLength
p_Buffer.PokeByte(l_Offset + 2 + l_ReasonPos, l_ReasonBytes[l_ReasonPos])
Next
MemFree l_ReasonBytes
Return GetSize()
EndMethod
'----------------------------------------------------------------
'-- Getters / Setters
'----------------------------------------------------------------
Method getReason:String()
Return m_Reason
EndMethod
Method setReason(p_Reason:String)
m_Reason = p_Reason
EndMethod
End Type
Type BNetPacketData Extends BNetPacket
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
Field m_UniqueId:Short
Field m_Flags:Byte
Field m_Position:Vector3F
Field m_Rotation:Vector3F
Field m_Velocity:Vector3F
Field m_Aim:Vector2F
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
Function Create:BNetPacket(p_Buffer:TBank)
Return (New BNetPacketData).Read(p_Buffer)
End Function
' Constructor
Method New()
' This is a Login packet.
m_Id = BNetPacket.ID_DATA
' Create Vector objects for storage
m_Position = New Vector3F
m_Rotation = New Vector3F
m_Velocity = New Vector3F
m_Aim = New Vector2F
' Set Default Data
m_UniqueId = 0
m_Flags = 0
EndMethod
' Destructor
Method Destroy()
' Release vector object references
m_Position = Null
m_Rotation = Null
m_Velocity = Null
m_Aim = Null
EndMethod
' Size of Packet
Method GetSize:Int()
Local l_Size:Int = 1
If m_Flags & BNET_DATAFLAG_POSITIONX Then l_Size :+ 4
If m_Flags & BNET_DATAFLAG_POSITIONY Then l_Size :+ 4
If m_Flags & BNET_DATAFLAG_POSITIONZ Then l_Size :+ 4
If m_Flags & BNET_DATAFLAG_ROTATIONX Then l_Size :+ 2
If m_Flags & BNET_DATAFLAG_ROTATIONY Then l_Size :+ 2
If m_Flags & BNET_DATAFLAG_ROTATIONZ Then l_Size :+ 2
If m_Flags & BNET_DATAFLAG_VELOCITY Then l_Size :+ 6
If m_Flags & BNET_DATAFLAG_AIM Then l_Size :+ 4
Return Super.GetSize() + l_Size
EndMethod
' Read the packet data from the buffer.
Method Read:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Read(p_Buffer)
m_Flags = p_Buffer.PeekByte(l_Offset);l_Offset :+ 1
If m_Flags & BNET_DATAFLAG_POSITIONX Then
m_Position.X = p_Buffer.PeekFloat(l_Offset)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_POSITIONY Then
m_Position.Y = p_Buffer.PeekFloat(l_Offset)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_POSITIONZ Then
m_Position.Z = p_Buffer.PeekFloat(l_Offset)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONX Then
m_Rotation.X = p_Buffer.PeekShort(l_Offset) / 65536.0 * 360.0
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONY Then
m_Rotation.Y = p_Buffer.PeekShort(l_Offset) / 65536.0 * 360.0
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONZ Then
m_Rotation.Z = p_Buffer.PeekShort(l_Offset) / 65536.0 * 360.0
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_VELOCITY Then
m_Rotation.X = (p_Buffer.PeekShort(l_Offset + 0) / 65536.0) * 256.0
m_Rotation.Y = (p_Buffer.PeekShort(l_Offset + 2) / 65536.0) * 256.0
m_Rotation.Z = (p_Buffer.PeekShort(l_Offset + 4) / 65536.0) * 256.0
l_Offset :+ 6
EndIf
If m_Flags & BNET_DATAFLAG_AIM Then
m_Rotation.X = (p_Buffer.PeekShort(l_Offset + 0) / 65536.0) * 360.0
m_Rotation.Y = (p_Buffer.PeekShort(l_Offset + 2) / 65536.0) * 360.0
l_Offset :+ 4
EndIf
Return GetSize()
EndMethod
' Write the Packet data to the Buffer.
Method Write:Int(p_Buffer:TBank)
Local l_Offset:Int = Super.Write(p_Buffer)
If m_Flags & BNET_DATAFLAG_POSITIONX Then
p_Buffer.PeekFloat(l_Offset, m_Position.X)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_POSITIONY Then
p_Buffer.PeekFloat(l_Offset, m_Position.Y)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_POSITIONZ Then
p_Buffer.PeekFloat(l_Offset, m_Position.Z)
l_Offset :+ 4
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONX Then
p_Buffer.PokeShort(l_Offset, Int((m_Rotation.X / 360.0) * 65536))
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONY Then
p_Buffer.PokeShort(l_Offset, Int((m_Rotation.Y / 360.0) * 65536))
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_ROTATIONZ Then
p_Buffer.PokeShort(l_Offset, Int((m_Rotation.Z / 360.0) * 65536))
l_Offset :+ 2
EndIf
If m_Flags & BNET_DATAFLAG_VELOCITY Then
p_Buffer.PokeShort(l_Offset, Int((m_Velocity.X / 256.0) * 65536))
p_Buffer.PokeShort(l_Offset, Int((m_Velocity.Y / 256.0) * 65536))
p_Buffer.PokeShort(l_Offset, Int((m_Velocity.Z / 256.0) * 65536))
l_Offset :+ 6
EndIf
If m_Flags & BNET_DATAFLAG_AIM Then
p_Buffer.PokeShort(l_Offset, Int((m_Aim.X / 360.0) * 65536))
p_Buffer.PokeShort(l_Offset, Int((m_Aim.Y / 360.0) * 65536))
l_Offset :+ 4
EndIf
Return GetSize()
EndMethod
'----------------------------------------------------------------
'-- Getters / Setters
'----------------------------------------------------------------
Method getUniqueId:Short()
Return m_UniqueId
EndMethod
Method setUniqueId(p_UniqueId:Short)
m_UniqueId = p_UniqueId
EndMethod
Method getFlags:Byte()
Return m_Flags
EndMethod
Method setFlags(p_Flags:Byte)
m_Flags = p_Flags
EndMethod
Method getPosition:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Position:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Position), Byte Ptr(m_Position), SizeOf(m_Position))
Return l_Position
EndMethod
Method setPosition(p_Position:Vector3F)
MemCopy(Byte Ptr(m_Position), Byte Ptr(p_Position), SizeOf(m_Position))
EndMethod
Method getRotation:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Rotation:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Rotation), Byte Ptr(m_Rotation), SizeOf(m_Rotation))
Return l_Rotation
EndMethod
Method setRotation(p_Rotation:Vector3F)
MemCopy(Byte Ptr(m_Rotation), Byte Ptr(p_Rotation), SizeOf(m_Rotation))
EndMethod
Method getVelocity:Vector3F()
' Return a clone of this Vector without bypassing GC.
Local l_Velocity:Vector3F = New Vector3F
MemCopy(Byte Ptr(l_Velocity), Byte Ptr(m_Velocity), SizeOf(m_Velocity))
Return l_Velocity
EndMethod
Method setVelocity(p_Velocity:Vector3F)
MemCopy(Byte Ptr(m_Velocity), Byte Ptr(p_Velocity), SizeOf(m_Velocity))
EndMethod
Method getAim:Vector2F()
' Return a clone of this Vector without bypassing GC.
Local l_Aim:Vector3F = New Vector2F
MemCopy(Byte Ptr(l_Aim), Byte Ptr(m_Aim), SizeOf(m_Aim))
Return l_Aim
EndMethod
Method setAim(p_Aim:Vector2F)
MemCopy(Byte Ptr(m_Aim), Byte Ptr(p_Aim), SizeOf(m_Aim))
EndMethod
Method setFromPlayer(p_Player:BNetPlayer Var)
setUniqueId(p_Player.m_UniqueId)
setPosition(p_Player.m_Position)
setRotation(p_Player.m_Rotation)
setVelocity(p_Player.m_Velocity)
setAim(p_Player.m_Aim)
' Calculate Flags
m_Flags = 0
If Abs(p_Player.m_Position.X - p_Player.m_OldPosition.X) > BNET_THRESHOLD_POSITION Then m_Flags :| BNET_DATAFLAG_POSITIONX
If Abs(p_Player.m_Position.Y - p_Player.m_OldPosition.Y) > BNET_THRESHOLD_POSITION Then m_Flags :| BNET_DATAFLAG_POSITIONY
If Abs(p_Player.m_Position.Z - p_Player.m_OldPosition.Z) > BNET_THRESHOLD_POSITION Then m_Flags :| BNET_DATAFLAG_POSITIONZ
If Abs(p_Player.m_Rotation.X - p_Player.m_OldRotation.X) > BNET_THRESHOLD_ROTATION Then m_Flags :| BNET_DATAFLAG_ROTATIONX
If Abs(p_Player.m_Rotation.Y - p_Player.m_OldRotation.Y) > BNET_THRESHOLD_ROTATION Then m_Flags :| BNET_DATAFLAG_ROTATIONY
If Abs(p_Player.m_Rotation.Z - p_Player.m_OldRotation.Z) > BNET_THRESHOLD_ROTATION Then m_Flags :| BNET_DATAFLAG_ROTATIONZ
If Abs(p_Player.m_Velocity.X - p_Player.m_OldVelocity.X) > BNET_THRESHOLD_VELOCITY Then m_Flags :| BNET_DATAFLAG_VELOCITY
If Abs(p_Player.m_Velocity.Y - p_Player.m_OldVelocity.Y) > BNET_THRESHOLD_VELOCITY Then m_Flags :| BNET_DATAFLAG_VELOCITY
If Abs(p_Player.m_Velocity.Z - p_Player.m_OldVelocity.Z) > BNET_THRESHOLD_VELOCITY Then m_Flags :| BNET_DATAFLAG_VELOCITY
If Abs(p_Player.m_Aim.X - p_Player.m_OldAim.X) > BNET_THRESHOLD_AIM Then m_Flags :| BNET_DATAFLAG_AIM
If Abs(p_Player.m_Aim.Y - p_Player.m_OldAim.Y) > BNET_THRESHOLD_AIM Then m_Flags :| BNET_DATAFLAG_AIM
EndMethod
Method setToPlayer(p_Player:BNetPlayer Var)
' Don't update information for the wrong player.
If m_UniqueId <> p_Player.m_UniqueId Then Return
' Only update those parts that we recieved.
If m_Flags & BNET_DATAFLAG_POSITIONX Then p_Player.m_OldPosition.X = p_Player.m_Position.X; p_Player.m_Position.X = m_Position.X
If m_Flags & BNET_DATAFLAG_POSITIONY Then p_Player.m_OldPosition.Y = p_Player.m_Position.Y; p_Player.m_Position.Y = m_Position.Y
If m_Flags & BNET_DATAFLAG_POSITIONZ Then p_Player.m_OldPosition.Z = p_Player.m_Position.Z; p_Player.m_Position.Z = m_Position.Z
If m_Flags & BNET_DATAFLAG_ROTATIONX Then p_Player.m_OldRotation.X = p_Player.m_Rotation.X; p_Player.m_Rotation.X = m_Rotation.X
If m_Flags & BNET_DATAFLAG_ROTATIONY Then p_Player.m_OldRotation.Y = p_Player.m_Rotation.Y; p_Player.m_Rotation.Y = m_Rotation.Y
If m_Flags & BNET_DATAFLAG_ROTATIONZ Then p_Player.m_OldRotation.Z = p_Player.m_Rotation.Z; p_Player.m_Rotation.Z = m_Rotation.Z
If m_Flags & BNET_DATAFLAG_VELOCITY Then
p_Player.m_OldRotation.X = p_Player.m_Rotation.X
p_Player.m_OldRotation.Y = p_Player.m_Rotation.Y
p_Player.m_OldRotation.Z = p_Player.m_Rotation.Z
p_Player.m_Rotation.X = m_Rotation.X
p_Player.m_Rotation.Y = m_Rotation.Y
p_Player.m_Rotation.Z = m_Rotation.Z
EndIf
If m_Flags & BNET_DATAFLAG_AIM Then
p_Player.m_OldAim.X = p_Player.m_Aim.X
p_Player.m_OldAim.Y = p_Player.m_Aim.Y
p_Player.m_Aim.X = m_Aim.X
p_Player.m_Aim.Y = m_Aim.Y
EndIf
EndMethod
End Type
Type BNetPacketAction Extends BNetPacket
'----------------------------------------------------------------
'-- Variables / Members
'----------------------------------------------------------------
'----------------------------------------------------------------
'-- Functions / Methods
'----------------------------------------------------------------
End Type
'----------------------------------------------------------------
'-- Storage Types
'----------------------------------------------------------------
Type Vector2F
Field X:Float
Field Y:Float
End Type
Type Vector3F Extends Vector2F
Field Z:Float
End Type
Type Vector4F Extends Vector3F
Field Z:Float
End Type