mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
Upload
This commit is contained in:
12
addons/gm_express/lua/autorun/gm_express_init.lua
Normal file
12
addons/gm_express/lua/autorun/gm_express_init.lua
Normal file
@@ -0,0 +1,12 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile( "includes/modules/pon.lua" )
|
||||
include( "gm_express/sh_init.lua" )
|
||||
63
addons/gm_express/lua/gm_express/cl_init.lua
Normal file
63
addons/gm_express/lua/gm_express/cl_init.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
express._receiverMadeQueue = {}
|
||||
express._canSendReceiverMade = false
|
||||
|
||||
|
||||
net.Receive( "express_access", function()
|
||||
express:SetAccess( net.ReadString() )
|
||||
express:_sendReceiversMadeQueue()
|
||||
end )
|
||||
|
||||
function express:_sendReceiversMadeQueue()
|
||||
express._canSendReceiverMade = true
|
||||
|
||||
local messages = table.GetKeys( express._receiverMadeQueue )
|
||||
express:_alertReceiversMade( unpack( messages ) )
|
||||
end
|
||||
|
||||
function express:_alertReceiversMade( ... )
|
||||
local names = { ... }
|
||||
local receiverCount = #names
|
||||
|
||||
net.Start( "express_receivers_made" )
|
||||
net.WriteUInt( receiverCount, 8 )
|
||||
|
||||
for i = 1, receiverCount do
|
||||
net.WriteString( names[i] )
|
||||
end
|
||||
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
|
||||
-- Registers a basic receiver --
|
||||
function express.Receive( message, cb )
|
||||
express:_setReceiver( message, cb )
|
||||
|
||||
if not express._canSendReceiverMade then
|
||||
express._receiverMadeQueue[message] = true
|
||||
return
|
||||
end
|
||||
|
||||
express:_alertReceiversMade( message )
|
||||
end
|
||||
|
||||
|
||||
-- Calls the main _send function but passes nil for the recipient --
|
||||
function express.Send( message, data, onProof )
|
||||
express:_send( message, data, nil, onProof )
|
||||
end
|
||||
|
||||
|
||||
function express:SetExpected( hash, cb )
|
||||
self._awaitingProof[hash] = cb
|
||||
end
|
||||
278
addons/gm_express/lua/gm_express/sh_helpers.lua
Normal file
278
addons/gm_express/lua/gm_express/sh_helpers.lua
Normal file
@@ -0,0 +1,278 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
express.version = 1
|
||||
express.revision = 1
|
||||
express._putCache = {}
|
||||
express._maxCacheTime = ( 24 - 1 ) * 60 * 60 -- TODO: Get this from the server, similar to the version check
|
||||
express._waitingForAccess = {}
|
||||
express.domain = CreateConVar(
|
||||
"express_domain", "gmod.express", FCVAR_ARCHIVE + FCVAR_REPLICATED, "The domain of the Express server"
|
||||
)
|
||||
|
||||
-- Useful for self-hosting if you need to set express_domain to localhost
|
||||
-- and direct clients to a global IP/domain to hit the same service
|
||||
express.domain_cl = CreateConVar(
|
||||
"express_domain_cl", "", FCVAR_ARCHIVE + FCVAR_REPLICATED, "The client-specific domain of the Express server. If empty, express_domain will be used."
|
||||
)
|
||||
|
||||
express.sendDelay = CreateConVar(
|
||||
"express_send_delay", 0.15, FCVAR_ARCHIVE + FCVAR_REPLICATED, "How long to wait (in seconds) before sending the Express Message ID to the recipient (longer delays will result in increased reliability)"
|
||||
)
|
||||
|
||||
|
||||
-- Runs the correct net Send function based on the realm --
|
||||
function express.shSend( target )
|
||||
if CLIENT then
|
||||
net.SendToServer()
|
||||
else
|
||||
net.Send( target )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Returns the correct domain based on the realm and convars --
|
||||
function express:getDomain()
|
||||
local domain = self.domain:GetString()
|
||||
if SERVER then return domain end
|
||||
|
||||
local clDomain = self.domain_cl:GetString()
|
||||
if clDomain ~= "" then return clDomain end
|
||||
|
||||
return domain
|
||||
end
|
||||
|
||||
|
||||
-- Creates the base of the API URL from the protocol, domain, and version --
|
||||
function express:makeBaseURL()
|
||||
local protocol = self._protocol
|
||||
local domain = self:getDomain()
|
||||
return string.format( "%s://%s/v%d", protocol, domain, self.version )
|
||||
end
|
||||
|
||||
|
||||
-- Creates a full URL with the given access token --
|
||||
function express:makeAccessURL( action, ... )
|
||||
local url = self:makeBaseURL()
|
||||
local args = { action, self.access, ... }
|
||||
|
||||
return url .. "/" .. table.concat( args, "/" )
|
||||
end
|
||||
|
||||
|
||||
-- Sets the access token and runs requests that were waiting --
|
||||
function express:SetAccess( access )
|
||||
self.access = access
|
||||
|
||||
local waiting = self._waitingForAccess
|
||||
for _, callback in ipairs( waiting ) do
|
||||
callback()
|
||||
end
|
||||
|
||||
self._waitingForAccess = {}
|
||||
end
|
||||
|
||||
|
||||
-- Checks the version of the API and alerts of a mismatch --
|
||||
function express.CheckRevision()
|
||||
|
||||
local suffix = " on version check! This is bad!"
|
||||
local err = function( msg )
|
||||
return "Express: " .. msg .. suffix
|
||||
end
|
||||
|
||||
local url = express:makeBaseURL() .. "/revision"
|
||||
local success = function( body, _, _, code )
|
||||
assert( code >= 200 and code < 300, err( "Invalid response code (" .. code .. ")" ) )
|
||||
|
||||
local dataHolder = util.JSONToTable( body )
|
||||
assert( dataHolder, err( "Invalid JSON response" ) )
|
||||
|
||||
local revision = dataHolder.revision
|
||||
assert( revision, err( "Invalid JSON response" ) )
|
||||
|
||||
local current = express.revision
|
||||
if revision ~= current then
|
||||
error( err( "Revision mismatch! Expected " .. current .. ", got " .. revision ) )
|
||||
end
|
||||
end
|
||||
|
||||
http.Fetch( url, success, function( message )
|
||||
error( err( message ) )
|
||||
end, express.jsonHeaders )
|
||||
end
|
||||
|
||||
|
||||
-- Runs the main :Get function, or queues the request if no access token is set --
|
||||
function express:_get( id, cb )
|
||||
if self.access then
|
||||
return self:Get( id, cb )
|
||||
end
|
||||
|
||||
table.insert( self._waitingForAccess, function()
|
||||
self:Get( id, cb )
|
||||
end )
|
||||
end
|
||||
|
||||
|
||||
-- Runs the main :GetSize function, or queues the request if no access token is set --
|
||||
-- FIXME: If this gets delayed because it doesn't have an access token, the PreDl Receiver will not be able to stop the download --
|
||||
function express:_getSize( id, cb )
|
||||
if self.access then
|
||||
return self:GetSize( id, cb )
|
||||
end
|
||||
|
||||
table.insert( self._waitingForAccess, function()
|
||||
self:GetSize( id, cb )
|
||||
end )
|
||||
end
|
||||
|
||||
|
||||
---Encodes and compresses the given data, then sends it to the API if not already cached
|
||||
function express:_put( data, cb )
|
||||
if table.Count( data ) == 0 then
|
||||
error( "Express: Tried to send empty data!" )
|
||||
end
|
||||
|
||||
data = pon.encode( data )
|
||||
|
||||
if string.len( data ) > self._maxDataSize then
|
||||
data = "<enc>" .. util.Compress( data )
|
||||
assert( data, "Express: Failed to compress data!" )
|
||||
|
||||
local dataLen = string.len( data )
|
||||
if dataLen > self._maxDataSize then
|
||||
error( "Express: Data too large (" .. dataLen .. " bytes)" )
|
||||
end
|
||||
end
|
||||
|
||||
local hash = util.SHA1( data )
|
||||
|
||||
local cached = self._putCache[hash]
|
||||
if cached then
|
||||
local cachedAt = cached.cachedAt
|
||||
|
||||
if os.time() <= ( cachedAt + self._maxCacheTime ) then
|
||||
-- Force the callback to run asynchronously for consistency
|
||||
timer.Simple( 0, function()
|
||||
cb( cached.id, hash )
|
||||
end )
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local function wrapCb( id )
|
||||
self._putCache[hash] = { id = id, cachedAt = os.time() }
|
||||
cb( id, hash )
|
||||
end
|
||||
|
||||
if self.access then
|
||||
return self:Put( data, wrapCb )
|
||||
end
|
||||
|
||||
table.insert( self._waitingForAccess, function()
|
||||
self:Put( data, wrapCb )
|
||||
end )
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Fix GLuaTest so we can actually test this function...
|
||||
-- Creates a contextual callback for the :_put endpoint, delaying the notification to the recipient(s) --
|
||||
function express:_putCallback( message, plys, onProof )
|
||||
return function( id, hash )
|
||||
if onProof then
|
||||
self:SetExpected( hash, onProof, plys )
|
||||
end
|
||||
|
||||
-- Cloudflare isn't fulfilling their promise that the first lookup in
|
||||
-- each region will "search" for the target key in K/V if it has't been cached yet.
|
||||
-- This delay makes it more likely that the data will have "settled" into K/V before the first lookup
|
||||
-- (Once it's cached as a 404, it'll stay that way for about 60 seconds)
|
||||
timer.Simple( self.sendDelay:GetFloat(), function()
|
||||
net.Start( "express" )
|
||||
net.WriteString( message )
|
||||
net.WriteString( id )
|
||||
net.WriteBool( onProof ~= nil )
|
||||
|
||||
express.shSend( plys )
|
||||
end )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Calls the _put function with a contextual callback --
|
||||
function express:_send( message, data, plys, onProof )
|
||||
self:_put( data, self:_putCallback( message, plys, onProof ) )
|
||||
end
|
||||
|
||||
|
||||
-- Assigns a callback to the given message --
|
||||
function express:_setReceiver( message, cb )
|
||||
message = string.lower( message )
|
||||
self._receivers[message] = cb
|
||||
end
|
||||
|
||||
|
||||
-- Returns the receiver set for the given message --
|
||||
function express:_getReceiver( message )
|
||||
message = string.lower( message )
|
||||
return self._receivers[message]
|
||||
end
|
||||
|
||||
|
||||
-- Returns the pre-download receiver set for the given message --
|
||||
function express:_getPreDlReceiver( message )
|
||||
message = string.lower( message )
|
||||
return self._preDlReceivers[message]
|
||||
end
|
||||
|
||||
|
||||
-- Returns a realm-specific timeout value for HTTP requests --
|
||||
function express:_getTimeout()
|
||||
return CLIENT and 240 or 60
|
||||
end
|
||||
|
||||
|
||||
-- Ensures that the given HTTP response code indicates a succcessful request --
|
||||
function express._checkResponseCode( code )
|
||||
local isOk = isnumber( code ) and code >= 200 and code < 300
|
||||
if isOk then return end
|
||||
|
||||
error( "Express: Invalid response code (" .. tostring( code ) .. ")" )
|
||||
end
|
||||
|
||||
|
||||
-- Attempts to re-register with the new domain, and then verifies its version --
|
||||
cvars.AddChangeCallback( "express_domain", function()
|
||||
express._putCache = {}
|
||||
|
||||
if SERVER then express:Register() end
|
||||
|
||||
express:CheckRevision()
|
||||
end, "domain_check" )
|
||||
|
||||
-- Both client and server should check the version on startup so that errors are caught early --
|
||||
cvars.AddChangeCallback( "express_domain_cl", function( _, _, new )
|
||||
if CLIENT then express._putCache = {} end
|
||||
if new == "" then return end
|
||||
|
||||
express:CheckRevision()
|
||||
end, "domain_check" )
|
||||
|
||||
|
||||
hook.Add( "ExpressLoaded", "Express_HTTPInit", function()
|
||||
hook.Add( "Tick", "Express_RevisionCheck", function()
|
||||
hook.Remove( "Tick", "Express_RevisionCheck" )
|
||||
if SERVER then express:Register() end
|
||||
express:CheckRevision()
|
||||
end )
|
||||
end )
|
||||
227
addons/gm_express/lua/gm_express/sh_init.lua
Normal file
227
addons/gm_express/lua/gm_express/sh_init.lua
Normal file
@@ -0,0 +1,227 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
require( "pon" )
|
||||
if SERVER then
|
||||
util.AddNetworkString( "express" )
|
||||
util.AddNetworkString( "express_proof" )
|
||||
util.AddNetworkString( "express_receivers_made" )
|
||||
end
|
||||
|
||||
express = {}
|
||||
express._receivers = {}
|
||||
express._protocol = "http"
|
||||
express._awaitingProof = {}
|
||||
express._preDlReceivers = {}
|
||||
express._maxDataSize = 24 * 1024 * 1024
|
||||
express._jsonHeaders = { ["Content-Type"] = "application/json" }
|
||||
express._bytesHeaders = { ["Accept"] = "application/octet-stream" }
|
||||
|
||||
|
||||
-- Removes a receiver --
|
||||
function express.ClearReceiver( message )
|
||||
message = string.lower( message )
|
||||
express._receivers[message] = nil
|
||||
end
|
||||
|
||||
|
||||
-- Registers a PreDownload receiver --
|
||||
function express.ReceivePreDl( message, preDl )
|
||||
message = string.lower( message )
|
||||
express._preDlReceivers[message] = preDl
|
||||
end
|
||||
|
||||
|
||||
-- Retrieves and parses the data for given ID --
|
||||
function express:Get( id, cb, _attempts )
|
||||
_attempts = _attempts or 0
|
||||
local url = self:makeAccessURL( "read", id )
|
||||
|
||||
local success = function( code, body )
|
||||
if code == 404 then
|
||||
assert( _attempts <= 35, "express:Get() failed to retrieve data after 35 attempts: " .. id )
|
||||
timer.Simple( 0.125 * _attempts, function()
|
||||
self:Get( id, cb, _attempts + 1 )
|
||||
end )
|
||||
return
|
||||
end
|
||||
|
||||
express._checkResponseCode( code )
|
||||
if _attempts > 0 then
|
||||
print( "express:Get() succeeded after " .. _attempts .. " attempts: " .. id )
|
||||
end
|
||||
|
||||
if string.StartWith( body, "<enc>" ) then
|
||||
body = util.Decompress( string.sub( body, 6 ) )
|
||||
if ( not body ) or #body == 0 then
|
||||
error( "Express: Failed to decompress data for ID '" .. id .. "'." )
|
||||
end
|
||||
end
|
||||
|
||||
local hash = util.SHA1( body )
|
||||
local decodedData = pon.decode( body )
|
||||
cb( decodedData, hash )
|
||||
end
|
||||
|
||||
HTTP( {
|
||||
method = "GET",
|
||||
url = url,
|
||||
success = success,
|
||||
failed = error,
|
||||
headers = self._bytesHeaders,
|
||||
timeout = self:_getTimeout()
|
||||
} )
|
||||
end
|
||||
|
||||
|
||||
-- Asks the API for this ID's data's size --
|
||||
function express:GetSize( id, cb )
|
||||
local url = self:makeAccessURL( "size", id )
|
||||
|
||||
local success = function( code, body )
|
||||
express._checkResponseCode( code )
|
||||
|
||||
local sizeHolder = util.JSONToTable( body )
|
||||
assert( sizeHolder, "Invalid JSON" )
|
||||
|
||||
local size = sizeHolder.size
|
||||
if not size then
|
||||
print( "Express: Failed to get size for ID '" .. id .. "'.", code )
|
||||
print( body )
|
||||
end
|
||||
assert( size, "No size data" )
|
||||
|
||||
cb( tonumber( size ) )
|
||||
end
|
||||
|
||||
HTTP( {
|
||||
method = "GET",
|
||||
url = url,
|
||||
success = success,
|
||||
failed = error,
|
||||
headers = self._jsonHeaders,
|
||||
timeout = self:_getTimeout()
|
||||
} )
|
||||
end
|
||||
|
||||
|
||||
-- Given prepared data, sends it to the API --
|
||||
function express:Put( data, cb )
|
||||
local success = function( code, body )
|
||||
express._checkResponseCode( code )
|
||||
|
||||
local response = util.JSONToTable( body )
|
||||
assert( response, "Invalid JSON" )
|
||||
assert( response.id, "No ID returned" )
|
||||
|
||||
cb( response.id )
|
||||
end
|
||||
|
||||
HTTP( {
|
||||
method = "POST",
|
||||
url = self:makeAccessURL( "write" ),
|
||||
body = data,
|
||||
success = success,
|
||||
failed = error,
|
||||
headers = {
|
||||
["Content-Length"] = #data,
|
||||
["Accept"] = "application/json"
|
||||
},
|
||||
type = "application/octet-stream",
|
||||
timeout = CLIENT and 240 or 60
|
||||
} )
|
||||
end
|
||||
|
||||
|
||||
-- Runs the express receiver for the given message --
|
||||
function express:Call( message, ply, data )
|
||||
local cb = self:_getReceiver( message )
|
||||
if not cb then return end
|
||||
|
||||
if CLIENT then return cb( data ) end
|
||||
if SERVER then return cb( ply, data ) end
|
||||
end
|
||||
|
||||
|
||||
-- Runs the express pre-download receiver for the given message --
|
||||
function express:CallPreDownload( message, ply, id, size, needsProof )
|
||||
local cb = self:_getPreDlReceiver( message )
|
||||
if not cb then return end
|
||||
|
||||
if CLIENT then return cb( message, id, size, needsProof ) end
|
||||
if SERVER then return cb( message, ply, id, size, needsProof ) end
|
||||
end
|
||||
|
||||
|
||||
-- Handles a net message containing an ID to download from the API --
|
||||
function express.OnMessage( _, ply )
|
||||
local message = net.ReadString()
|
||||
if not express:_getReceiver( message ) then
|
||||
error( "Express: Received a message that has no listener! (" .. message .. ")" )
|
||||
end
|
||||
|
||||
local id = net.ReadString()
|
||||
local needsProof = net.ReadBool()
|
||||
|
||||
local function makeRequest( size )
|
||||
if size then
|
||||
local check = express:CallPreDownload( message, ply, id, size, needsProof )
|
||||
if check == false then return end
|
||||
end
|
||||
|
||||
express:_get( id, function( data, hash )
|
||||
express:Call( message, ply, data )
|
||||
|
||||
if not needsProof then return end
|
||||
net.Start( "express_proof" )
|
||||
net.WriteString( hash )
|
||||
express.shSend( ply )
|
||||
end )
|
||||
end
|
||||
|
||||
if express:_getPreDlReceiver( message ) then
|
||||
return express:_getSize( id, makeRequest )
|
||||
end
|
||||
|
||||
makeRequest()
|
||||
end
|
||||
|
||||
|
||||
-- Handles a net message containing a proof of data download --
|
||||
function express.OnProof( _, ply )
|
||||
-- Server prefixes the hash with the player's Steam ID
|
||||
local prefix = ply and ply:SteamID64() .. "-" or ""
|
||||
local hash = prefix .. net.ReadString()
|
||||
|
||||
local cb = express._awaitingProof[hash]
|
||||
if not cb then return end
|
||||
|
||||
cb( ply )
|
||||
express._awaitingProof[hash] = nil
|
||||
end
|
||||
|
||||
|
||||
net.Receive( "express", express.OnMessage )
|
||||
net.Receive( "express_proof", express.OnProof )
|
||||
|
||||
include( "sh_helpers.lua" )
|
||||
|
||||
if SERVER then
|
||||
include( "sv_init.lua" )
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
else
|
||||
include( "cl_init.lua" )
|
||||
end
|
||||
|
||||
hook.Add( "CreateTeams", "ExpressLoaded", function()
|
||||
hook.Run( "ExpressLoaded" )
|
||||
end )
|
||||
94
addons/gm_express/lua/gm_express/sv_init.lua
Normal file
94
addons/gm_express/lua/gm_express/sv_init.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
require( "playerload" )
|
||||
util.AddNetworkString( "express_access" )
|
||||
|
||||
|
||||
-- Registers a basic receiver --
|
||||
function express.Receive( message, cb )
|
||||
express:_setReceiver( message, cb )
|
||||
end
|
||||
|
||||
|
||||
-- Broadcasts the given data to all connected players --
|
||||
function express.Broadcast( message, data, onProof )
|
||||
express.Send( message, data, player.GetAll(), onProof )
|
||||
end
|
||||
|
||||
|
||||
-- Registers with the current API, storing and distributing access tokens --
|
||||
function express.Register()
|
||||
-- All stored items expire after a day
|
||||
-- That includes tokens, so we need
|
||||
-- to re-register if we make it this far
|
||||
local oneDay = 60 * 60 * 24
|
||||
timer.Create( "Express_Register", oneDay, 0, express.Register )
|
||||
|
||||
local url = express:makeBaseURL() .. "/register"
|
||||
|
||||
http.Fetch( url, function( body, _, _, code )
|
||||
express._checkResponseCode( code )
|
||||
|
||||
local response = util.JSONToTable( body )
|
||||
assert( response, "Invalid JSON" )
|
||||
assert( response.server, "No server access token" )
|
||||
assert( response.client, "No client access token" )
|
||||
|
||||
express:SetAccess( response.server )
|
||||
express._clientAccess = response.client
|
||||
|
||||
if player.GetCount() == 0 then return end
|
||||
|
||||
net.Start( "express_access" )
|
||||
net.WriteString( express._clientAccess )
|
||||
net.Broadcast()
|
||||
end, error, express.jsonHeaders )
|
||||
end
|
||||
|
||||
|
||||
-- Passthrough for the shared _send function --
|
||||
function express.Send( ... )
|
||||
express:_send( ... )
|
||||
end
|
||||
|
||||
|
||||
-- Sets a callback for each of the recipients that will run when they provide proof --
|
||||
function express:SetExpected( hash, cb, plys )
|
||||
if not istable( plys ) then plys = { plys } end
|
||||
|
||||
for _, ply in ipairs( plys ) do
|
||||
local key = ply:SteamID64() .. "-" .. hash
|
||||
self._awaitingProof[key] = cb
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Runs a hook when a player makes a new express Receiver --
|
||||
function express._onReceiverMade( _, ply )
|
||||
local messageCount = net.ReadUInt( 8 )
|
||||
|
||||
for _ = 1, messageCount do
|
||||
local name = string.lower( net.ReadString() )
|
||||
hook.Run( "ExpressPlayerReceiver", ply, name )
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive( "express_receivers_made", express._onReceiverMade )
|
||||
|
||||
|
||||
-- Send the player their access token as soon as it's safe to do so --
|
||||
function express._onPlayerLoaded( ply )
|
||||
net.Start( "express_access" )
|
||||
net.WriteString( express._clientAccess )
|
||||
net.Send( ply )
|
||||
end
|
||||
|
||||
hook.Add( "PlayerFullLoad", "Express_PlayerReady", express._onPlayerLoaded )
|
||||
23
addons/gm_express/lua/includes/modules/playerload.lua
Normal file
23
addons/gm_express/lua/includes/modules/playerload.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local loadQueue = {}
|
||||
|
||||
hook.Add( "PlayerInitialSpawn", "GM_FullLoadQueue", function( ply )
|
||||
loadQueue[ply] = true
|
||||
end )
|
||||
|
||||
hook.Add( "SetupMove", "GM_FullLoadInit", function( ply, _, cmd )
|
||||
if not loadQueue[ply] then return end
|
||||
if cmd:IsForced() then return end
|
||||
|
||||
loadQueue[ply] = nil
|
||||
hook.Run( "PlayerFullLoad", ply )
|
||||
end )
|
||||
415
addons/gm_express/lua/includes/modules/pon.lua
Normal file
415
addons/gm_express/lua/includes/modules/pon.lua
Normal file
@@ -0,0 +1,415 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
|
||||
DEVELOPMENTAL VERSION;
|
||||
|
||||
VERSION 1.2.2
|
||||
Copyright thelastpenguin™
|
||||
|
||||
You may use this for any purpose as long as:
|
||||
- You don't remove this copyright notice.
|
||||
- You don't claim this to be your own.
|
||||
- You properly credit the author, thelastpenguin™, if you publish your work based on (and/or using) this.
|
||||
|
||||
If you modify the code for any purpose, the above still applies to the modified code.
|
||||
|
||||
The author is not held responsible for any damages incured from the use of pon, you use it at your own risk.
|
||||
|
||||
DATA TYPES SUPPORTED:
|
||||
- tables - k,v - pointers
|
||||
- strings - k,v - pointers
|
||||
- numbers - k,v
|
||||
- booleans- k,v
|
||||
- Vectors - k,v
|
||||
- Angles - k,v
|
||||
- Entities- k,v
|
||||
- Players - k,v
|
||||
- PhysObj - k,v
|
||||
|
||||
CHANGE LOG
|
||||
V 1.1.0
|
||||
- Added Vehicle, NPC, NextBot, Player, Weapon
|
||||
V 1.2.0
|
||||
- Added custom handling for k,v tables without any array component.
|
||||
V 1.2.1
|
||||
- fixed deserialization bug.
|
||||
|
||||
THANKS TO...
|
||||
- VERCAS for the inspiration.
|
||||
]]
|
||||
|
||||
|
||||
local pon = {};
|
||||
_G.pon = pon;
|
||||
|
||||
local type, count = type, table.Count ;
|
||||
local tonumber = tonumber ;
|
||||
local format = string.format;
|
||||
do
|
||||
local type, count = type, table.Count ;
|
||||
local tonumber = tonumber ;
|
||||
local format = string.format;
|
||||
|
||||
local encode = {};
|
||||
|
||||
local tryCache ;
|
||||
|
||||
local cacheSize = 0;
|
||||
|
||||
encode['table'] = function( self, tbl, output, cache )
|
||||
|
||||
if( cache[ tbl ] )then
|
||||
output[ #output + 1 ] = format('(%x)', cache[tbl] );
|
||||
return ;
|
||||
else
|
||||
cacheSize = cacheSize + 1;
|
||||
cache[ tbl ] = cacheSize;
|
||||
end
|
||||
|
||||
|
||||
local first = next(tbl, nil)
|
||||
local predictedNumeric = 1
|
||||
local lastKey = nil
|
||||
-- starts with a numeric dealio
|
||||
if first == 1 then
|
||||
output[#output + 1] = '{'
|
||||
|
||||
for k,v in next, tbl do
|
||||
if k == predictedNumeric then
|
||||
predictedNumeric = predictedNumeric + 1
|
||||
|
||||
local tv = type(v)
|
||||
if tv == 'string' then
|
||||
local pid = cache[v]
|
||||
if pid then
|
||||
output[#output + 1] = format('(%x)', pid)
|
||||
else
|
||||
cacheSize = cacheSize + 1
|
||||
cache[v] = cacheSize
|
||||
self.string(self, v, output, cache)
|
||||
end
|
||||
else
|
||||
self[tv](self, v, output, cache)
|
||||
end
|
||||
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
predictedNumeric = predictedNumeric - 1
|
||||
else
|
||||
predictedNumeric = nil
|
||||
end
|
||||
|
||||
if predictedNumeric == nil then
|
||||
output[#output + 1] = '[' -- no array component
|
||||
else
|
||||
output[#output + 1] = '~' -- array component came first so shit needs to happen
|
||||
end
|
||||
|
||||
for k, v in next, tbl, predictedNumeric do
|
||||
local tk, tv = type(k), type(v)
|
||||
|
||||
-- WRITE KEY
|
||||
if tk == 'string' then
|
||||
local pid = cache[ k ];
|
||||
if( pid )then
|
||||
output[ #output + 1 ] = format('(%x)', pid );
|
||||
else
|
||||
cacheSize = cacheSize + 1;
|
||||
cache[ k ] = cacheSize;
|
||||
|
||||
self.string( self, k, output, cache );
|
||||
end
|
||||
else
|
||||
self[tk](self, k, output, cache)
|
||||
end
|
||||
|
||||
-- WRITE VALUE
|
||||
if( tv == 'string' )then
|
||||
local pid = cache[ v ];
|
||||
if( pid )then
|
||||
output[ #output + 1 ] = format('(%x)', pid );
|
||||
else
|
||||
cacheSize = cacheSize + 1;
|
||||
cache[ v ] = cacheSize;
|
||||
|
||||
self.string( self, v, output, cache );
|
||||
end
|
||||
else
|
||||
self[ tv ]( self, v, output, cache );
|
||||
end
|
||||
end
|
||||
|
||||
output[#output + 1] = '}'
|
||||
end
|
||||
-- ENCODE STRING
|
||||
local gsub = string.gsub ;
|
||||
encode['string'] = function( self, str, output )
|
||||
--if tryCache( str, output ) then return end
|
||||
local estr, count = gsub( str, ";", "\\;");
|
||||
if( count == 0 )then
|
||||
output[ #output + 1 ] = '\''..str..';';
|
||||
else
|
||||
output[ #output + 1 ] = '"'..estr..'";';
|
||||
end
|
||||
end
|
||||
-- ENCODE NUMBER
|
||||
encode['number'] = function( self, num, output )
|
||||
if num%1 == 0 then
|
||||
if num < 0 then
|
||||
output[ #output + 1 ] = format( 'x%x;', -num );
|
||||
else
|
||||
output[ #output + 1 ] = format('X%x;', num );
|
||||
end
|
||||
else
|
||||
output[ #output + 1 ] = tonumber( num )..';';
|
||||
end
|
||||
end
|
||||
-- ENCODE BOOLEAN
|
||||
encode['boolean'] = function( self, val, output )
|
||||
output[ #output + 1 ] = val and 't' or 'f'
|
||||
end
|
||||
-- ENCODE VECTOR
|
||||
encode['Vector'] = function( self, val, output )
|
||||
output[ #output + 1 ] = ('v'..val.x..','..val.y)..(','..val.z..';');
|
||||
end
|
||||
-- ENCODE ANGLE
|
||||
encode['Angle'] = function( self, val, output )
|
||||
output[ #output + 1 ] = ('a'..val.p..','..val.y)..(','..val.r..';');
|
||||
end
|
||||
encode['Entity'] = function( self, val, output )
|
||||
local entIndex = val == NULL and '#' or val:EntIndex();
|
||||
output[ #output + 1] = 'E'..entIndex..';';
|
||||
end
|
||||
encode['Player'] = encode['Entity'];
|
||||
encode['Vehicle'] = encode['Entity'];
|
||||
encode['Weapon'] = encode['Entity'];
|
||||
encode['NPC'] = encode['Entity'];
|
||||
encode['NextBot'] = encode['Entity'];
|
||||
encode['PhysObj'] = encode['Entity'];
|
||||
|
||||
encode['nil'] = function( _, _, output )
|
||||
output[ #output + 1 ] = '?;';
|
||||
end
|
||||
|
||||
setmetatable( encode, {
|
||||
__index = function( self, key )
|
||||
local val = rawget( self, key );
|
||||
if val then return val end
|
||||
ErrorNoHalt('Type: '..key..' can not be encoded. Encoded as as pass-over value.');
|
||||
return rawget( self, 'nil' );
|
||||
end
|
||||
});
|
||||
|
||||
do
|
||||
local empty, concat = table.Empty, table.concat ;
|
||||
function pon.encode( tbl )
|
||||
local output = {};
|
||||
cacheSize = 0;
|
||||
encode[ 'table' ]( encode, tbl, output, {} );
|
||||
local res = concat( output );
|
||||
|
||||
return res;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local tonumber = tonumber ;
|
||||
local find, sub, gsub, Explode = string.find, string.sub, string.gsub, string.Explode ;
|
||||
local Vector, Angle, Entity = Vector, Angle, Entity ;
|
||||
|
||||
local decode = {};
|
||||
decode['{'] = function( self, index, str, cache )
|
||||
|
||||
local cur = {};
|
||||
cache[ #cache + 1 ] = cur;
|
||||
|
||||
local k, v, tk, tv = 1, nil, nil, nil;
|
||||
while( true )do
|
||||
tv = sub( str, index, index );
|
||||
if( not tv or tv == '~' )then
|
||||
index = index + 1;
|
||||
break ;
|
||||
end
|
||||
if( tv == '}' )then
|
||||
return index + 1, cur;
|
||||
end
|
||||
|
||||
-- READ THE VALUE
|
||||
index = index + 1;
|
||||
index, v = self[ tv ]( self, index, str, cache );
|
||||
cur[ k ] = v;
|
||||
|
||||
k = k + 1;
|
||||
end
|
||||
|
||||
while( true )do
|
||||
tk = sub( str, index, index );
|
||||
if( not tk or tk == '}' )then
|
||||
index = index + 1;
|
||||
break ;
|
||||
end
|
||||
|
||||
-- READ THE KEY
|
||||
index = index + 1;
|
||||
index, k = self[ tk ]( self, index, str, cache );
|
||||
|
||||
-- READ THE VALUE
|
||||
tv = sub( str, index, index );
|
||||
index = index + 1;
|
||||
index, v = self[ tv ]( self, index, str, cache );
|
||||
|
||||
cur[ k ] = v;
|
||||
end
|
||||
|
||||
return index, cur;
|
||||
end
|
||||
decode['['] = function( self, index, str, cache )
|
||||
|
||||
local cur = {};
|
||||
cache[ #cache + 1 ] = cur;
|
||||
|
||||
local k, v, tk, tv = 1, nil, nil, nil;
|
||||
while( true )do
|
||||
tk = sub( str, index, index );
|
||||
if( not tk or tk == '}' )then
|
||||
index = index + 1;
|
||||
break ;
|
||||
end
|
||||
|
||||
-- READ THE KEY
|
||||
index = index + 1;
|
||||
index, k = self[ tk ]( self, index, str, cache );
|
||||
if not k then continue end
|
||||
|
||||
-- READ THE VALUE
|
||||
tv = sub( str, index, index );
|
||||
index = index + 1;
|
||||
if not self[tv] then
|
||||
print('did not find type: '..tv)
|
||||
end
|
||||
index, v = self[ tv ]( self, index, str, cache );
|
||||
|
||||
cur[ k ] = v;
|
||||
end
|
||||
|
||||
return index, cur;
|
||||
end
|
||||
|
||||
-- STRING
|
||||
decode['"'] = function( self, index, str, cache )
|
||||
local finish = find( str, '";', index, true );
|
||||
local res = gsub( sub( str, index, finish - 1 ), '\\;', ';' );
|
||||
index = finish + 2;
|
||||
|
||||
cache[ #cache + 1 ] = res;
|
||||
return index, res;
|
||||
end
|
||||
-- STRING NO ESCAPING NEEDED
|
||||
decode['\''] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local res = sub( str, index, finish - 1 )
|
||||
index = finish + 1;
|
||||
|
||||
cache[ #cache + 1 ] = res;
|
||||
return index, res;
|
||||
end
|
||||
|
||||
-- NUMBER
|
||||
decode['n'] = function( self, index, str, cache )
|
||||
index = index - 1;
|
||||
local finish = find( str, ';', index, true );
|
||||
local num = tonumber( sub( str, index, finish - 1 ) );
|
||||
index = finish + 1;
|
||||
return index, num;
|
||||
end
|
||||
decode['0'] = decode['n'];
|
||||
decode['1'] = decode['n'];
|
||||
decode['2'] = decode['n'];
|
||||
decode['3'] = decode['n'];
|
||||
decode['4'] = decode['n'];
|
||||
decode['5'] = decode['n'];
|
||||
decode['6'] = decode['n'];
|
||||
decode['7'] = decode['n'];
|
||||
decode['8'] = decode['n'];
|
||||
decode['9'] = decode['n'];
|
||||
decode['-'] = decode['n'];
|
||||
-- positive hex
|
||||
decode['X'] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local num = tonumber( sub( str, index, finish - 1), 16 );
|
||||
index = finish + 1;
|
||||
return index, num;
|
||||
end
|
||||
-- negative hex
|
||||
decode['x'] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local num = -tonumber( sub( str, index, finish - 1), 16 );
|
||||
index = finish + 1;
|
||||
return index, num;
|
||||
end
|
||||
|
||||
-- POINTER
|
||||
decode['('] = function( self, index, str, cache )
|
||||
local finish = find( str, ')', index, true );
|
||||
local num = tonumber( sub( str, index, finish - 1), 16 );
|
||||
index = finish + 1;
|
||||
return index, cache[ num ];
|
||||
end
|
||||
|
||||
-- BOOLEAN. ONE DATA TYPE FOR YES, ANOTHER FOR NO.
|
||||
decode[ 't' ] = function( self, index )
|
||||
return index, true;
|
||||
end
|
||||
decode[ 'f' ] = function( self, index )
|
||||
return index, false;
|
||||
end
|
||||
|
||||
-- VECTOR
|
||||
decode[ 'v' ] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local vecStr = sub( str, index, finish - 1 );
|
||||
index = finish + 1;
|
||||
local segs = Explode( ',', vecStr, false );
|
||||
return index, Vector( segs[1], segs[2], segs[3] );
|
||||
end
|
||||
-- ANGLE
|
||||
decode[ 'a' ] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local angStr = sub( str, index, finish - 1 );
|
||||
index = finish + 1;
|
||||
local segs = Explode( ',', angStr, false );
|
||||
return index, Angle( tonumber( segs[1] ), tonumber( segs[2] ), tonumber( segs[3] ) );
|
||||
end
|
||||
-- ENTITY
|
||||
decode[ 'E' ] = function( self, index, str, cache )
|
||||
local finish = find( str, ';', index, true );
|
||||
local num = sub( str, index, finish - 1 );
|
||||
index = finish + 1;
|
||||
return index, num == '#' and NULL or Entity( num );
|
||||
end
|
||||
-- PLAYER
|
||||
decode[ 'P' ] = decode[ 'E' ];
|
||||
-- NIL
|
||||
decode[ '?' ] = function( _, index )
|
||||
return index + 1, nil;
|
||||
end
|
||||
|
||||
|
||||
function pon.decode( data )
|
||||
local _, res = decode[ sub( data, 1, 1 ) ]( decode, 2, data, {});
|
||||
return res;
|
||||
end
|
||||
end
|
||||
48
addons/gm_express/lua/tests/gm_express/pon.lua
Normal file
48
addons/gm_express/lua/tests/gm_express/pon.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
return {
|
||||
groupName = "pON",
|
||||
|
||||
beforeAll = function()
|
||||
require( "pon" )
|
||||
end,
|
||||
|
||||
beforeEach = function( state )
|
||||
state.tbl = {
|
||||
1, "a", true, false, nil, {}, { {}, {}, nil },
|
||||
Color( 1, 1, 1 ), Angle( 1, 1, 1 ), Vector( 1, 1, 1 ), game.GetWorld()
|
||||
}
|
||||
end,
|
||||
|
||||
cases = {
|
||||
{
|
||||
name = "It loads properly",
|
||||
func = function()
|
||||
expect( pon ).to.exist()
|
||||
end
|
||||
},
|
||||
|
||||
{
|
||||
name = "It encodes a table",
|
||||
func = function( state )
|
||||
expect( pon.encode, state.tbl ).to.succeed()
|
||||
end
|
||||
},
|
||||
|
||||
{
|
||||
name = "It decodes a pON string",
|
||||
func = function( state )
|
||||
local encoded = pon.encode( state.tbl )
|
||||
expect( pon.decode, encoded ).to.succeed()
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
796
addons/gm_express/lua/tests/gm_express/sh_helpers.lua
Normal file
796
addons/gm_express/lua/tests/gm_express/sh_helpers.lua
Normal file
@@ -0,0 +1,796 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
return {
|
||||
groupName = "Shared Helpers",
|
||||
|
||||
cases = {
|
||||
-- variables
|
||||
{
|
||||
name = "Should create necessary variables",
|
||||
func = function()
|
||||
expect( express.version ).to.exist()
|
||||
expect( express.version ).to.beA( "number" )
|
||||
|
||||
expect( express.revision ).to.exist()
|
||||
expect( express.revision ).to.beA( "number" )
|
||||
|
||||
expect( express._putCache ).to.exist()
|
||||
expect( express._putCache ).to.beA( "table" )
|
||||
|
||||
expect( express._waitingForAccess ).to.exist()
|
||||
expect( express._waitingForAccess ).to.beA( "table" )
|
||||
end
|
||||
},
|
||||
|
||||
-- express.shSend
|
||||
{
|
||||
name = "express.shSend calls SendToServer on CLIENT",
|
||||
|
||||
func = function()
|
||||
_G.CLIENT = true
|
||||
_G.SERVER = false
|
||||
|
||||
local send = stub( net, "Send" )
|
||||
local sendToServer = stub( net, "SendToServer" )
|
||||
|
||||
express.shSend()
|
||||
|
||||
expect( send ).wasNot.called()
|
||||
expect( sendToServer ).was.called()
|
||||
end,
|
||||
|
||||
cleanup = function()
|
||||
_G.CLIENT = false
|
||||
_G.SERVER = true
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.shSend calls Send on SERVER",
|
||||
|
||||
func = function()
|
||||
local send = stub( net, "Send" )
|
||||
local sendToServer = stub( net, "SendToServer" )
|
||||
|
||||
express.shSend()
|
||||
|
||||
expect( send ).was.called()
|
||||
expect( sendToServer ).wasNot.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.getDomain
|
||||
{
|
||||
name = "express.getDomain returns express_domain on SERVER",
|
||||
func = function( state )
|
||||
state.original_domain = express.domain
|
||||
express.domain = {
|
||||
GetString = function()
|
||||
return "example.cfcservers.org"
|
||||
end
|
||||
}
|
||||
|
||||
expect( express:getDomain() ).to.equal( "example.cfcservers.org" )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.domain = state.original_domain
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.getDomain returns express_domain on CLIENT if express_domain_cl is empty",
|
||||
func = function( state )
|
||||
_G.CLIENT = true
|
||||
_G.SERVER = false
|
||||
state.original_domain = express.domain
|
||||
state.original_cl_domain = express.domain_cl
|
||||
|
||||
express.domain = {
|
||||
GetString = function()
|
||||
return "example.cfcservers.org"
|
||||
end
|
||||
}
|
||||
|
||||
express.domain_cl = {
|
||||
GetString = function()
|
||||
return ""
|
||||
end
|
||||
}
|
||||
|
||||
expect( express:getDomain() ).to.equal( "example.cfcservers.org" )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
_G.CLIENT = false
|
||||
_G.SERVER = true
|
||||
express.domain = state.original_domain
|
||||
express.domain_cl = state.original_cl_domain
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.getDomain returns express_domain_cl on CLIENT if express_domain_cl is not empty",
|
||||
func = function( state )
|
||||
_G.CLIENT = true
|
||||
_G.SERVER = false
|
||||
state.original_domain = express.domain
|
||||
state.original_cl_domain = express.domain_cl
|
||||
|
||||
express.domain = {
|
||||
GetString = function()
|
||||
return "example.cfcservers.org"
|
||||
end
|
||||
}
|
||||
|
||||
express.domain_cl = {
|
||||
GetString = function()
|
||||
return "cl.cfcservers.org"
|
||||
end
|
||||
}
|
||||
|
||||
expect( express:getDomain() ).to.equal( "cl.cfcservers.org" )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
_G.CLIENT = false
|
||||
_G.SERVER = true
|
||||
express.domain = state.original_domain
|
||||
express.domain_cl = state.original_cl_domain
|
||||
end
|
||||
},
|
||||
|
||||
-- express.makeBaseURL
|
||||
{
|
||||
name = "express.makeBaseURL makes the correct URL",
|
||||
func = function( state )
|
||||
state.original_protocol = express._protocol
|
||||
state.original_version = express.version
|
||||
|
||||
express.version = "1"
|
||||
express._protocol = "https"
|
||||
stub( express, "getDomain" ).returns( "example.cfcservers.org" )
|
||||
|
||||
local expected = "https://example.cfcservers.org/v1"
|
||||
local actual = express:makeBaseURL()
|
||||
|
||||
expect( actual ).to.equal( expected )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._protocol = state.original_protocol
|
||||
express.version = state.original_version
|
||||
end
|
||||
},
|
||||
|
||||
-- express.makeAccessURL
|
||||
{
|
||||
name = "express.makeAccessURL makes the correct URL",
|
||||
func = function( state )
|
||||
state.original_protocol = express._protocol
|
||||
state.original_domain = express.domain
|
||||
state.original_version = express.version
|
||||
state.original_access = express.access
|
||||
|
||||
express._protocol = "https"
|
||||
express.domain = {
|
||||
GetString = function() return "example.cfcservers.org" end
|
||||
}
|
||||
express.version = "1"
|
||||
express.access = "access-token"
|
||||
|
||||
local action = "action"
|
||||
local param = "param"
|
||||
|
||||
local expected = "https://example.cfcservers.org/v1/action/access-token/param"
|
||||
local actual = express:makeAccessURL( action, param )
|
||||
|
||||
expect( actual ).to.equal( expected )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._protocol = state.original_protocol
|
||||
express.domain = state.original_domain
|
||||
express.version = state.original_version
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
|
||||
-- express.SetAccess
|
||||
{
|
||||
name = "express.SetAccess sets the access token to the given value",
|
||||
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
|
||||
-- Sanity check
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
local access = "access-token"
|
||||
express:SetAccess( access )
|
||||
|
||||
expect( express.access ).to.equal( access )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.SetAccess runs pending requests",
|
||||
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
|
||||
-- Sanity check
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
local waitingStub = stub()
|
||||
express._waitingForAccess = { waitingStub }
|
||||
|
||||
express:SetAccess( "access-token" )
|
||||
expect( waitingStub ).was.called()
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
|
||||
-- express.CheckRevision
|
||||
{
|
||||
name = "express.CheckRevision alerts if the request fails",
|
||||
|
||||
func = function()
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, _, failure )
|
||||
expect( failure, "unsuccessful" ).to.errWith( "Express: unsuccessful on version check! This is bad!" )
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.CheckRevision alerts if the request succeeds with a non 200-level status code",
|
||||
|
||||
func = function()
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, success )
|
||||
expect( success, "", "nil", "nil", 418 ).to.errWith(
|
||||
"Express: Invalid response code (418) on version check! This is bad!"
|
||||
)
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.CheckRevision alerts if it cannot parse the response",
|
||||
|
||||
func = function()
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, success )
|
||||
expect( success, "", "nil", "nil", 200 ).to.errWith(
|
||||
"Express: Invalid JSON response on version check! This is bad!"
|
||||
)
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.CheckRevision alerts if it cannot get a revision from the response",
|
||||
|
||||
func = function()
|
||||
local response = util.TableToJSON( { notTheRevisionLol = "0" } )
|
||||
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, success )
|
||||
expect( success, response, "nil", "nil", 200 ).to.errWith(
|
||||
"Express: Invalid JSON response on version check! This is bad!"
|
||||
)
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.CheckRevision alerts if the remote revision doesn't match the current addon revision",
|
||||
|
||||
func = function( state )
|
||||
state.original_revision = state.original_revision or express.revision
|
||||
|
||||
express.revision = 1
|
||||
local response = util.TableToJSON( { revision = 0 } )
|
||||
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, success )
|
||||
expect( success, response, "nil", "nil", 200 ).to.errWith(
|
||||
"Express: Revision mismatch! Expected 1, got 0 on version check! This is bad!"
|
||||
)
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.revision = state.original_revision
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.CheckRevision does nothing if the revisions match",
|
||||
|
||||
func = function( state )
|
||||
state.original_revision = state.original_revision or express.revision
|
||||
|
||||
express.revision = 1
|
||||
local response = util.TableToJSON( { revision = 1 } )
|
||||
|
||||
local fetchStub = stub( http, "Fetch" ).with( function( _, success )
|
||||
expect( success, response, "nil", "nil", 200 ).to.succeed()
|
||||
end )
|
||||
|
||||
express.CheckRevision()
|
||||
expect( fetchStub ).was.called()
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.revision = state.original_revision
|
||||
end
|
||||
},
|
||||
|
||||
-- express._get
|
||||
{
|
||||
name = "express._get calls express.Get if the access token is set",
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
|
||||
-- Sanity check
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
express.access = "access-token"
|
||||
local getStub = stub( express, "Get" )
|
||||
express:_get( "id", "callback" )
|
||||
|
||||
expect( getStub ).was.called()
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._get adds the request to the waiting list if the access token is not set",
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
|
||||
-- Sanity check
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
express.access = nil
|
||||
local getStub = stub( express, "Get" )
|
||||
express:_get( "id", "callback" )
|
||||
|
||||
expect( getStub ).wasNot.called()
|
||||
expect( #express._waitingForAccess ).to.equal( 1 )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._waitingForAccess = {}
|
||||
end
|
||||
},
|
||||
|
||||
-- express._put
|
||||
{
|
||||
name = "express._put encodes the given data if the access token is set",
|
||||
func = function( state )
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = "access-token"
|
||||
|
||||
local encode = stub( pon, "encode" ).returns( "encoded-data" )
|
||||
local compress = stub( util, "Compress" ).returns( "hello" )
|
||||
local putStub = stub( express, "Put" )
|
||||
|
||||
express:_put( { "data" }, "callback" )
|
||||
|
||||
expect( encode ).was.called()
|
||||
expect( compress ).wasNot.called()
|
||||
expect( putStub ).was.called()
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._putCache = {}
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._put compresses the given data if the access token is set and data exceeds max size",
|
||||
func = function( state )
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
|
||||
state.original_putCache = state.original_putCache or express._putCache
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = "access-token"
|
||||
|
||||
local encode = stub( pon, "encode" ).returns( "encoded-data" )
|
||||
local compress = stub( util, "Compress" ).returns( "hello" )
|
||||
local putStub = stub( express, "Put" )
|
||||
|
||||
stub( util, "SHA1" ).returns( "hash" )
|
||||
stub( string, "len" ).returnsSequence( { express._maxDataSize + 1, 1 } )
|
||||
|
||||
express:_put( { "data" }, "callback" )
|
||||
|
||||
expect( encode ).was.called()
|
||||
expect( compress ).was.called()
|
||||
expect( putStub ).was.called()
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._putCache = state.original_putCache
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._put queues the request if the access token is not set",
|
||||
func = function( state )
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = nil
|
||||
|
||||
local encode = stub( pon, "encode" ).returns( "encoded-data" )
|
||||
local compress = stub( util, "Compress" ).returns( "hello" )
|
||||
local putStub = stub( express, "Put" )
|
||||
|
||||
express:_put( { "data" }, "callback" )
|
||||
|
||||
expect( encode ).was.called()
|
||||
expect( compress ).wasNot.called()
|
||||
expect( putStub ).wasNot.called()
|
||||
|
||||
expect( #express._waitingForAccess ).to.equal( 1 )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._waitingForAccess = {}
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._put rejects data that is too large",
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
state.original_maxDataSize = state.original_maxDataSize or express._maxDataSize
|
||||
express._maxDataSize = 0
|
||||
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
|
||||
local mockData = "hello"
|
||||
local expectedBytes = #( "<enc>" .. mockData )
|
||||
local putStub = stub( express, "Put" )
|
||||
|
||||
stub( pon, "encode" ).returns( mockData )
|
||||
stub( util, "Compress" ).returns( mockData )
|
||||
|
||||
expect( express._put, express, { "data" }, stub() ).to.errWith(
|
||||
"Express: Data too large (" .. expectedBytes .. " bytes)"
|
||||
)
|
||||
|
||||
expect( putStub ).wasNot.called()
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._maxDataSize = state.original_maxDataSize
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._put returns the ID from the cache if the data is already cached",
|
||||
async = true,
|
||||
timeout = 0.2,
|
||||
func = function()
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
|
||||
local mockData = { "hello" }
|
||||
local mockId = "test-id"
|
||||
local mockHash = "test-hash"
|
||||
local mockCallback = stub()
|
||||
local putStub = stub( express, "Put" )
|
||||
|
||||
express._putCache[mockHash] = {
|
||||
id = mockId,
|
||||
cachedAt = os.time()
|
||||
}
|
||||
|
||||
stub( pon, "encode" ).returns( "encoded-data" )
|
||||
stub( util, "Compress" ).returns( mockData )
|
||||
stub( util, "SHA1" ).returns( mockHash )
|
||||
|
||||
express:_put( mockData, mockCallback )
|
||||
|
||||
timer.Simple( 0.1, function()
|
||||
expect( putStub ).wasNot.called()
|
||||
expect( mockCallback ).was.called()
|
||||
done()
|
||||
end )
|
||||
end,
|
||||
|
||||
cleanup = function()
|
||||
express._putCache = {}
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._put on success, calls given callback and stores response ID in cache",
|
||||
func = function( state )
|
||||
-- Sanity check
|
||||
expect( table.Count( express._putCache ) ).to.equal( 0 )
|
||||
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = "access-token"
|
||||
|
||||
local mockData = "hello"
|
||||
local mockId = "test-id"
|
||||
local mockHash = "test-hash"
|
||||
local mockCallback = stub()
|
||||
local putStub = stub( express, "Put" ).with( function( _, _, cb )
|
||||
cb( mockId )
|
||||
end )
|
||||
|
||||
stub( pon, "encode" ).returns( "encoded-data" )
|
||||
stub( util, "Compress" ).returns( mockData )
|
||||
stub( util, "SHA1" ).returns( mockHash )
|
||||
|
||||
express:_put( { "data" }, mockCallback )
|
||||
|
||||
expect( putStub ).was.called()
|
||||
expect( mockCallback ).was.called()
|
||||
|
||||
local actualCached = express._putCache[mockHash]
|
||||
expect( actualCached ).to.exist()
|
||||
expect( actualCached.id ).to.equal( mockId )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._putCache = {}
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
|
||||
-- express:_getSize
|
||||
{
|
||||
name = "express:_getSize calls express:GetSize if access token is set",
|
||||
func = function( state )
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = "access-token"
|
||||
|
||||
local getSizeStub = stub( express, "GetSize" )
|
||||
express:_getSize( "id", stub() )
|
||||
|
||||
expect( getSizeStub ).was.called()
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express:_getSize queues the GetSize call if access token is not set",
|
||||
func = function( state )
|
||||
-- Sanity check
|
||||
expect( #express._waitingForAccess ).to.equal( 0 )
|
||||
|
||||
state.original_access = state.original_access or express.access
|
||||
express.access = nil
|
||||
|
||||
local getSizeStub = stub( express, "GetSize" )
|
||||
express:_getSize( "id", stub() )
|
||||
|
||||
expect( getSizeStub ).wasNot.called()
|
||||
expect( #express._waitingForAccess ).to.equal( 1 )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express.access = state.original_access
|
||||
express._waitingForAccess = {}
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
-- express:_send
|
||||
{
|
||||
name = "express._send calls _putCallback",
|
||||
func = function()
|
||||
local putCallback = stub()
|
||||
stub( express, "_putCallback" ).returns( putCallback )
|
||||
|
||||
local putStub = stub( express, "_put" ).with( function( _, _, cb )
|
||||
cb( "test-id", "test-hash" )
|
||||
end )
|
||||
|
||||
express:_send( "test-message", "test-data", {}, stub() )
|
||||
|
||||
expect( putStub ).was.called()
|
||||
expect( putCallback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express._getReceiver
|
||||
{
|
||||
name = "express._getReceiver returns the valid receiver for the given message",
|
||||
func = function( state )
|
||||
state.original_receivers = state.original_receivers or express._receivers
|
||||
express._receivers = {
|
||||
["test-message"] = "test-receiver"
|
||||
}
|
||||
|
||||
local receiver = express:_getReceiver( "test-message" )
|
||||
|
||||
expect( receiver ).to.equal( "test-receiver" )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._receivers = state.original_receivers
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._getReceiver returns the valid receiver for the given message, regardless of casing",
|
||||
func = function( state )
|
||||
state.original_receivers = state.original_receivers or express._receivers
|
||||
express._receivers = {
|
||||
["test-message"] = "test-receiver"
|
||||
}
|
||||
|
||||
local receiver = express:_getReceiver( "TEST-MESSAGE" )
|
||||
|
||||
expect( receiver ).to.equal( "test-receiver" )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._receivers = state.original_receivers
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._getReceiver returns nil if no receiver exists for the given message",
|
||||
func = function( state )
|
||||
state.original_receivers = state.original_receivers or express._receivers
|
||||
express._receivers = {}
|
||||
|
||||
local receiver = express:_getReceiver( "test-message" )
|
||||
|
||||
expect( receiver ).to.beNil()
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._receivers = state.original_receivers
|
||||
end
|
||||
},
|
||||
|
||||
-- express._getPreDlReceiver
|
||||
{
|
||||
name = "express._getPreDlReceiver returns the valid receiver for the given message",
|
||||
func = function( state )
|
||||
state.original_preDlReceivers = state.original_preDlReceivers or express._preDlReceivers
|
||||
express._preDlReceivers = {
|
||||
["test-message"] = "test-receiver"
|
||||
}
|
||||
|
||||
local receiver = express:_getPreDlReceiver( "test-message" )
|
||||
|
||||
expect( receiver ).to.equal( "test-receiver" )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._preDlReceivers = state.original_preDlReceivers
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._getPreDlReceiver returns the valid receiver for the given message, regardless of casing",
|
||||
func = function( state )
|
||||
state.original_preDlReceivers = state.original_preDlReceivers or express._preDlReceivers
|
||||
express._preDlReceivers = {
|
||||
["test-message"] = "test-receiver"
|
||||
}
|
||||
|
||||
local receiver = express:_getPreDlReceiver( "TEST-MESSAGE" )
|
||||
|
||||
expect( receiver ).to.equal( "test-receiver" )
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._preDlReceivers = state.original_preDlReceivers
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._getPreDlReceiver returns nil if no receiver exists for the given message",
|
||||
func = function( state )
|
||||
state.original_preDlReceivers = state.original_preDlReceivers or express._preDlReceivers
|
||||
express._preDlReceivers = {}
|
||||
|
||||
local receiver = express:_getPreDlReceiver( "test-message" )
|
||||
|
||||
expect( receiver ).to.beNil()
|
||||
end,
|
||||
cleanup = function( state )
|
||||
express._preDlReceivers = state.original_preDlReceivers
|
||||
end
|
||||
},
|
||||
|
||||
-- express_domain callback
|
||||
{
|
||||
name = "express_domain callback calls express.Register and express.CheckRevision",
|
||||
func = function()
|
||||
local callbacks = cvars.GetConVarCallbacks( "express_domain" )
|
||||
expect( callbacks ).to.exist()
|
||||
expect( #callbacks ).to.equal( 1 )
|
||||
|
||||
local firstCallback = callbacks[1]
|
||||
local callbackFunc = firstCallback[1]
|
||||
|
||||
expect( callbackFunc ).to.beA( "function" )
|
||||
expect( firstCallback[2] ).to.equal( "domain_check" )
|
||||
|
||||
local registerStub = stub( express, "Register" )
|
||||
local checkRevisionStub = stub( express, "CheckRevision" )
|
||||
|
||||
callbackFunc()
|
||||
|
||||
expect( registerStub ).was.called()
|
||||
expect( checkRevisionStub ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express:_checkResponseCode
|
||||
{
|
||||
name = "express._checkResponseCode succeeds if the response code is 200",
|
||||
func = function()
|
||||
expect( express._checkResponseCode, 200 ).to.succeed()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._checkResponseCode throws an error if the response code is under 200",
|
||||
func = function()
|
||||
expect( express._checkResponseCode, 199 ).to.errWith( "Express: Invalid response code (199)" )
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._checkResponseCode throws an error if the response code is over 300",
|
||||
func = function()
|
||||
expect( express._checkResponseCode, 420 ).to.errWith( "Express: Invalid response code (420)" )
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._checkResponseCode throws an error if the response code is nil",
|
||||
func = function()
|
||||
expect( express._checkResponseCode, nil ).to.errWith( "Express: Invalid response code (nil)" )
|
||||
end
|
||||
},
|
||||
|
||||
-- express._getTimeout
|
||||
{
|
||||
name = "express._getTimeout returns 240 on CLIENT",
|
||||
func = function()
|
||||
_G.CLIENT = true
|
||||
_G.SERVER = false
|
||||
|
||||
local timeout = express:_getTimeout()
|
||||
expect( timeout ).to.equal( 240 )
|
||||
end,
|
||||
cleanup = function()
|
||||
_G.CLIENT = false
|
||||
_G.SERVER = true
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express._getTimeout returns 60 on SERVER",
|
||||
func = function()
|
||||
local timeout = express:_getTimeout()
|
||||
expect( timeout ).to.equal( 60 )
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
433
addons/gm_express/lua/tests/gm_express/sh_init.lua
Normal file
433
addons/gm_express/lua/tests/gm_express/sh_init.lua
Normal file
@@ -0,0 +1,433 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
return {
|
||||
groupName = "Shared Main",
|
||||
|
||||
beforeEach = function()
|
||||
stub( express, "makeAccessURL" ).returns( "https://gmod.express/v1/action/access-token" )
|
||||
end,
|
||||
|
||||
cases = {
|
||||
{
|
||||
name = "Sets up necessary variables and hooks",
|
||||
func = function()
|
||||
expect( express ).to.exist()
|
||||
expect( express ).to.beA( "table" )
|
||||
|
||||
expect( express._receivers ).to.exist()
|
||||
expect( express._receivers ).to.beA( "table" )
|
||||
|
||||
expect( express._protocol ).to.exist()
|
||||
expect( string.StartWith( express._protocol, "http" ) ).to.beTrue()
|
||||
|
||||
expect( express._maxDataSize ).to.exist()
|
||||
expect( express._maxDataSize ).to.beA( "number" )
|
||||
expect( express._maxDataSize ).to.beLessThan( ( 24 * 1024 * 1024 ) + 1 )
|
||||
|
||||
expect( express._jsonHeaders ).to.exist()
|
||||
expect( express._jsonHeaders ).to.beA( "table" )
|
||||
expect( express._jsonHeaders["Content-Type"] ).to.equal( "application/json" )
|
||||
|
||||
expect( express._bytesHeaders ).to.exist()
|
||||
expect( express._bytesHeaders ).to.beA( "table" )
|
||||
expect( express._bytesHeaders["Accept"] ).to.equal( "application/octet-stream" )
|
||||
|
||||
expect( express.domain ).to.exist()
|
||||
expect( tostring( express.domain ) ).to.equal( tostring( GetConVar( "express_domain" ) ) )
|
||||
|
||||
expect( express.domain_cl ).to.exist()
|
||||
expect( tostring( express.domain_cl ) ).to.equal( tostring( GetConVar( "express_domain_cl" ) ) )
|
||||
|
||||
expect( net.Receivers["express"] ).to.exist()
|
||||
expect( net.Receivers["express_proof"] ).to.exist()
|
||||
expect( net.Receivers["express_receivers_made"] ).to.exist()
|
||||
end
|
||||
},
|
||||
|
||||
-- express:_setReceiver
|
||||
{
|
||||
name = "express:_setReceiver adds the given callback to the receivers table and normalizes the name",
|
||||
func = function( state )
|
||||
state.original_receivers = table.Copy( express._receivers )
|
||||
express._receivers = {}
|
||||
|
||||
local callback = stub()
|
||||
express:_setReceiver( "TEST-MESSAGE", callback )
|
||||
|
||||
expect( express._receivers["test-message"] ).to.equal( callback )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._receivers = state.original_receivers
|
||||
end
|
||||
},
|
||||
|
||||
-- express.ClearReceiver
|
||||
{
|
||||
name = "express.ClearReceiver removes the callback for the given message and normalizes the name",
|
||||
func = function( state )
|
||||
state.original_receivers = table.Copy( express._receivers )
|
||||
express._receivers = {}
|
||||
|
||||
express.Receive( "test-message", stub() )
|
||||
|
||||
express.ClearReceiver( "TEST-MESSAGE" )
|
||||
expect( express._receivers["test-message"] ).toNot.exist()
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._receivers = state.original_receivers
|
||||
end
|
||||
},
|
||||
|
||||
-- express.ReceivePreDl
|
||||
{
|
||||
name = "express.ReceivePreDl adds the given callback to the receivers table and normalizes the name",
|
||||
func = function( state )
|
||||
state.original_preDlReceivers = table.Copy( express._preDlReceivers )
|
||||
express._preDlReceivers = {}
|
||||
|
||||
local callback = stub()
|
||||
express.ReceivePreDl( "TEST-MESSAGE", callback )
|
||||
|
||||
expect( express._preDlReceivers["test-message"] ).to.equal( callback )
|
||||
end,
|
||||
|
||||
cleanup = function( state )
|
||||
express._preDlReceivers = state.original_preDlReceivers
|
||||
end
|
||||
},
|
||||
|
||||
-- express.Get
|
||||
{
|
||||
name = "express.Get errors if the request fails",
|
||||
func = function()
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.failed ).to.equal( error )
|
||||
end )
|
||||
|
||||
express:Get( "test-id", stub() )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Get errors if the requests succeeds with a non 200-level status code",
|
||||
func = function()
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 418, "" ).to.errWith( "Express: Invalid response code (418)" )
|
||||
end )
|
||||
|
||||
express:Get( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Get calls the given callback on successful response",
|
||||
func = function()
|
||||
stub( util, "SHA1" ).returns( "test-hash" )
|
||||
stub( util, "Decompress" ).returns( "test-data" )
|
||||
stub( pon, "decode" ).returns( {} )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
options.success( 200, "" )
|
||||
end )
|
||||
|
||||
express:Get( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.GetSize
|
||||
{
|
||||
name = "express.GetSize errors if the request fails",
|
||||
func = function()
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.failed ).to.equal( error )
|
||||
end )
|
||||
|
||||
express:GetSize( "test-id", stub() )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.GetSize errors if the requests succeeds with a non 200-level status code",
|
||||
func = function()
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 418, "" ).to.errWith( "Express: Invalid response code (418)" )
|
||||
end )
|
||||
|
||||
express:GetSize( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.GetSize errors if it cannot read the retrieved data to JSON",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( nil )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 200, "" ).to.errWith( "Invalid JSON" )
|
||||
end )
|
||||
|
||||
express:GetSize( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.GetSize errors if it cannot read the size from the retrieved JSON",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( { notTheSizeLol = 123 } )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 200, "" ).to.errWith( "No size data" )
|
||||
end )
|
||||
|
||||
express:GetSize( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.GetSize calls the given callback on successful response",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( { size = 123 } )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
options.success( 200, "" )
|
||||
end )
|
||||
|
||||
express:GetSize( "test-id", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.Put
|
||||
{
|
||||
name = "express.Put errors if the request fails",
|
||||
func = function()
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.failed ).to.equal( error )
|
||||
end )
|
||||
|
||||
express:Put( "test-data", stub() )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Put errors if the requests succeeds with a non 200-level status code",
|
||||
func = function()
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 418, "" ).to.errWith( "Express: Invalid response code (418)" )
|
||||
end )
|
||||
|
||||
express:Put( "test-data", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Put errors if it cannot read the retrieved data to JSON",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( nil )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 200, "" ).to.errWith( "Invalid JSON" )
|
||||
end )
|
||||
|
||||
express:Put( "test-data", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Put errors if it cannot read the ID from the retrieved JSON",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( { notTheIdLol = "test-id" } )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
expect( options.success, 200, "" ).to.errWith( "No ID returned" )
|
||||
end )
|
||||
|
||||
express:Put( "test-data", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.Put calls the given callback on successful response",
|
||||
func = function()
|
||||
stub( util, "JSONToTable" ).returns( { id = "test-id" } )
|
||||
|
||||
local callback = stub()
|
||||
|
||||
local httpStub = stub( _G, "HTTP" ).with( function( options )
|
||||
options.success( 200, "" )
|
||||
end )
|
||||
|
||||
express:Put( "test-data", callback )
|
||||
|
||||
expect( httpStub ).was.called()
|
||||
expect( callback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.Call
|
||||
{
|
||||
name = "express.Call runs the stored callback for the given message",
|
||||
func = function()
|
||||
local callback = stub()
|
||||
stub( express, "_getReceiver" ).returns( callback )
|
||||
|
||||
express:Call( "test-message" )
|
||||
expect( callback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.CallPreDownload
|
||||
{
|
||||
name = "express.CallPreDownload runs the stored pre-download callback for the given message",
|
||||
func = function()
|
||||
local callback = stub()
|
||||
stub( express, "_getPreDlReceiver" ).returns( callback )
|
||||
|
||||
express:CallPreDownload( "test-message" )
|
||||
expect( callback ).was.called()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.OnMessage
|
||||
{
|
||||
name = "express.OnMessage errors if no callback exists for the given message",
|
||||
func = function()
|
||||
stub( net, "ReadBool" )
|
||||
stub( express, "GetSize" )
|
||||
stub( express, "CallPreDownload" )
|
||||
stub( express, "_get" )
|
||||
stub( express, "_getPreDlReceiver" )
|
||||
stub( net, "ReadString" ).returnsSequence( { "test-message" } )
|
||||
|
||||
stub( express, "_getReceiver" ).returns( nil )
|
||||
expect( express.OnMessage ).to.errWith( "Express: Received a message that has no listener! (test-message)" )
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage calls GetSize if the message has a pre-download receiver",
|
||||
func = function()
|
||||
stub( net, "ReadBool" )
|
||||
stub( express, "CallPreDownload" )
|
||||
stub( express, "_get" )
|
||||
stub( express, "_getReceiver" ).returns( stub() )
|
||||
stub( express, "_getPreDlReceiver" ).returns( stub() )
|
||||
stub( net, "ReadString" ).returnsSequence( { "test-message" } )
|
||||
|
||||
local getSizeStub = stub( express, "_getSize" )
|
||||
|
||||
express:OnMessage()
|
||||
|
||||
expect( getSizeStub ).was.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage does not call GetSize if the message doesn't have a pre-download receiver",
|
||||
func = function()
|
||||
stub( net, "ReadBool" )
|
||||
stub( express, "CallPreDownload" )
|
||||
stub( express, "_get" )
|
||||
stub( express, "_getReceiver" ).returns( stub() )
|
||||
stub( express, "_getPreDlReceiver" )
|
||||
stub( net, "ReadString" ).returnsSequence( { "test-message" } )
|
||||
|
||||
local getSizeStub = stub( express, "GetSize" )
|
||||
|
||||
express:OnMessage()
|
||||
|
||||
expect( getSizeStub ).wasNot.called()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage calls CallPreDownload if the message has a pre-download receiver",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage does not call _get if CallPreDownload returns false",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage calls _get and Call",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage networks the data hash if proof was requested",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnMessage does not network the data hash if proof was not requested",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
|
||||
-- express.OnProof
|
||||
{
|
||||
name = "express.OnProof prefixes the hash with the player's steam ID if a player is given",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnProof uses only the data hash if no player is given",
|
||||
func = function()
|
||||
end
|
||||
},
|
||||
{
|
||||
name = "express.OnProof runs the stored proof callback for the given hash",
|
||||
func = function()
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user