Skip to content

Instantly share code, notes, and snippets.

@ndbeals
Last active November 14, 2018 01:44
Show Gist options
  • Save ndbeals/5ad1d5082841aa74aab0 to your computer and use it in GitHub Desktop.
Save ndbeals/5ad1d5082841aa74aab0 to your computer and use it in GitHub Desktop.
--- Class Creator.
-- Creates a basic class template and returns it to be used for further editing and extending.
-- Supports infinite class inheritence.
--@function Class
--@param base The baseclass for the newly created class to inherit from.
--@return class Returns the new class template to be edited.
function Class( base )
local class = { -- a new class metatable
base = base
}
class.__index = class
local classmt = { -- the new classes' meta table, this allows inheritence if the class has a base, and lets you create a new instance with <classname>()
__index = base
}
classmt.__call = function( class , ... ) -- expose a constructor which can be called by <classname>(<args>)
local obj = {} -- new instance of the class to be manipulated.
setmetatable( obj , class )
if class.Initialize then
class.Initialize( obj , ... )
end
return obj
end
setmetatable(class, classmt)
return class
end
--- Networker Class.
--@section Networker
Networker = Class() -- In charge of networking all of a players needs
--- Networker Class Constructor.
-- Creates a Networker class instance, Limits network usage so users don't flood the server.
--@param player Player to send it to (if used server side)
function Networker:Initialize( player )
self.SendBuffer = {}
self.Receivers = {}
self.ReceiverTemplates = {}
self.PooledNames = {}
self.PooledNum = 0
self:SetPlayer( player )
end
--- Set Player.
-- Sets the player object the networker class will send data too (only useful serverside as clients always send to server)
-- Cannot change player while any buffers are open.
--@param player Player to send it to.
function Networker:SetPlayer( player )
if not player or not IsValid( player ) then error("Invalid Player object",2) return end
if #self.SendBuffer > 0 then error("Cannot change player while any buffers are still open",2) return end
self.Player = player
end
--- Get Player.
-- Gets the player object the networker class will send data too (only useful serverside as clients always send to server)
--@return player Player to send it to.
function Networker:GetPlayer()
return self.Player
end
--- Add To Buffer.
-- adds a variable to the send buffer, basically a wrapper for table.insert.
--@param tab Table input to be added to the send buffer
function Networker:AddToBuffer( tab )
if not self.CurrentBuffer then error("No Current buffer, no message started",2) end
self.CurrentBuffer.Size = self.CurrentBuffer.Size + tab.Size
table.insert( self.CurrentBuffer , tab )
end
--- Write Integer.
-- Writes an Integer to the network buffer.
--@param int Integer to send.
function Networker:WriteInteger( int )
self:AddToBuffer( { net.WriteInt , int , 32 , Size = 4 } )
end
--- Write Unsigned Integer.
-- Writes an unsigned integer to the network buffer.
--@param int Integer to send.
function Networker:WriteUInteger( int )
self:AddToBuffer( { net.WriteUInt , int , 32 , Size = 4 } )
end
--- Write Number.
-- Writes a Number of a given size in bits to the network buffer.
--@param num Number to be written.
--@param size Size in bits of number.
function Networker:WriteNumber( num , size )
size = size or 32
self:AddToBuffer( { net.WriteInt , num , size , Size = size/8 } )
end
--- Write Unsigned Number.
-- Writes an Unsigned Number of a given size in bits to the network buffer.
--@param num Number to be written.
--@param size Size in bits of number.
function Networker:WriteUNumber( num , size )
size = size or 32
self:AddToBuffer( { net.WriteUInt , num , size , Size = size/8 } )
end
--- Write Angle.
-- Writes an angle to the network buffer.
--@param ang Angle to send.
function Networker:WriteAngle( ang )
self:AddToBuffer( { net.WriteAngle , ang , Size = 8 } )
end
--- Write Bit.
-- Writes a bit to the network buffer.
--@param bit Bit to send.
function Networker:WriteBit( bit )
self:AddToBuffer( { net.WriteBit , bit , Size = 0.125 } )
end
--- Write Bool.
-- Writes a boolean to the network buffer.
--@param bool Boolean to send.
function Networker:WriteBool( bool )
self:AddToBuffer( { net.WriteBool, bool , Size = 0.125 } )
end
--- Write Color.
-- Writes a color to the network buffer.
--@param col Color object to send.
function Networker:WriteColor( col )
self:AddToBuffer( { net.WriteColor , col , Size = 4 } )
end
--- Write Data.
-- Writes binary string data to the network buffer.
--@param data Data to send.
function Networker:WriteData( data )
self:AddToBuffer( { net.WriteData , data , Size = #data } )
end
--- Write Doube.
-- Writes a double to the network buffer.
--@param dbl Double to send.
function Networker:WriteDouble( dbl )
self:AddToBuffer( { net.WriteDouble , dbl , Size = 8 } )
end
--- Write Entity.
-- Writes an Entity to the network buffer.
--@param ent Entity to send.
function Networker:WriteEntity( ent )
self:AddToBuffer( { net.WriteEntity , ent , Size = 2 } )
end
--- Write Float.
-- Writes a float to the network buffer.
--@param float Float to send.
function Networker:WriteFloat( float )
self:AddToBuffer( { net.WriteFloat , float , Size = 4 } )
end
--- Write Normal.
-- Writes a vector normal to the network buffer.
--@param nrm Normal to send
function Networker:WriteNormal( nrm )
self:AddToBuffer( { net.WriteNormal , nrm , Size = 4 } )
end
--- Write Vector.
-- Writes a Vector to the network buffer.
--@param vec Vector to send.
function Networker:WriteVector( vec )
self:AddToBuffer( { net.WriteVector , vec , Size = 9 } )
end
--- Write Table.
-- Writes a table to the network buffer. Don't use this often.
--@param tab Table to send.
function Networker:WriteTable( tab )
self:AddToBuffer( { net.WriteTable , tab , Size = 200 } )
end
--- Write String.
-- Writes a String to the network buffer.
--@param str String to send.
function Networker:WriteString( str )
if #str <= 250 then --base message is atleast 4 bytes long
self:AddToBuffer( { net.WriteUInt , 1 , 16 , Size = 2 } )
self:AddToBuffer( { net.WriteString , str , Size = #str } )
else
local chunks = math.ceil( #str / 250 )
if chunks > 2^16 then error("Net message too large >15.625 Mega Bytes (why on earth do you need a single string this large?)",3) end
self:AddToBuffer( { net.WriteUInt , chunks , 16 , Size = 2 } )
for index = 1 , #str , 250 do
self:AddToBuffer( { net.WriteString , str:sub(index,index + 249) , Size = #(str:sub(index,index + 249)) } )
end
end
end
--- Read String.
-- Reads a String from the network buffer.
--@return String.
function Networker:ReadString()
coroutine.yield()
local ret , chunks = {} , net.ReadUInt( 16 )
while chunks >= 1 do
coroutine.yield()
table.insert( ret , net.ReadString() )
chunks = chunks - 1
end
return table.concat( ret )
end
--- Read Integer.
-- Reads a signed integer from the network buffer.
--@return Integer.
function Networker:ReadInteger()
coroutine.yield()
return net.ReadInt( 32 )
end
--- Read Unsigned Integer.
-- Reads an unsigned integer from the network buffer.
--@return UInteger.
function Networker:ReadUInteger()
coroutine.yield()
return net.ReadUInt( 32 )
end
--- Read Number.
-- Reads a Number of a given size in bits from the network buffer.
--@param size Size in bits of number.
--@return Number.
function Networker:ReadNumber( size )
size = size or 32
coroutine.yield()
return net.ReadInt( size )
end
--- Read Unsigned Number.
-- Reads an Unsigned Number of a given size in bits from the network buffer.
--@param size Size in bits of number.
--@return UNumber.
function Networker:ReadUNumber( size )
size = size or 32
coroutine.yield()
return net.ReadUInt( size )
end
--- Read Angle.
-- Reads an angle from the network buffer.
--@return Angle.
function Networker:ReadAngle()
coroutine.yield()
return net.ReadAngle()
end
--- Read Bit.
-- Reads a bit from the network buffer.
--@return Bit.
function Networker:ReadBit()
coroutine.yield()
return net.ReadBit()
end
--- Read Bool.
-- Reads a boolean from the network buffer.
--@return Boolean.
function Networker:ReadBool()
coroutine.yield()
return net.ReadBool()
end
--- Read Color.
-- Reads a color from the network buffer.
--@return Color.
function Networker:ReadColor()
coroutine.yield()
return net.ReadColor()
end
--- Read Data.
-- Reads binary string data from the network buffer.
--@return Data.
function Networker:ReadData()
coroutine.yield()
return net.ReadData()
end
--- Read Doube.
-- Reads a double from the network buffer.
--@return Double.
function Networker:ReadDouble()
coroutine.yield()
return net.ReadDouble()
end
--- Read Entity.
-- Reads an Entity from the network buffer.
--@return Entity.
function Networker:ReadEntity()
coroutine.yield()
return net.ReadEntity()
end
--- Read Float.
-- Reads a float from the network buffer.
--@return Float.
function Networker:ReadFloat()
coroutine.yield()
return net.ReadFloat()
end
--- Read Normal.
-- Reads a vector normal from the network buffer.
--@return Normal.
function Networker:ReadNormal()
coroutine.yield()
return net.ReadNormal()
end
--- Read Vector.
-- Reads a Vector from the network buffer.
--@return Vector.
function Networker:ReadVector()
coroutine.yield()
return net.ReadVector()
end
--- Read Table.
-- Reads a table from the network buffer, Don't use much.
--@param tab Table to send.
function Networker:ReadTable()
coroutine.yield()
return net.ReadTable()
end
--- Start Message.
-- Wrapping around net.Start to control resource usage
--@param name Name of the message to be sent
function Networker:Start( name )
if not name then return end
identity = self:PoolMessage( name )
self.CurrentBuffer = self.SendBuffer[ table.insert( self.SendBuffer , { Name = identity, Size = 0 } ) ]
self.CurrentBuffer.Position = 0
end
--- End Message.
-- Simple wrapper to use the proper net.Send functions depending on server or client
function Networker:EndMessage()
if SERVER then
net.Send( self.Player )
elseif CLIENT then
net.SendToServer()
end
end
--- Send Batch.
-- Sends a batch of info to the other realm (server to client or client to server).
--@param player optional, defaulted to container owner.
function Networker:SendBatch()
local curbuffer , size , messages = self.SendBuffer[ 1 ] , 4 , 0
for msg = 1 , #curbuffer do
local message = curbuffer[msg + curbuffer.Position ]
if not message then
size = size - 256 -- make sure it returns true
break
end
if math.ceil( size + message.Size ) > 256 then
size = math.ceil( size + message.Size )
break
end
messages = msg
size = size + message.Size
end
net.Start( "luabox_sendmessage" )
net.WriteUInt( bit.lshift( curbuffer.Name - 1 , 9 ) + ( messages - 1 ) , 32 )
for msg = 1 , messages do
local message = curbuffer[ msg + curbuffer.Position ]
message[ 1 ]( unpack( message , 2 ) )
end
curbuffer.Position = curbuffer.Position + messages
self:EndMessage()
return size < 256
end
---Finish Send.
-- Removes the net data from the send buffer.
function Networker:FinishSend()
table.remove( self.SendBuffer , 1 )
end
function Networker:Send()
if not self:SendBatch() then
hook.Add( "Think" , "Luabox_NetworkThink:"..tostring( self ) , function()
if self.SendBuffer[1] then
if self:SendBatch() then
hook.Remove( "Think" , "Luabox_NetworkThink:"..tostring( self ) )
self:FinishSend()
end
end
end)
else
self:FinishSend()
end
self.CurrentBuffer = nil
end
function Networker:Receive( name , func )
if not func or not name then return end
--name = self:GetPooledIndex( name )
self.Receivers[ name ] = coroutine.create( func )
self.ReceiverTemplates[ name ] = func
self.FirstExecute = true -- Work around for coroutines needing to be resumed once to start their execution, Runs only when a new coroutine hasn't been ran at all.
end
function Networker:ProcessReceiver( name )
if not name then return end
local coro = self.Receivers[ name ]
if self.FirstExecute then
coroutine.resume( coro )
self.FirstExecute = false
end
coroutine.resume( coro )
if coroutine.status( coro ) == "dead" then
self:Receive( name , self.ReceiverTemplates[ name ] )
end
end
function Networker:PoolMessage( name , index )
if self.PooledNames[ name ] then return self.PooledNames[ name ] end
if not index then
index = self.PooledNum + 1
end
self.PooledNames[ index ] = name
self.PooledNames[ name ] = index
if SERVER then
net.Start( "luabox_poolmessagename" )
net.WriteString( name )
net.WriteUInt( index , 24 )
net.Send( self.Player )
end
self.PooledNum = index
return index
end
function Networker:GetPooledName( idx )
return self.PooledNames [ idx ]
end
if SERVER then
util.AddNetworkString( "luabox_sendmessage" )
util.AddNetworkString( "luabox_poolmessagename" )
end
net.Receive( "luabox_sendmessage" , function( length , ply )
local networker , info = PlayerContainer( ply ):GetNetworker() , net.ReadUInt(32)
local identity = bit.rshift( info , 9 ) + 1
local messages = bit.rshift( bit.lshift( info , 23 ) , 23 ) + 1
while messages > 0 do
networker:ProcessReceiver( networker.PooledNames[ identity ] )
messages = messages - 1
end
end)
net.Receive( "luabox_poolmessagename" , function( length , ply )
local networker , name , poolnum = PlayerContainer( ply ):GetNetworker() , net.ReadString() , net.ReadUInt(24)
networker.PooledNames[ poolnum ] = name
networker.PooledNames[ name ] = poolnum
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment