--[[ | 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 Msg = Msg local type = type local pairs = pairs local gmod = gmod local table = table --[[ This module is to help bind the engine's saverestore stuff to Lua. This is so we can save Lua stuff in the save game file. The entities should automatically save most of their table contents to the save file. !!Warning!!: Editing this file may render old saves useless. You can hook into this system like so local function MySaveFunction( save ) saverestore.WriteTable( my_table, save ) end local function MyRestoreFunction( restore ) my_table = saverestore.ReadTable( restore ) end saverestore.AddSaveHook( "HookNameHere", MySaveFunction ) saverestore.AddRestoreHook( "HookNameHere", MyRestoreFunction ) --]] module( "saverestore" ) local TYPE_NONE = 0 local TYPE_FLOAT = 1 -- Treat all numbers as floats (they're all the same to Lua) local TYPE_STRING = 2 local TYPE_ENTITY = 3 local TYPE_VECTOR = 4 local TYPE_TABLE = 5 local TYPE_BOOL = 6 local TYPE_ANGLE = 7 local SaveHooks = {} local RestoreHooks = {} local TableRefs = {} local TableRef = 1 function PreSave() TableRefs = {} TableRef = 1 end function PreRestore() TableRefs = {} TableRef = 1 end --[[--------------------------------------------------------- Name: GetTypeStr Desc: Given a string returns a TYPE_ -----------------------------------------------------------]] local function GetTypeStr( name ) if ( name == "function" ) then return TYPE_NONE end if ( name == "number" ) then return TYPE_FLOAT end if ( name == "string" ) then return TYPE_STRING end if ( name == "Entity" ) then return TYPE_ENTITY end if ( name == "Vehicle" ) then return TYPE_ENTITY end if ( name == "NPC" ) then return TYPE_ENTITY end if ( name == "Player" ) then return TYPE_ENTITY end if ( name == "Weapon" ) then return TYPE_ENTITY end if ( name == "Vector" ) then return TYPE_VECTOR end if ( name == "Angle" ) then return TYPE_ANGLE end if ( name == "table" ) then return TYPE_TABLE end if ( name == "boolean" ) then return TYPE_BOOL end -- These aren't saved if ( name == "ConVar" ) then return TYPE_NONE end if ( name == "PhysObj" ) then return TYPE_NONE end -- Bitch about it incase I've forgot to hook a savable type up Msg( "Can't save unknown type " .. name .. "\n" ) return TYPE_NONE end --[[--------------------------------------------------------- Name: GetType Desc: Given a variable returns a TYPE_ -----------------------------------------------------------]] local function GetType( var ) return GetTypeStr( type(var) ) end --[[--------------------------------------------------------- Name: IsWritable Desc: Returns true if we can save the K/V pair -----------------------------------------------------------]] local function IsWritable( k, v ) local itype = GetType( k ) if ( itype == TYPE_NONE ) then return false end if ( itype == TYPE_STRING && k == "SR_Recursion" ) then return false end local itype = GetType( v ) if ( itype == TYPE_NONE ) then return false end return true end --[[--------------------------------------------------------- Name: WriteVar Desc: Writes a single variable to the save -----------------------------------------------------------]] function WritableKeysInTable( t ) local i = 0 for k, v in pairs( t ) do if ( IsWritable( k, v ) ) then i = i + 1 end end return i end --[[--------------------------------------------------------- Name: WriteVar Desc: Writes a single variable to the save -----------------------------------------------------------]] function WriteVar( var, save ) local itype = GetType( var ) if ( itype == TYPE_NONE ) then return end save:StartBlock( type( var ) ) if ( itype == TYPE_FLOAT ) then save:WriteFloat( var ) elseif ( itype == TYPE_BOOL ) then save:WriteBool( var ) elseif ( itype == TYPE_STRING ) then save:WriteString( var ) elseif ( itype == TYPE_ENTITY ) then save:WriteEntity( var ) elseif ( itype == TYPE_ANGLE ) then save:WriteAngle( var ) elseif ( itype == TYPE_VECTOR ) then save:WriteVector( var ) elseif ( itype == TYPE_TABLE ) then WriteTable( var, save ) else Msg( "Error! Saving unsupported Type: " .. type( var ) .. "\n" ) end save:EndBlock() end --[[--------------------------------------------------------- Name: ReadVar Desc: Reads a single variable -----------------------------------------------------------]] function ReadVar( restore ) local retval = 0 local typename = restore:StartBlock() local itype = GetTypeStr( typename ) if ( itype == TYPE_FLOAT ) then retval = restore:ReadFloat() elseif ( itype == TYPE_BOOL ) then retval = restore:ReadBool() elseif ( itype == TYPE_STRING ) then retval = restore:ReadString() elseif ( itype == TYPE_ENTITY ) then retval = restore:ReadEntity() elseif ( itype == TYPE_ANGLE ) then retval = restore:ReadAngle() elseif ( itype == TYPE_VECTOR ) then retval = restore:ReadVector() elseif ( itype == TYPE_TABLE ) then retval = ReadTable( restore ) else Msg( "Error! Loading unsupported Type: " .. typename .. "\n" ) end restore:EndBlock() return retval end --[[--------------------------------------------------------- Name: WriteTable Desc: Writes a table to the save -----------------------------------------------------------]] function WriteTable( tab, save ) -- Write a blank table (because read will be expecting one) if ( tab == nil ) then save:StartBlock( "Table" ) save:EndBlock() end -- We have already saved this table if ( TableRefs[ tab ] ) then save:StartBlock( "TableRef" ) save:WriteInt( TableRefs[ tab ] ) save:EndBlock() return end TableRefs[ tab ] = TableRef save:StartBlock( "Table" ) local iCount = WritableKeysInTable( tab ) save:WriteInt( TableRef ) TableRef = TableRef + 1 save:WriteInt( iCount ) for k, v in pairs( tab ) do if ( IsWritable( k, v ) ) then WriteVar( k, save ) WriteVar( v, save ) end end save:EndBlock() end --[[--------------------------------------------------------- Name: ReadTable Desc: Assumes a table is waiting to be read, returns a table -----------------------------------------------------------]] function ReadTable( restore ) local name = restore:StartBlock() local tab = {} if ( name == "TableRef" ) then local ref = restore:ReadInt() if ( !TableRefs[ ref ] ) then TableRefs[ ref ] = {} return end tab = TableRefs[ ref ] else local iRef = restore:ReadInt() local iCount = restore:ReadInt() if ( TableRefs[ iRef ] ) then tab = TableRefs[ iRef ] end for i = 0, iCount - 1 do local k = ReadVar( restore ) local v = ReadVar( restore ) tab[ k ] = v end TableRefs[ iRef ] = tab end restore:EndBlock() return tab end --[[--------------------------------------------------------- Name: SaveEntity Desc: Called by the engine for each entity -----------------------------------------------------------]] function SaveEntity( ent, save ) save:StartBlock( "EntityTable" ) WriteTable( ent:GetTable(), save ) save:EndBlock() end --[[--------------------------------------------------------- Name: LoadEntity Desc: Called by the engine for each entity -----------------------------------------------------------]] function LoadEntity( ent, restore ) local EntTable = ent:GetTable() local name = restore:StartBlock() if ( name == "EntityTable" ) then table.Merge( EntTable, ReadTable( restore ) ) end restore:EndBlock() ent:SetTable( EntTable ) end --[[--------------------------------------------------------- Name: AddSaveHook Desc: Adds a hook enabling something to save something.. -----------------------------------------------------------]] function AddSaveHook( name, func ) local h = {} h.Name = name h.Func = func SaveHooks[ name ] = h end --[[--------------------------------------------------------- Name: AddRestoreHook Desc: Adds a hook enabling something to restore something.. -----------------------------------------------------------]] function AddRestoreHook( name, func ) local h = {} h.Name = name h.Func = func RestoreHooks[ name ] = h end --[[--------------------------------------------------------- Name: SaveGlobal Desc: Should save any Lua stuff that isn't entity based. -----------------------------------------------------------]] function SaveGlobal( save ) save:StartBlock( "GameMode" ) WriteTable( gmod.GetGamemode(), save ) save:EndBlock() for k, v in pairs( SaveHooks ) do save:StartBlock( v.Name ) v.Func( save ) save:EndBlock() end end --[[--------------------------------------------------------- Name: LoadGlobal Desc: ... -----------------------------------------------------------]] function LoadGlobal( restore ) local name = restore:StartBlock() if ( name == "GameMode" ) then table.Merge( gmod.GetGamemode(), ReadTable( restore ) ) end restore:EndBlock() while ( name != "EndGlobal" ) do name = restore:StartBlock() local tab = RestoreHooks[ name ] if ( tab ) then tab.Func( restore ) end restore:EndBlock() end end