diff --git a/docs/classes/PhysicsSurfacePropertiesHandle/ParseSurfaceData.md b/docs/classes/PhysicsSurfaceProperties/ParseSurfaceData.md similarity index 74% rename from docs/classes/PhysicsSurfacePropertiesHandle/ParseSurfaceData.md rename to docs/classes/PhysicsSurfaceProperties/ParseSurfaceData.md index aa78cc7693..8f54724043 100644 --- a/docs/classes/PhysicsSurfacePropertiesHandle/ParseSurfaceData.md +++ b/docs/classes/PhysicsSurfaceProperties/ParseSurfaceData.md @@ -8,7 +8,7 @@ tags: - needs-verification - needs-example lua: - library: PhysicsSurfacePropertiesHandle + library: PhysicsSurfaceProperties function: ParseSurfaceData realm: shared description: "Parses surface data" @@ -20,5 +20,5 @@ lua: ---
-PhysicsSurfacePropertiesHandle:ParseSurfaceData – Parses surface data +PhysicsSurfaceProperties:ParseSurfaceData – Parses surface data
diff --git a/docs/general/conventions.md b/docs/general/conventions.md index d3e9052fb2..3f4e661e2a 100644 --- a/docs/general/conventions.md +++ b/docs/general/conventions.md @@ -92,6 +92,7 @@ Additionally, we use the following conventions: - `Vcd` (short for Valve Choreography Data) and other valve formats (e.g. `Vmt`, `Vtf`, `Vpk`, `Mdl`, etc.) - `Vgui` + - `Vs` for versus - Abbreviations should be treated as words when capitalizing: diff --git a/docs/hooks/shared/EntityKeyValue.md b/docs/hooks/server/EntityKeyValue.md similarity index 88% rename from docs/hooks/shared/EntityKeyValue.md rename to docs/hooks/server/EntityKeyValue.md index 97906fd8b0..9487410f18 100644 --- a/docs/hooks/shared/EntityKeyValue.md +++ b/docs/hooks/server/EntityKeyValue.md @@ -1,18 +1,18 @@ --- template: lua-hook.html title: EntityKeyValue -icon: lua-shared +icon: lua-server tags: - lua - - shared + - server - needs-verification - needs-example lua: function: EntityKeyValue - realm: shared + realm: server description: "" arguments: - - name: "this" + - name: "pNode" description: "" type: Entity - name: "szKeyName" diff --git a/docs/hooks/shared/CalcViewModelView.md b/docs/hooks/shared/CalcViewModelView.md new file mode 100644 index 0000000000..0f6333d30b --- /dev/null +++ b/docs/hooks/shared/CalcViewModelView.md @@ -0,0 +1,42 @@ +--- +template: lua-hook.html +title: CalcViewModelView +icon: lua-shared +tags: + - lua + - shared + - needs-verification + - needs-example +lua: + function: CalcViewModelView + realm: shared + description: "Allows overriding the view model's position and angles." + arguments: + - name: "weapon" + description: "" + type: Entity + - name: "viewModel" + description: "" + type: Entity + - name: "eyePosition" + description: "" + type: Vector + - name: "eyeAngles" + description: "" + type: Angle + - name: "vmorigin" + description: "" + type: Vector + - name: "vmangles" + description: "" + type: Angle + returns: + - type: Vector + description: "replacement origin" + - type: Angle + description: "replacement angles" +--- + +
+hook CalcViewModelView – "Allows overriding the view model's position and angles." +
diff --git a/docs/hooks/shared/ShouldCollide.md b/docs/hooks/shared/ShouldCollide.md index 51c5c924ab..f757c0f6ad 100644 --- a/docs/hooks/shared/ShouldCollide.md +++ b/docs/hooks/shared/ShouldCollide.md @@ -10,19 +10,19 @@ tags: lua: function: ShouldCollide realm: shared - description: "" + description: "Called when two entities are about to collide. Return false to prevent the collision." arguments: - - name: "collisionGroup0" - description: "" - type: number - - name: "collisionGroup1" - description: "" - type: number + - name: "entity1" + description: "The entity colliding" + type: Entity + - name: "entity2" + description: "The entity colliding with entity1" + type: Entity returns: - type: unknown description: "" ---
-hook ShouldCollide – "" +hook ShouldCollide – "Called when two entities are about to collide. Return false to prevent the collision."
diff --git a/docs/libraries/GameEvents/Listen.md b/docs/libraries/GameEvents/Listen.md new file mode 100644 index 0000000000..a034ff12e8 --- /dev/null +++ b/docs/libraries/GameEvents/Listen.md @@ -0,0 +1,25 @@ +--- +template: lua-library-function.html +title: Listen +icon: lua-shared +tags: + - lua + - shared + - needs-verification + - needs-example +lua: + library: GameEvents + function: Listen + realm: shared + description: "Call a hook for this game event name, when the event occurs." + + arguments: + - name: "eventName" + type: string + returns: + +--- + +
+GameEvents.Listen – Call a hook for this game event name, when the event occurs. +
diff --git a/docs/libraries/Predictions/IsFirstTimePredicted.md b/docs/libraries/Predictions/IsFirstTimePredicted.client.md similarity index 100% rename from docs/libraries/Predictions/IsFirstTimePredicted.md rename to docs/libraries/Predictions/IsFirstTimePredicted.client.md diff --git a/docs/libraries/Predictions/IsFirstTimePredicted.server.md b/docs/libraries/Predictions/IsFirstTimePredicted.server.md new file mode 100644 index 0000000000..f2d98aa05e --- /dev/null +++ b/docs/libraries/Predictions/IsFirstTimePredicted.server.md @@ -0,0 +1,24 @@ +--- +template: lua-library-function.html +title: IsFirstTimePredicted +icon: lua-server +tags: + - lua + - server + - needs-verification + - needs-example +lua: + library: Predictions + function: IsFirstTimePredicted + realm: server + description: "Always returns true on the server. TODO: what should it do?" + + + returns: + - type: boolean + description: "Always true on the server." +--- + +
+Predictions.IsFirstTimePredicted – Always returns true on the server. TODO: what should it do? +
diff --git a/docs/libraries/Sounds/Add.md b/docs/libraries/Sounds/Add.md new file mode 100644 index 0000000000..b86a28ff5d --- /dev/null +++ b/docs/libraries/Sounds/Add.md @@ -0,0 +1,25 @@ +--- +template: lua-library-function.html +title: Add +icon: lua-shared +tags: + - lua + - shared + - needs-verification + - needs-example +lua: + library: Sounds + function: Add + realm: shared + description: "Creates a sound script." + + arguments: + - name: "soundData" + type: table + returns: + +--- + +
+Sounds.Add – Creates a sound script. +
diff --git a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_enumerations.lua b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_enumerations.lua index fb9710e8f4..b8bdfd89b3 100644 --- a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_enumerations.lua +++ b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_enumerations.lua @@ -94,8 +94,9 @@ local enumsToMergeWithKey = { MOVE_TYPE = "MOVETYPE", OBSERVER_MODE = "OBS_MODE", SOLID = "SOLID", + SOUND_CHANNEL = "CHAN", SURFACE = "SURF", - + DAMAGE_TYPE = {"DMG", stripUnderscores}, HIT_GROUP = {"HITGROUP", stripUnderscores}, MATERIAL_TYPE = {"MAT", stripUnderscores}, diff --git a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua index ba61ab1136..1bb23fbf8d 100644 --- a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua +++ b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua @@ -172,6 +172,9 @@ system = Systems vgui = Panels VMatrix = Matrix concommand = ConsoleCommands +physenv = PhysicsEnvironments +gameevent = GameEvents +sound = Sounds Angle = Angles.Create Color = Colors.Create @@ -199,6 +202,7 @@ GetHostName = Engines.GetServerName PrecacheParticleSystem = ParticleSystems.Precache GetPredictionPlayer = Predictions.GetPredictionPlayer +IsFirstTimePredicted = Predictions.IsFirstTimePredicted LerpVector = Vectors.Lerp RecipientFilter = RecipientFilters.Create @@ -295,6 +299,13 @@ for _, languageFile in ipairs(languageFiles) do end end +function physenv.AddSurfaceData(surfaceDataKeyValuesString) + -- ParseSurfaceData expects a unique file name, so we generate one + local fakeFileName = "physenv.AddSurfaceData" .. tostring(os.time()) .. ".txt" + + return PhysicsSurfaceProperties.ParseSurfaceData(fakeFileName, surfaceDataKeyValuesString) +end + umsg = require("UserMessages") umsg.Angle = umsg.WriteAngles umsg.Bool = umsg.WriteBool diff --git a/game/experiment/scripts/game_sounds_manifest.txt b/game/experiment/scripts/game_sounds_manifest.txt new file mode 100644 index 0000000000..5585425fad --- /dev/null +++ b/game/experiment/scripts/game_sounds_manifest.txt @@ -0,0 +1,4 @@ +game_sounds_manifest +{ + "precache_file" "scripts/sounds/lua_procedural.txt" +} diff --git a/game/experiment/scripts/lua/includes/extensions/entity_networked_variables.lua b/game/experiment/scripts/lua/includes/extensions/entity_networked_variables.lua index f40ee2239e..86f95135c8 100644 --- a/game/experiment/scripts/lua/includes/extensions/entity_networked_variables.lua +++ b/game/experiment/scripts/lua/includes/extensions/entity_networked_variables.lua @@ -20,9 +20,9 @@ local Networks = require("networks") local Hooks = require("hooks") local debugPrint = function(...) - if (not EntityNetworkedVariablesDebug) then - return - end + if (not EntityNetworkedVariablesDebug) then + return + end local prefix = CLIENT and "Client]" or "Server]" print("[EntityNetworking ", prefix, ...) @@ -32,49 +32,54 @@ end --- @param key string --- @param value any function ENTITY_META:SetNetworkedVariable(key, value) - if (not self.__networkedVariables) then - self.__networkedVariables = {} - end + if (not self.__networkedVariables) then + self.__networkedVariables = {} + end - self.__networkedVariables[key] = value + self.__networkedVariables[key] = value - if (not SERVER) then - -- TODO: Okay so the server should be updating the client sometimes, + if (not SERVER) then + -- TODO: Okay so the server should be updating the client sometimes, -- otherwise clientside SetNetworkedVariables will differ from the server for too long. - return - end + return + end debugPrint("Setting networked variable", self, key, value) - Networks.Start("EntityNetworkedVariable") - Networks.WriteEntity(self) - Networks.WriteString(key) - Networks.WriteType(value) - Networks.BroadcastPVS(self) + Networks.Start("EntityNetworkedVariable") + Networks.WriteEntity(self) + Networks.WriteString(key) + Networks.WriteType(value) + Networks.BroadcastPVS(self) end --- Gets a networked variable from the entity --- @param key string +--- @param defaultValue any --- @return any -function ENTITY_META:GetNetworkedVariable(key) - if (not self.__networkedVariables) then - return nil - end +function ENTITY_META:GetNetworkedVariable(key, defaultValue) + if (not self.__networkedVariables) then + return defaultValue + end - return self.__networkedVariables[key] + if (self.__networkedVariables[key] == nil) then + return defaultValue + end + + return self.__networkedVariables[key] end --- Sets up a callback for when a networked variable changes --- @param key string --- @param callback fun(entity: Entity, key: string, oldValue: any, newValue: any) function ENTITY_META:SetNetworkedVariableCallback(key, callback) - if (not self.__networkedVarCallbacks) then - self.__networkedVarCallbacks = {} - end + if (not self.__networkedVarCallbacks) then + self.__networkedVarCallbacks = {} + end debugPrint("Setting networked variable callback", key) - self.__networkedVarCallbacks[key] = callback + self.__networkedVarCallbacks[key] = callback end --- Calls all callbacks for the provided key on the entity @@ -82,15 +87,15 @@ end --- @param oldValue any --- @param newValue any function ENTITY_META:CallNetworkedVariableCallbacks(key, oldValue, newValue) - if (not self.__networkedVarCallbacks) then - return - end + if (not self.__networkedVarCallbacks) then + return + end - debugPrint("Calling networked variable callbacks", key) + debugPrint("Calling networked variable callbacks", key) - for _, callback in pairs(self.__networkedVarCallbacks) do - callback(self, key, oldValue, newValue) - end + for _, callback in pairs(self.__networkedVarCallbacks) do + callback(self, key, oldValue, newValue) + end end if (SERVER) then @@ -124,15 +129,15 @@ local dataTypes = { } for _, dataType in ipairs(dataTypes) do - ENTITY_META["SetNetworked" .. dataType] = ENTITY_META.SetNetworkedVariable - ENTITY_META["SetNetworked2" .. dataType] = ENTITY_META.SetNetworkedVariable - ENTITY_META["SetNW" .. dataType] = ENTITY_META.SetNetworkedVariable - ENTITY_META["SetNW2" .. dataType] = ENTITY_META.SetNetworkedVariable - - ENTITY_META["GetNetworked" .. dataType] = ENTITY_META.GetNetworkedVariable - ENTITY_META["GetNetworked2" .. dataType] = ENTITY_META.GetNetworkedVariable - ENTITY_META["GetNW" .. dataType] = ENTITY_META.GetNetworkedVariable - ENTITY_META["GetNW2" .. dataType] = ENTITY_META.GetNetworkedVariable + ENTITY_META["SetNetworked" .. dataType] = ENTITY_META.SetNetworkedVariable + ENTITY_META["SetNetworked2" .. dataType] = ENTITY_META.SetNetworkedVariable + ENTITY_META["SetNW" .. dataType] = ENTITY_META.SetNetworkedVariable + ENTITY_META["SetNW2" .. dataType] = ENTITY_META.SetNetworkedVariable + + ENTITY_META["GetNetworked" .. dataType] = ENTITY_META.GetNetworkedVariable + ENTITY_META["GetNetworked2" .. dataType] = ENTITY_META.GetNetworkedVariable + ENTITY_META["GetNW" .. dataType] = ENTITY_META.GetNetworkedVariable + ENTITY_META["GetNW2" .. dataType] = ENTITY_META.GetNetworkedVariable end ENTITY_META.SetNetworkedVarProxy = ENTITY_META.SetNetworkedVariableCallback diff --git a/game/experiment/scripts/lua/includes/modules/scripted_entities.lua b/game/experiment/scripts/lua/includes/modules/scripted_entities.lua index 14587c0b23..83cf5bc8df 100644 --- a/game/experiment/scripts/lua/includes/modules/scripted_entities.lua +++ b/game/experiment/scripts/lua/includes/modules/scripted_entities.lua @@ -3,6 +3,8 @@ _BASE_ENTITY_CLASS = "prop_scripted" local MODULE = {} MODULE.registeredEntities = MODULE.registeredEntities or {} +-- TODO: This file has a lot of similar code to scripted_weapons.lua, reduce duplication + --- Returns an entity table --- @param className string Name of the entity --- @return table @@ -28,12 +30,41 @@ function MODULE.Get(className) return foundEntity end ---- Returns all registered entities +--- Returns all registered entities. +--- Be careful, this returns a reference to the actual table. --- @return table -function MODULE.GetList() +function MODULE.GetStored() return MODULE.registeredEntities end +--- Returns a list of all registered entities. +--- @return table +function MODULE.GetList() + return table.Copy(MODULE.registeredEntities) +end + +--- Checks if the entity class is based on another entity class +--- @param className string Name of the entity +--- @param potentialBaseClassName string Name of the potential base entity +--- @return boolean +function MODULE.IsBasedOn(className, potentialBaseClassName) + local entityTable = MODULE.Get(className) + + if (not entityTable) then + return false + end + + if (entityTable.Base == potentialBaseClassName) then + return true + end + + if (entityTable.Base ~= className) then + return MODULE.IsBasedOn(entityTable.Base, potentialBaseClassName) + end + + return false +end + --- Registers an entity --- @param entityTable table Entity table --- @param className string Name of the entity diff --git a/game/experiment/scripts/lua/includes/modules/scripted_weapons.lua b/game/experiment/scripts/lua/includes/modules/scripted_weapons.lua index 544a98a1cc..88dabbb10f 100644 --- a/game/experiment/scripts/lua/includes/modules/scripted_weapons.lua +++ b/game/experiment/scripts/lua/includes/modules/scripted_weapons.lua @@ -42,6 +42,28 @@ function MODULE.GetList() return table.Copy(MODULE.registeredWeapons) end +--- Checks if the entity class is based on another entity class +--- @param className string Name of the entity +--- @param potentialBaseClassName string Name of the potential base entity +--- @return boolean +function MODULE.IsBasedOn(className, potentialBaseClassName) + local entityTable = MODULE.Get(className) + + if (not entityTable) then + return false + end + + if (entityTable.Base == potentialBaseClassName) then + return true + end + + if (entityTable.Base ~= className) then + return MODULE.IsBasedOn(entityTable.Base, potentialBaseClassName) + end + + return false +end + --- Registers a weapon. --- @param weaponTable table --- @param className string diff --git a/game/experiment/scripts/sounds/lua_procedural.txt b/game/experiment/scripts/sounds/lua_procedural.txt new file mode 100644 index 0000000000..98b31f111d --- /dev/null +++ b/game/experiment/scripts/sounds/lua_procedural.txt @@ -0,0 +1,2 @@ +// This file exists purely to allow sounds to be created from lua using Sounds.Add +// That function will reference this file as the soundscript to use for the sound \ No newline at end of file diff --git a/src/game/client/cdll_client_int.cpp b/src/game/client/cdll_client_int.cpp index 16cca65049..a58df316a7 100644 --- a/src/game/client/cdll_client_int.cpp +++ b/src/game/client/cdll_client_int.cpp @@ -1848,14 +1848,14 @@ void CHLClient::LevelInitPreEntity( char const *pMapName ) //----------------------------------------------------------------------------- void CHLClient::LevelInitPostEntity() { + IGameSystem::LevelInitPostEntityAllSystems(); + C_PhysPropClientside::RecreateAll(); + internalCenterPrint->Clear(); + #ifdef LUA_SDK LUA_CALL_HOOK_BEGIN( "LevelInitPostEntity", "After loading entities." ); LUA_CALL_HOOK_END( 0, 0 ); #endif - - IGameSystem::LevelInitPostEntityAllSystems(); - C_PhysPropClientside::RecreateAll(); - internalCenterPrint->Clear(); } //----------------------------------------------------------------------------- diff --git a/src/game/client/client_base.vpc b/src/game/client/client_base.vpc index 22f20ff65d..f8c9337833 100644 --- a/src/game/client/client_base.vpc +++ b/src/game/client/client_base.vpc @@ -579,6 +579,7 @@ $Project $File "$SRCDIR\public\lnetworkstringtabledefs.cpp" $File "$SRCDIR\public\vphysics\lperformance.cpp" $File "lc_prediction.cpp" + $File "$SRCDIR\game\shared\lprediction_shared.cpp" $File "$SRCDIR\public\vstdlib\lrandom.cpp" $File "$SRCDIR\game\shared\lshareddefs.cpp" $File "$SRCDIR\game\shared\lsrcinit.cpp" @@ -606,6 +607,10 @@ $Project $File "$SRCDIR\public\vgui\LVGUI.cpp" $File "$SRCDIR\public\mathlib\lvmatrix.cpp" $File "$SRCDIR\public\lvphysics_interface.cpp" + $File "$SRCDIR\public\lgameevents.cpp" + $File "$SRCDIR\public\lgameevents.h" + $File "$SRCDIR\public\lsounds.cpp" + $File "$SRCDIR\public\lsounds.h" $File "$SRCDIR\public\XUnzip.cpp" $File "$SRCDIR\public\zip_utils.cpp" } diff --git a/src/game/client/lc_prediction.cpp b/src/game/client/lc_prediction.cpp index a5e6deb66e..0b4aadf0c8 100644 --- a/src/game/client/lc_prediction.cpp +++ b/src/game/client/lc_prediction.cpp @@ -9,21 +9,6 @@ LUA_REGISTRATION_INIT( Predictions ) -// TODO: These two functions also exist in lprediction. Make a shared place for them. -LUA_BINDING_BEGIN( Predictions, GetPredictionPlayer, "library", "Get prediction player." ) -{ - CBaseEntity::PushLuaInstanceSafe( L, CBaseEntity::GetPredictionPlayer() ); - return 1; -} -LUA_BINDING_END( "Entity", "The prediction player." ) - -LUA_BINDING_BEGIN( Predictions, GetPredictionRandomSeed, "library", "Get prediction random seed." ) -{ - lua_pushinteger( L, CBaseEntity::GetPredictionRandomSeed() ); - return 1; -} -LUA_BINDING_END( "integer", "The prediction random seed." ) - LUA_BINDING_BEGIN( Predictions, GetIdealPitch, "library", "Get the ideal pitch." ) { lua_pushnumber( L, prediction->GetIdealPitch() ); @@ -148,7 +133,7 @@ LUA_BINDING_END() /* ** Open prediction library */ -LUALIB_API int luaopen_prediction( lua_State *L ) +LUALIB_API int luaopen_Predictions( lua_State *L ) { LUA_REGISTRATION_COMMIT_LIBRARY( Predictions ); return 1; diff --git a/src/game/server/lprediction.cpp b/src/game/server/lprediction.cpp index be6ff1ae34..5d76bfbc4d 100644 --- a/src/game/server/lprediction.cpp +++ b/src/game/server/lprediction.cpp @@ -7,25 +7,18 @@ LUA_REGISTRATION_INIT( Predictions ) -// TODO: These two functions also exist in lc_prediction. Make a shared place for them. -LUA_BINDING_BEGIN( Predictions, GetPredictionPlayer, "library", "Get prediction player." ) +LUA_BINDING_BEGIN( Predictions, IsFirstTimePredicted, "library", "Always returns true on the server. TODO: what should it do?" ) { - CBaseEntity::PushLuaInstanceSafe( L, CBaseEntity::GetPredictionPlayer() ); + // TODO: What should this return on the server? + lua_pushboolean( L, true ); return 1; } -LUA_BINDING_END( "Entity", "The prediction player." ) - -LUA_BINDING_BEGIN( Predictions, GetPredictionRandomSeed, "library", "Get prediction random seed." ) -{ - lua_pushinteger( L, CBaseEntity::GetPredictionRandomSeed() ); - return 1; -} -LUA_BINDING_END( "integer", "The prediction random seed." ) +LUA_BINDING_END( "boolean", "Always true on the server." ) /* ** Open prediction library */ -LUALIB_API int luaopen_prediction( lua_State *L ) +LUALIB_API int luaopen_Predictions( lua_State *L ) { LUA_REGISTRATION_COMMIT_LIBRARY( Predictions ); return 1; diff --git a/src/game/server/physics.cpp b/src/game/server/physics.cpp index 6e5b4d033e..82eed1c8d8 100644 --- a/src/game/server/physics.cpp +++ b/src/game/server/physics.cpp @@ -470,6 +470,15 @@ int CCollisionEvent::ShouldCollide_2( IPhysicsObject *pObj0, IPhysicsObject *pOb if ( !pEntity0 || !pEntity1 ) return 1; +#if defined( LUA_SDK ) + LUA_CALL_HOOK_BEGIN( "ShouldCollide", "Called when two entities are about to collide. Return false to prevent the collision." ); + CBaseEntity::PushLuaInstanceSafe( L, pEntity0 ); // doc: entity1 (The entity colliding) + CBaseEntity::PushLuaInstanceSafe( L, pEntity1 ); // doc: entity2 (The entity colliding with entity1) + LUA_CALL_HOOK_END( 2, 1 ); + + LUA_RETURN_BOOLEAN(); +#endif + unsigned short gameFlags0 = pObj0->GetGameFlags(); unsigned short gameFlags1 = pObj1->GetGameFlags(); diff --git a/src/game/server/server_base.vpc b/src/game/server/server_base.vpc index 6e92c26067..ebc7d8be97 100644 --- a/src/game/server/server_base.vpc +++ b/src/game/server/server_base.vpc @@ -741,6 +741,7 @@ $Project $File "$SRCDIR\public\vphysics\lperformance.cpp" $File "$SRCDIR\public\vphysics\lperformance.h" $File "lprediction.cpp" + $File "$SRCDIR\game\shared\lprediction_shared.cpp" $File "lplayer.cpp" $File "$SRCDIR\game\shared\lrecipientfilter.cpp" $File "$SRCDIR\game\shared\lrecipientfilter.h" @@ -773,6 +774,10 @@ $Project $File "$SRCDIR\public\mathlib\lvmatrix.cpp" $File "$SRCDIR\public\mathlib\lvmatrix.h" $File "$SRCDIR\public\lvphysics_interface.cpp" + $File "$SRCDIR\public\lgameevents.cpp" + $File "$SRCDIR\public\lgameevents.h" + $File "$SRCDIR\public\lsounds.cpp" + $File "$SRCDIR\public\lsounds.h" $File "$SRCDIR\public\XUnzip.cpp" $File "$SRCDIR\public\zip_utils.cpp" } diff --git a/src/game/shared/experiment/experiment_gamerules.cpp b/src/game/shared/experiment/experiment_gamerules.cpp index 3668272fd8..c22e849141 100644 --- a/src/game/shared/experiment/experiment_gamerules.cpp +++ b/src/game/shared/experiment/experiment_gamerules.cpp @@ -1386,15 +1386,6 @@ void CExperimentRules::Precache( void ) bool CExperimentRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) { -#if defined( LUA_SDK ) - LUA_CALL_HOOK_BEGIN( "ShouldCollide" ); - lua_pushinteger( L, collisionGroup0 ); - lua_pushinteger( L, collisionGroup1 ); - LUA_CALL_HOOK_END( 2, 1 ); - - LUA_RETURN_BOOLEAN(); -#endif - if ( collisionGroup0 > collisionGroup1 ) { // swap so that lowest is always first diff --git a/src/game/shared/lprediction_shared.cpp b/src/game/shared/lprediction_shared.cpp new file mode 100644 index 0000000000..fee1c4573c --- /dev/null +++ b/src/game/shared/lprediction_shared.cpp @@ -0,0 +1,36 @@ +#include "cbase.h" +#ifdef CLIENT_DLL +#include "prediction.h" +#endif +#include "luamanager.h" +#include "luasrclib.h" +#include "mathlib/lvector.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LUA_REGISTRATION_INIT( Predictions ) + +// TODO: These two functions also exist in lc_prediction. Make a shared place for them. +LUA_BINDING_BEGIN( Predictions, GetPredictionPlayer, "library", "Get prediction player." ) +{ + CBaseEntity::PushLuaInstanceSafe( L, CBaseEntity::GetPredictionPlayer() ); + return 1; +} +LUA_BINDING_END( "Entity", "The prediction player." ) + +LUA_BINDING_BEGIN( Predictions, GetPredictionRandomSeed, "library", "Get prediction random seed." ) +{ + lua_pushinteger( L, CBaseEntity::GetPredictionRandomSeed() ); + return 1; +} +LUA_BINDING_END( "integer", "The prediction random seed." ) + +/* +** Open prediction library +*/ +LUALIB_API int luaopen_PredictionShared( lua_State *L ) +{ + LUA_REGISTRATION_COMMIT_LIBRARY( Predictions ); + return 1; +} diff --git a/src/game/shared/lsrcinit.cpp b/src/game/shared/lsrcinit.cpp index 22aa7bb965..5e4df7dc9d 100644 --- a/src/game/shared/lsrcinit.cpp +++ b/src/game/shared/lsrcinit.cpp @@ -55,6 +55,7 @@ static const luaL_RegForState luasrclibs[] = { { LUA_FILESYSTEMLIBNAME, luaopen_Files, REALM_SHARED | REALM_GAMEUI }, { LUA_FLEDICTLIBNAME, luaopen_FL_EDICT, REALM_SHARED | REALM_GAMEUI }, { LUA_GAMETRACEMETANAME, luaopen_CGameTrace, REALM_SHARED }, + { LUA_GAMEEVENTSLIBNAME, luaopen_GameEvents, REALM_SHARED }, { LUA_GESTURESLOTLIBNAME, luaopen_GESTURE_SLOT, REALM_SHARED | REALM_GAMEUI }, { LUA_INETWORKSTRINGTABLELIBNAME, luaopen_INetworkStringTable, REALM_SHARED }, { LUA_INENUMLIBNAME, luaopen_IN, REALM_SHARED | REALM_GAMEUI }, @@ -77,12 +78,14 @@ static const luaL_RegForState luasrclibs[] = { { LUA_PHYSICSOBJECTMETANAME, luaopen_IPhysicsObject, REALM_SHARED }, { LUA_PHYSICSSURFACEPROPSMETANAME, luaopen_IPhysicsSurfaceProps, REALM_SHARED }, { LUA_PREDICTIONSYSTEMLIBNAME, luaopen_PredictionSystems, REALM_SHARED }, + { LUA_PREDICTIONSLIBNAME, luaopen_PredictionShared, REALM_SHARED }, { LUA_QANGLEMETANAME, luaopen_QAngle, REALM_SHARED | REALM_GAMEUI }, { LUA_RANDOMLIBNAME, luaopen_random, REALM_SHARED | REALM_GAMEUI }, { LUA_RECIPIENTFILTERMETANAME, luaopen_CRecipientFilter, REALM_SHARED }, { LUA_RENDERLIBNAME, luaopen_render, REALM_SHARED | REALM_GAMEUI }, { LUA_SERIALIZERSLIBNAME, luaopen_Serializers, REALM_SHARED | REALM_GAMEUI }, { LUA_SOLIDFLAGLIBNAME, luaopen_SOLIDFLAG, REALM_SHARED | REALM_GAMEUI }, + { LUA_SOUNDSLIBNAME, luaopen_Sounds, REALM_SHARED }, { LUA_SOLIDLIBNAME, luaopen_SOLID, REALM_SHARED | REALM_GAMEUI }, { LUA_STEAMAPICONTEXTLIBNAME, luaopen_SteamApiContexts, REALM_SHARED | REALM_GAMEUI }, { LUA_STEAMFRIENDSMETANAME, luaopen_ISteamFriends, REALM_SHARED | REALM_GAMEUI }, @@ -103,7 +106,7 @@ static const luaL_RegForState luasrclibs[] = { { LUA_SERVERENUMNAME, luaopen_ServerEnumerations, REALM_SERVER }, { LUA_NETCHANNELINFOMETANAME, luaopen_INetChannelInfo, REALM_SERVER }, { LUA_RESOURCESLIBNAME, luaopen_resources, REALM_SERVER }, - { LUA_PREDICTIONLIBNAME, luaopen_prediction, REALM_SERVER }, + { LUA_PREDICTIONSLIBNAME, luaopen_Predictions, REALM_SERVER }, #endif #ifdef CLIENT_DLL @@ -123,7 +126,7 @@ static const luaL_RegForState luasrclibs[] = { { LUA_FONTLIBNAME, luaopen_HFont, REALM_CLIENT }, { LUA_INPUTLIBNAME, luaopen_input, REALM_CLIENT | REALM_GAMEUI }, { LUA_ISCHEMELIBNAME, luaopen_IScheme, REALM_CLIENT | REALM_GAMEUI }, - { LUA_PREDICTIONLIBNAME, luaopen_prediction, REALM_CLIENT }, + { LUA_PREDICTIONSLIBNAME, luaopen_Predictions, REALM_CLIENT }, { LUA_SCHEMELIBNAME, luaopen_Schemes, REALM_CLIENT | REALM_GAMEUI }, { LUA_SURFACELIBNAME, luaopen_Surfaces, REALM_CLIENT | REALM_GAMEUI }, { LUA_VGUILIBNAME, luaopen_vgui, REALM_CLIENT | REALM_GAMEUI }, diff --git a/src/game/shared/luamanager.cpp b/src/game/shared/luamanager.cpp index da1438adf0..5bf0d2e94a 100644 --- a/src/game/shared/luamanager.cpp +++ b/src/game/shared/luamanager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -42,6 +43,7 @@ extern "C" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#include static FileHandle_t g_LuaLogFileHandle = FILESYSTEM_INVALID_HANDLE; @@ -85,8 +87,6 @@ static void LuaLogToFile( const char *format, ... ) #define SHOW_LUA_MESSAGE( format, ... ) \ Msg( "\n[Lua] " format "\n", ##__VA_ARGS__ ); -static char contentSearchPath[MAX_PATH]; - static void tag_error( lua_State *L, int narg, int tag ) { luaL_typerror( L, narg, lua_typename( L, tag ) ); @@ -794,6 +794,8 @@ void luasrc_init( void ) RegisterLuaUserMessages(); + InitializeLuaGameEventHandler( L ); + Msg( "Lua initialized (" LUA_VERSION ")\n" ); SetupMountedAddons( L ); @@ -849,15 +851,11 @@ void luasrc_ui_disable( void ) void luasrc_shutdown( void ) { #ifdef CLIENT_DLL - Msg( "Lua shutdown - Client\n" ); - delete g_pClientLuaPanel; g_pClientLuaPanel = NULL; delete g_pClientLuaPanelHUD; g_pClientLuaPanelHUD = NULL; -#else - Msg( "Lua shutdown - Server\n" ); #endif if ( g_LuaLogFileHandle != FILESYSTEM_INVALID_HANDLE ) @@ -872,11 +870,35 @@ void luasrc_shutdown( void ) LUA_CALL_HOOK_BEGIN( "ShutDown" ); LUA_CALL_HOOK_END( 0, 0 ); +#ifdef CLIENT_DLL + Msg( "Lua shutdown - Client\n" ); +#else + Msg( "Lua shutdown - Server\n" ); +#endif + + ShutdownLuaGameEventHandler( L ); + luasrc_UnloadLoadedModules( L ); g_bLuaInitialized = false; - filesystem->RemoveSearchPath( contentSearchPath, CONTENT_SEARCH_PATH ); + // Remove the content searchpath for any load gamemode + lua_getglobal( L, "GAMEMODE" ); + + if ( lua_istable( L, -1 ) ) + { + lua_getfield( L, -1, "Folder" ); + const char *gamemodeFolder = lua_tostring( L, -1 ); + lua_pop( L, 1 ); // Remove the folder name + + char contentSearchPath[MAX_PATH]; + Q_strcpy( contentSearchPath, gamemodeFolder ); + Q_strncat( contentSearchPath, "\\content", sizeof( contentSearchPath ) ); + + RemoveRootSearchPaths( contentSearchPath, L ); + } + + lua_pop( L, 1 ); // Remove the GAMEMODE/non-table value ResetConCommandDatabase(); @@ -2057,18 +2079,17 @@ bool luasrc_SetGamemode( const char *gamemodeName ) lua_pushstring( L, gamemodeName ); // Push gamemode name luasrc_pcall( L, 1, 0 ); // Call gamemodes.InternalSetActiveName(gamemode) -#ifdef CLIENT_DLL - const char *gamePath = engine->GetGameDirectory(); -#else - char gamePath[MAX_PATH]; - engine->GetGameDir( gamePath, MAX_PATH ); -#endif - Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, LUA_PATH_GAMEMODES "\\", sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, gamemodeName, sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, "\\content", sizeof( contentSearchPath ) ); + // Get the folder name of the gamemode + lua_getglobal( L, "GAMEMODE" ); + lua_getfield( L, -1, "Folder" ); + const char *gamemodeFolder = lua_tostring( L, -1 ); + lua_pop( L, 2 ); // Remove the folder name and GAMEMODE table - filesystem->AddSearchPath( contentSearchPath, "GAME" ); + char contentSearchPath[MAX_PATH]; + Q_strcpy( contentSearchPath, gamemodeFolder ); + Q_strncat( contentSearchPath, "content", sizeof( contentSearchPath ) ); + + SetupRootSearchPaths( contentSearchPath, L ); char loadPath[MAX_PATH]; Q_snprintf( loadPath, sizeof( loadPath ), "%s\\", contentSearchPath ); @@ -2359,173 +2380,173 @@ CON_COMMAND( lua_dumpstack, "Prints the Lua stack" ) #endif #endif -#ifdef CLIENT_DLL -#define LUA_GMOD_REPOSITORY_DOWNLOAD "https://github.com/Facepunch/garrysmod/archive/%s.zip" -#define LUA_GMOD_REPOSITORY_HASH "430fc8a2cf4c25873766b1d1a0df1cb94c68d5b7" - -class GmodCompatDownloader -{ - public: - void OnCompleted( HTTPRequestCompleted_t *arg, bool bFailed ) - { - ISteamHTTP *pHTTP = steamapicontext->SteamHTTP(); - - if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK ) - { - Warning( "Failed to download the GMod repository! Status code: %d\n", arg->m_eStatusCode ); - pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); - delete this; - return; - } - - if ( !arg->m_bRequestSuccessful ) - { - Warning( "Failed to download the GMod repository! Request unsuccessful\n" ); - pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); - delete this; - return; - } - - if ( bFailed ) - { - Warning( "Failed to download the GMod repository! Request failed\n" ); - pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); - delete this; - return; - } - - uint32 bodySize; - if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &bodySize ) ) - { - Assert( 0 ); - delete this; - return; - } - - // Save the downloaded file to the addons folder - CUtlBuffer bodyBuffer; - bodyBuffer.EnsureCapacity( bodySize ); - - if ( !pHTTP->GetHTTPResponseBodyData( arg->m_hRequest, ( uint8 * )bodyBuffer.Base(), bodySize ) ) - { - Assert( 0 ); - bodyBuffer.Purge(); - delete this; - return; - } - - pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); - - // Store the zip file to disk so we can then extract it - const char *zipPath = "addons/garrysmod.zip"; - - bodyBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, bodySize ); - filesystem->WriteFile( zipPath, CONTENT_SEARCH_PATH, bodyBuffer ); - - // This is the name of the folder from the zip that we actually want to extract to 'addons/garrysmod' - char desiredFolder[MAX_PATH]; - Q_snprintf( desiredFolder, sizeof( desiredFolder ), "garrysmod-%s/garrysmod", LUA_GMOD_REPOSITORY_HASH ); - - // Extract the downloaded zip file, but only the files in the desired folder - char fullZipPath[MAX_PATH]; - filesystem->RelativePathToFullPath( zipPath, CONTENT_SEARCH_PATH, fullZipPath, sizeof( fullZipPath ) ); - -#ifdef CLIENT_DLL - const char *gamePath = engine->GetGameDirectory(); -#else - char gamePath[MAX_PATH]; - engine->GetGameDir( gamePath, MAX_PATH ); -#endif - - HZIP hZipFile = OpenZip( fullZipPath, 0, ZIP_FILENAME ); - ZIPENTRY zipEntry; - - GetZipItem( hZipFile, -1, &zipEntry ); - - int numItems = zipEntry.index; - - for ( int i = 0; i < numItems; i++ ) - { - GetZipItem( hZipFile, i, &zipEntry ); - - if ( ( zipEntry.attr & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY ) - continue; // skip directories - - if ( Q_strnicmp( zipEntry.name, desiredFolder, Q_strlen( desiredFolder ) ) != 0 ) - continue; // skip files not in the desired folder - - char pathWithoutDesiredFolder[MAX_PATH]; - Q_strncpy( pathWithoutDesiredFolder, zipEntry.name + Q_strlen( desiredFolder ) + 1, sizeof( pathWithoutDesiredFolder ) ); - - char pathWithoutFileName[MAX_PATH]; - Q_snprintf( pathWithoutFileName, sizeof( pathWithoutFileName ), "addons\\garrysmod\\%s", pathWithoutDesiredFolder ); - Q_StripFilename( pathWithoutFileName ); - - filesystem->CreateDirHierarchy( pathWithoutFileName, CONTENT_SEARCH_PATH ); - - char fullPath[MAX_PATH]; - Q_snprintf( fullPath, sizeof( fullPath ), "%s\\addons\\garrysmod\\%s", gamePath, pathWithoutDesiredFolder ); - - UnzipItem( hZipFile, i, fullPath, 0, ZIP_FILENAME ); - } - - CloseZip( hZipFile ); - - // Remove the downloaded zip file - filesystem->RemoveFile( zipPath, CONTENT_SEARCH_PATH ); - - Msg( "Successfully downloaded the GMod repository to 'garrysmod' in the 'addons' folder\n" ); - - MountAddon( "garrysmod" ); - - delete this; - } -}; - -CCallResult< GmodCompatDownloader, HTTPRequestCompleted_t > callback; - -// Used in GameUI to download the GMod repository to the addons folder -CON_COMMAND( lua_download_gmod_repository, "Downloads the GMod repository to 'garrysmod' in your addons folder" ) -{ - ISteamHTTP *pHTTP = steamapicontext->SteamHTTP(); - - if ( !pHTTP ) - { - Warning( "Steam HTTP interface not available\n" ); - return; - } - - if ( filesystem->IsDirectory( "addons/garrysmod", CONTENT_SEARCH_PATH ) ) - { - Warning( "The 'garrysmod' folder already exists in the 'addons' folder. Remove it manually if you want to download the garrysmod repo from github again.\n" ); - return; - } - - char downloadUrl[MAX_PATH]; - Q_snprintf( downloadUrl, sizeof( downloadUrl ), LUA_GMOD_REPOSITORY_DOWNLOAD, LUA_GMOD_REPOSITORY_HASH ); - - HTTPRequestHandle hRequest = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, downloadUrl ); - - if ( !hRequest ) - { - Warning( "Failed to create HTTP request to %s\n", downloadUrl ); - return; - } - - pHTTP->SetHTTPRequestGetOrPostParameter( hRequest, "Accept-Encoding", "gzip, deflate" ); - pHTTP->SetHTTPRequestGetOrPostParameter( hRequest, "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" ); - - SteamAPICall_t hCall; - - if ( !pHTTP->SendHTTPRequest( hRequest, &hCall ) ) - { - Warning( "Failed to send HTTP request to %s\n", downloadUrl ); - pHTTP->ReleaseHTTPRequest( hRequest ); - return; - } - - Warning( "Downloading the GMod repository from %s\n", downloadUrl ); - - callback.Set( hCall, new GmodCompatDownloader(), &GmodCompatDownloader::OnCompleted ); -} -#endif +//#ifdef CLIENT_DLL +//#define LUA_GMOD_REPOSITORY_DOWNLOAD "https://github.com/Facepunch/garrysmod/archive/%s.zip" +//#define LUA_GMOD_REPOSITORY_HASH "430fc8a2cf4c25873766b1d1a0df1cb94c68d5b7" +// +//class GmodCompatDownloader +//{ +// public: +// void OnCompleted( HTTPRequestCompleted_t *arg, bool bFailed ) +// { +// ISteamHTTP *pHTTP = steamapicontext->SteamHTTP(); +// +// if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK ) +// { +// Warning( "Failed to download the GMod repository! Status code: %d\n", arg->m_eStatusCode ); +// pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); +// delete this; +// return; +// } +// +// if ( !arg->m_bRequestSuccessful ) +// { +// Warning( "Failed to download the GMod repository! Request unsuccessful\n" ); +// pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); +// delete this; +// return; +// } +// +// if ( bFailed ) +// { +// Warning( "Failed to download the GMod repository! Request failed\n" ); +// pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); +// delete this; +// return; +// } +// +// uint32 bodySize; +// if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &bodySize ) ) +// { +// Assert( 0 ); +// delete this; +// return; +// } +// +// // Save the downloaded file to the addons folder +// CUtlBuffer bodyBuffer; +// bodyBuffer.EnsureCapacity( bodySize ); +// +// if ( !pHTTP->GetHTTPResponseBodyData( arg->m_hRequest, ( uint8 * )bodyBuffer.Base(), bodySize ) ) +// { +// Assert( 0 ); +// bodyBuffer.Purge(); +// delete this; +// return; +// } +// +// pHTTP->ReleaseHTTPRequest( arg->m_hRequest ); +// +// // Store the zip file to disk so we can then extract it +// const char *zipPath = "addons/garrysmod.zip"; +// +// bodyBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, bodySize ); +// filesystem->WriteFile( zipPath, CONTENT_SEARCH_PATH, bodyBuffer ); +// +// // This is the name of the folder from the zip that we actually want to extract to 'addons/garrysmod' +// char desiredFolder[MAX_PATH]; +// Q_snprintf( desiredFolder, sizeof( desiredFolder ), "garrysmod-%s/garrysmod", LUA_GMOD_REPOSITORY_HASH ); +// +// // Extract the downloaded zip file, but only the files in the desired folder +// char fullZipPath[MAX_PATH]; +// filesystem->RelativePathToFullPath( zipPath, CONTENT_SEARCH_PATH, fullZipPath, sizeof( fullZipPath ) ); +// +//#ifdef CLIENT_DLL +// const char *gamePath = engine->GetGameDirectory(); +//#else +// char gamePath[MAX_PATH]; +// engine->GetGameDir( gamePath, MAX_PATH ); +//#endif +// +// HZIP hZipFile = OpenZip( fullZipPath, 0, ZIP_FILENAME ); +// ZIPENTRY zipEntry; +// +// GetZipItem( hZipFile, -1, &zipEntry ); +// +// int numItems = zipEntry.index; +// +// for ( int i = 0; i < numItems; i++ ) +// { +// GetZipItem( hZipFile, i, &zipEntry ); +// +// if ( ( zipEntry.attr & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY ) +// continue; // skip directories +// +// if ( Q_strnicmp( zipEntry.name, desiredFolder, Q_strlen( desiredFolder ) ) != 0 ) +// continue; // skip files not in the desired folder +// +// char pathWithoutDesiredFolder[MAX_PATH]; +// Q_strncpy( pathWithoutDesiredFolder, zipEntry.name + Q_strlen( desiredFolder ) + 1, sizeof( pathWithoutDesiredFolder ) ); +// +// char pathWithoutFileName[MAX_PATH]; +// Q_snprintf( pathWithoutFileName, sizeof( pathWithoutFileName ), "addons\\garrysmod\\%s", pathWithoutDesiredFolder ); +// Q_StripFilename( pathWithoutFileName ); +// +// filesystem->CreateDirHierarchy( pathWithoutFileName, CONTENT_SEARCH_PATH ); +// +// char fullPath[MAX_PATH]; +// Q_snprintf( fullPath, sizeof( fullPath ), "%s\\addons\\garrysmod\\%s", gamePath, pathWithoutDesiredFolder ); +// +// UnzipItem( hZipFile, i, fullPath, 0, ZIP_FILENAME ); +// } +// +// CloseZip( hZipFile ); +// +// // Remove the downloaded zip file +// filesystem->RemoveFile( zipPath, CONTENT_SEARCH_PATH ); +// +// Msg( "Successfully downloaded the GMod repository to 'garrysmod' in the 'addons' folder\n" ); +// +// MountAddon( "garrysmod" ); +// +// delete this; +// } +//}; +// +//CCallResult< GmodCompatDownloader, HTTPRequestCompleted_t > callback; +// +//// Used in GameUI to download the GMod repository to the addons folder +//CON_COMMAND( lua_download_gmod_repository, "Downloads the GMod repository to 'garrysmod' in your addons folder" ) +//{ +// ISteamHTTP *pHTTP = steamapicontext->SteamHTTP(); +// +// if ( !pHTTP ) +// { +// Warning( "Steam HTTP interface not available\n" ); +// return; +// } +// +// if ( filesystem->IsDirectory( "addons/garrysmod", CONTENT_SEARCH_PATH ) ) +// { +// Warning( "The 'garrysmod' folder already exists in the 'addons' folder. Remove it manually if you want to download the garrysmod repo from github again.\n" ); +// return; +// } +// +// char downloadUrl[MAX_PATH]; +// Q_snprintf( downloadUrl, sizeof( downloadUrl ), LUA_GMOD_REPOSITORY_DOWNLOAD, LUA_GMOD_REPOSITORY_HASH ); +// +// HTTPRequestHandle hRequest = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, downloadUrl ); +// +// if ( !hRequest ) +// { +// Warning( "Failed to create HTTP request to %s\n", downloadUrl ); +// return; +// } +// +// pHTTP->SetHTTPRequestGetOrPostParameter( hRequest, "Accept-Encoding", "gzip, deflate" ); +// pHTTP->SetHTTPRequestGetOrPostParameter( hRequest, "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" ); +// +// SteamAPICall_t hCall; +// +// if ( !pHTTP->SendHTTPRequest( hRequest, &hCall ) ) +// { +// Warning( "Failed to send HTTP request to %s\n", downloadUrl ); +// pHTTP->ReleaseHTTPRequest( hRequest ); +// return; +// } +// +// Warning( "Downloading the GMod repository from %s\n", downloadUrl ); +// +// callback.Set( hCall, new GmodCompatDownloader(), &GmodCompatDownloader::OnCompleted ); +//} +//#endif diff --git a/src/game/shared/luasrclib.h b/src/game/shared/luasrclib.h index 9e6e66621a..ea4c4bffe4 100644 --- a/src/game/shared/luasrclib.h +++ b/src/game/shared/luasrclib.h @@ -122,6 +122,9 @@ LUALIB_API int( luaopen_g_pClientShadowMgr )( lua_State *L ); #define LUA_FONTLIBNAME "FontHandle" LUALIB_API int( luaopen_HFont )( lua_State *L ); +#define LUA_GAMEEVENTSLIBNAME "GameEvents" +LUALIB_API int( luaopen_GameEvents )( lua_State *L ); + #define LUA_MATERIALMETANAME "Material" LUALIB_API int( luaopen_IMaterial )( lua_State *L ); @@ -190,8 +193,9 @@ LUALIB_API int( luaopen_ParticleSystem )( lua_State *L ); #define LUA_PHYSENVLIBNAME "PhysicsEnvironments" LUALIB_API int( luaopen_PhysicsEnvironments )( lua_State *L ); -#define LUA_PREDICTIONLIBNAME "Prediction" -LUALIB_API int( luaopen_prediction )( lua_State *L ); +#define LUA_PREDICTIONSLIBNAME "Predictions" +LUALIB_API int( luaopen_Predictions )( lua_State *L ); +LUALIB_API int( luaopen_PredictionShared )( lua_State *L ); #define LUA_QANGLEMETANAME "Angle" LUALIB_API int( luaopen_QAngle )( lua_State *L ); @@ -214,6 +218,9 @@ LUALIB_API int( luaopen_SOLID )( lua_State *L ); #define LUA_SOLIDFLAGLIBNAME "SOLID_FLAG" LUALIB_API int( luaopen_SOLIDFLAG )( lua_State *L ); +#define LUA_SOUNDSLIBNAME "Sounds" +LUALIB_API int( luaopen_Sounds )( lua_State *L ); + #define LUA_STEAMAPICONTEXTLIBNAME "SteamApiContexts" LUALIB_API int( luaopen_SteamApiContexts )( lua_State *L ); diff --git a/src/game/shared/mountaddons.cpp b/src/game/shared/mountaddons.cpp index a72e73001d..de93cd117f 100644 --- a/src/game/shared/mountaddons.cpp +++ b/src/game/shared/mountaddons.cpp @@ -7,68 +7,84 @@ #include "cbase.h" #include "filesystem.h" #include "luamanager.h" +#include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" static CUtlVector< CUtlString > g_MountedAddons; -void MountAddon( const char *addonName ) +void MountAddon( const char *addonDirectoryName, const char *addonPath ) { - char relativeAddonPath[MAX_PATH]; - Q_snprintf( relativeAddonPath, sizeof( relativeAddonPath ), LUA_PATH_ADDONS "\\%s", addonName ); - - char currentDirectoryPath[MAX_PATH]; - bool bGetCurrentDirectory = V_GetCurrentDirectory( currentDirectoryPath, sizeof( currentDirectoryPath ) ); - - if ( bGetCurrentDirectory ) - { -#ifdef CLIENT_DLL - const char *gamePath = engine->GetGameDirectory(); -#else - char gamePath[MAX_PATH]; - engine->GetGameDir( gamePath, sizeof( gamePath ) ); -#endif - - V_SetCurrentDirectory( gamePath ); - } + char addonFullPath[MAX_PATH]; + Q_snprintf( addonFullPath, sizeof( addonFullPath ), "%s\\%s", addonPath, addonDirectoryName ); // Ensure the engine can find content in the addon - filesystem->AddSearchPath( relativeAddonPath, CONTENT_SEARCH_PATH, PATH_ADD_TO_TAIL ); - - if ( bGetCurrentDirectory ) - V_SetCurrentDirectory( currentDirectoryPath ); + filesystem->AddSearchPath( addonDirectoryName, CONTENT_SEARCH_PATH, PATH_ADD_TO_TAIL ); #ifdef GAME_DLL // Prevent the message from printing twice in clients - ConColorMsg( Color( 200, 255, 0, 255 ), "Mounting addon \"%s\"...\n", addonName ); + ConColorMsg( Color( 200, 255, 0, 255 ), "Mounting addon \"%s\"...\n", addonDirectoryName ); #endif - g_MountedAddons.AddToTail( CUtlString( addonName ) ); + g_MountedAddons.AddToTail( CUtlString( addonFullPath ) ); } void MountAddons() { - FileFindHandle_t fh; - char addonName[255]; - char const *fn = g_pFullFileSystem->FindFirstEx( LUA_PATH_ADDONS "\\*", CONTENT_SEARCH_PATH, &fh ); + // We want the absolute paths so we can use them to set them as mount roots + // and find their gamemodes, lua paths, etc get added to the search paths + // We'll split the search at each ; and loop through each path, adding LUA_PATH_ADDONS "\\*" + // and finding the addons in each path + char mountPaths[MAX_PATH * 50]; // TODO: Make this dynamic + filesystem->GetSearchPath( "GAME", false, mountPaths, sizeof( mountPaths ) ); - g_MountedAddons.Purge(); + char *pPath = mountPaths; + char *pEnd = pPath + strlen( pPath ); - while ( fn ) + while ( pPath < pEnd ) { - Q_strcpy( addonName, fn ); - if ( fn[0] != '.' ) + char *pSemiColon = strchr( pPath, ';' ); + + if ( pSemiColon ) { - if ( g_pFullFileSystem->FindIsDirectory( fh ) ) + *pSemiColon = '\0'; + } + + char addonPath[MAX_PATH]; + Q_snprintf( addonPath, sizeof( addonPath ), "%s" LUA_PATH_ADDONS, pPath ); + + char addonSearchPath[MAX_PATH]; + Q_snprintf( addonSearchPath, sizeof( addonSearchPath ), "%s\\*", addonPath ); + + FileFindHandle_t fh; + char addonName[255]; + const char *fn = filesystem->FindFirstEx( addonSearchPath, CONTENT_SEARCH_PATH, &fh ); + + while ( fn ) + { + Q_strcpy( addonName, fn ); + if ( fn[0] != '.' ) { - MountAddon( addonName ); + if ( filesystem->FindIsDirectory( fh ) ) + { + MountAddon( addonName, addonPath ); + } } + + fn = filesystem->FindNext( fh ); } - fn = g_pFullFileSystem->FindNext( fh ); - } + filesystem->FindClose( fh ); - g_pFullFileSystem->FindClose( fh ); + if ( pSemiColon ) + { + pPath = pSemiColon + 1; + } + else + { + break; + } + } } int GetMountedAddons( CUtlVector< CUtlString > &addons ) @@ -91,52 +107,9 @@ void SetupMountedAddons( lua_State *L ) { int nAddonCount = g_MountedAddons.Count(); -#ifdef CLIENT_DLL - const char *gamePath = engine->GetGameDirectory(); -#else - char gamePath[MAX_PATH]; - engine->GetGameDir( gamePath, MAX_PATH ); -#endif - for ( int i = 0; i < nAddonCount; i++ ) { - const char *addonName = g_MountedAddons[i].Get(); - - char fullAddonPath[MAX_PATH]; - Q_snprintf( fullAddonPath, sizeof( fullAddonPath ), "%s\\" LUA_PATH_ADDONS "\\%s", gamePath, addonName ); - - // /addons//scripts/lua/?.lua - char addonLuaRootPath[MAX_PATH]; - Q_snprintf( addonLuaRootPath, sizeof( addonLuaRootPath ), "%s\\%s\\?.lua", fullAddonPath, LUA_ROOT ); - - luasrc_add_to_package_path( L, addonLuaRootPath ); - - // /addons//scripts/lua/modules/?.lua - char addonLuaModulesPath[MAX_PATH]; - Q_snprintf( addonLuaModulesPath, sizeof( addonLuaModulesPath ), "%s\\%s\\?.lua", fullAddonPath, LUA_PATH_MODULES ); - - luasrc_add_to_package_path( L, addonLuaModulesPath ); - - // /addons//scripts/lua/bin/?.[dll|so] (cpath) - char addonLuaBinaryModulesPath[MAX_PATH]; - - Q_snprintf( - addonLuaBinaryModulesPath, - sizeof( addonLuaBinaryModulesPath ), -#ifdef _WIN32 - "%s\\%s\\?.dll", -#elif _LINUX - "%s\\%s\\?.so", -#endif - fullAddonPath, - LUA_PATH_BINARY_MODULES ); - - luasrc_add_to_package_path( L, addonLuaBinaryModulesPath, /* isPathC = */ true ); - - // /addons//gamemodes/?.lua - char addonLuaGamemodesPath[MAX_PATH]; - Q_snprintf( addonLuaGamemodesPath, sizeof( addonLuaGamemodesPath ), "%s%s\\?.lua", fullAddonPath, LUA_PATH_GAMEMODES ); - - luasrc_add_to_package_path( L, addonLuaGamemodesPath ); + const char *addonFullPath = g_MountedAddons[i].Get(); + SetupRootSearchPaths( addonFullPath, L ); } } diff --git a/src/game/shared/mountaddons.h b/src/game/shared/mountaddons.h index 09758a296a..b4d713db98 100644 --- a/src/game/shared/mountaddons.h +++ b/src/game/shared/mountaddons.h @@ -11,7 +11,7 @@ #pragma once #endif -void MountAddon( const char *addonName ); +void MountAddon( const char *addonDirectoryName, const char *addonPath ); void MountAddons(); int GetMountedAddons( CUtlVector< CUtlString > &addons ); void SetupMountedAddons( lua_State *L ); diff --git a/src/game/shared/mountsteamcontent.cpp b/src/game/shared/mountsteamcontent.cpp index 685ec5b8b0..33f4ef1b66 100644 --- a/src/game/shared/mountsteamcontent.cpp +++ b/src/game/shared/mountsteamcontent.cpp @@ -18,59 +18,145 @@ static CUtlVector< mountableGame_t > g_GamePaths; #define REGISTER_GAME( pSteamApps, GameName, GameDirectory, GameAppId ) \ g_GamePaths.AddToTail( mountableGame_t( GameName, GameDirectory, GameAppId, pSteamApps->BIsAppInstalled( GameAppId ), pSteamApps->BIsSubscribedApp( GameAppId ) ) ); -#define REGISTER_GAME_WITH_VPK( pSteamApps, GameName, GameDirectory, GameAppId, ... ) \ - { \ - mountableGame_t game( GameName, GameDirectory, GameAppId, pSteamApps->BIsAppInstalled( GameAppId ), pSteamApps->BIsSubscribedApp( GameAppId ) ); \ - const char *vpkPaths[] = { __VA_ARGS__ }; \ - for ( int i = 0; i < ARRAYSIZE( vpkPaths ); i++ ) \ - { \ - game.AddVPK( vpkPaths[i] ); \ - } \ - g_GamePaths.AddToTail( game ); \ +#define REGISTER_GAME_WITH_VPK( pSteamApps, GameName, GameDirectory, GameAppId, ... ) \ + { \ + mountableGame_t game( GameName, GameDirectory, GameAppId, pSteamApps->BIsAppInstalled( GameAppId ), pSteamApps->BIsSubscribedApp( GameAppId ) ); \ + const char *vpkPaths[] = { __VA_ARGS__ }; \ + for ( int i = 0; i < ARRAYSIZE( vpkPaths ); i++ ) \ + { \ + game.AddVPK( vpkPaths[i] ); \ + } \ + g_GamePaths.AddToTail( game ); \ } #define HL1MP_APPID 360 -static void SetupRootSearchPaths( const char *gamePath ) +void SetupRootSearchPaths( const char *rootPath, lua_State *L ) { char contentSearchPath[MAX_PATH]; // GAME - Add the game root as a search path - filesystem->AddSearchPath( gamePath, "GAME", PATH_ADD_TO_TAIL ); + filesystem->AddSearchPath( rootPath, "GAME", PATH_ADD_TO_TAIL ); + + // LUA - Lua root directory + Q_strncpy( contentSearchPath, rootPath, sizeof( contentSearchPath ) ); + Q_strncat( contentSearchPath, "\\" LUA_ROOT "\\", sizeof( contentSearchPath ) ); + + filesystem->AddSearchPath( contentSearchPath, CONTENT_SEARCH_PATH_LUA, PATH_ADD_TO_TAIL ); + + // LUA - Lua gmod root directory (for gmod compatibility) + Q_strncpy( contentSearchPath, rootPath, sizeof( contentSearchPath ) ); + Q_strncat( contentSearchPath, "\\lua\\", sizeof( contentSearchPath ) ); + + filesystem->AddSearchPath( contentSearchPath, CONTENT_SEARCH_PATH_LUA, PATH_ADD_TO_TAIL ); // LUA - gamemodes directory - Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); + Q_strncpy( contentSearchPath, rootPath, sizeof( contentSearchPath ) ); Q_strncat( contentSearchPath, LUA_PATH_GAMEMODES "\\", sizeof( contentSearchPath ) ); filesystem->AddSearchPath( contentSearchPath, CONTENT_SEARCH_PATH_LUA, PATH_ADD_TO_TAIL ); - // LUA - addons directory - Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, "\\" LUA_PATH_ADDONS "\\", sizeof( contentSearchPath ) ); + // If Lua is available, add the package paths + if ( !L ) + return; - filesystem->AddSearchPath( contentSearchPath, CONTENT_SEARCH_PATH_LUA, PATH_ADD_TO_TAIL ); -} + // /scripts/lua/?.lua + char luaRootPath[MAX_PATH]; + Q_snprintf( luaRootPath, sizeof( luaRootPath ), "%s\\%s\\?.lua", rootPath, LUA_ROOT ); + + luasrc_add_to_package_path( L, luaRootPath ); -// RemoveRootSearchPaths (inverse of SetupRootSearchPaths) + // /scripts/lua/modules/?.lua + char luaModulesPath[MAX_PATH]; + Q_snprintf( luaModulesPath, sizeof( luaModulesPath ), "%s\\%s\\?.lua", rootPath, LUA_PATH_MODULES ); -static void RemoveRootSearchPaths( const char *gamePath ) + luasrc_add_to_package_path( L, luaModulesPath ); + + // /scripts/lua/bin/?.[dll|so] (cpath) + char luaBinaryModulesPath[MAX_PATH]; + + Q_snprintf( + luaBinaryModulesPath, + sizeof( luaBinaryModulesPath ), +#ifdef _WIN32 + "%s\\%s\\?.dll", +#elif _LINUX + "%s\\%s\\?.so", +#endif + rootPath, + LUA_PATH_BINARY_MODULES ); + + luasrc_add_to_package_path( L, luaBinaryModulesPath, /* isPathC = */ true ); + + // /gamemodes/?.lua + char luaGamemodesPath[MAX_PATH]; + Q_snprintf( luaGamemodesPath, sizeof( luaGamemodesPath ), "%s%s\\?.lua", rootPath, LUA_PATH_GAMEMODES ); + + luasrc_add_to_package_path( L, luaGamemodesPath ); +} + +void RemoveRootSearchPaths( const char *gamePath, lua_State *L ) { char contentSearchPath[MAX_PATH]; // GAME - Add the game root as a search path filesystem->RemoveSearchPath( gamePath ); - // LUA - gamemodes directory + // LUA - Lua root directory Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, LUA_PATH_GAMEMODES "\\", sizeof( contentSearchPath ) ); + Q_strncat( contentSearchPath, "\\" LUA_ROOT "\\", sizeof( contentSearchPath ) ); + + filesystem->RemoveSearchPath( contentSearchPath ); + + // LUA - Lua gmod root directory (for gmod compatibility) + Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); + Q_strncat( contentSearchPath, "\\lua\\", sizeof( contentSearchPath ) ); filesystem->RemoveSearchPath( contentSearchPath ); - // LUA - addons directory + // LUA - gamemodes directory Q_strncpy( contentSearchPath, gamePath, sizeof( contentSearchPath ) ); - Q_strncat( contentSearchPath, "\\" LUA_PATH_ADDONS "\\", sizeof( contentSearchPath ) ); + Q_strncat( contentSearchPath, LUA_PATH_GAMEMODES "\\", sizeof( contentSearchPath ) ); filesystem->RemoveSearchPath( contentSearchPath ); + + // If Lua is available, remove the package paths + if ( !L ) + return; + + // /scripts/lua/?.lua + char luaRootPath[MAX_PATH]; + Q_snprintf( luaRootPath, sizeof( luaRootPath ), "%s\\%s\\?.lua", gamePath, LUA_ROOT ); + + luasrc_remove_from_package_path( L, luaRootPath ); + + // /scripts/lua/modules/?.lua + char luaModulesPath[MAX_PATH]; + Q_snprintf( luaModulesPath, sizeof( luaModulesPath ), "%s\\%s\\?.lua", gamePath, LUA_PATH_MODULES ); + + luasrc_remove_from_package_path( L, luaModulesPath ); + + // /scripts/lua/bin/?.[dll|so] (cpath) + char luaBinaryModulesPath[MAX_PATH]; + + Q_snprintf( + luaBinaryModulesPath, + sizeof( luaBinaryModulesPath ), +#ifdef _WIN32 + "%s\\%s\\?.dll", +#elif _LINUX + "%s\\%s\\?.so", +#endif + gamePath, + LUA_PATH_BINARY_MODULES ); + + luasrc_remove_from_package_path( L, luaBinaryModulesPath, /* isPathC = */ true ); + + // /gamemodes/?.lua + char luaGamemodesPath[MAX_PATH]; + Q_snprintf( luaGamemodesPath, sizeof( luaGamemodesPath ), "%s%s\\?.lua", gamePath, LUA_PATH_GAMEMODES ); + + luasrc_remove_from_package_path( L, luaGamemodesPath ); } void AddSearchPathByAppId( int nAppId ) @@ -104,7 +190,7 @@ void AddSearchPathByAppId( int nAppId ) // GAME - Add the game root as a search path Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[i].directoryName ); - SetupRootSearchPaths( fullPath ); + SetupRootSearchPaths( fullPath, L ); // GAME - Add the VPKs as search paths auto &vpkPaths = g_GamePaths[i].vpks; @@ -112,7 +198,7 @@ void AddSearchPathByAppId( int nAppId ) for ( int j = 0; j < vpkPaths.Count(); j++ ) { Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, vpkPaths[j] ); - SetupRootSearchPaths( fullPath ); + SetupRootSearchPaths( fullPath, L ); } g_GamePaths[i].isMounted = true; @@ -147,14 +233,14 @@ void RemoveSearchPathByAppId( int nAppId ) continue; Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[i].directoryName ); - RemoveRootSearchPaths( fullPath ); + RemoveRootSearchPaths( fullPath, L ); auto &vpkPaths = g_GamePaths[i].vpks; for ( int j = 0; j < vpkPaths.Count(); j++ ) { Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, vpkPaths[j] ); - RemoveRootSearchPaths( fullPath ); + RemoveRootSearchPaths( fullPath, L ); } g_GamePaths[i].isMounted = false; @@ -190,20 +276,20 @@ void InitializeGameContentMounting() Q_StripTrailingSlash( gamePath ); #endif - SetupRootSearchPaths( gamePath ); + SetupRootSearchPaths( gamePath, nullptr ); // Now mount the game content ISteamApps *pSteamApps = steamapicontext->SteamApps(); if ( !pSteamApps ) { - Error("Failed to mount game content (are you not signed into Steam?)\n"); + Error( "Failed to mount game content (are you not signed into Steam?)\n" ); return; } REGISTER_GAME( pSteamApps, "Half-Life 2", "hl2", 220 ); REGISTER_GAME( pSteamApps, "Half-Life", "hl1", 280 ); - + REGISTER_GAME( pSteamApps, "Day of Defeat: Source", "dod", 300 ); REGISTER_GAME( pSteamApps, "Half-Life 2: Lost Coast", "lostcoast", 340 ); REGISTER_GAME( pSteamApps, "Half-Life Deathmatch: Source", "hl1mp", HL1MP_APPID ); @@ -352,19 +438,19 @@ bool UnmountGameContentByAppId( int nAppId ) KeyValues *pMainFile, *pFileSystemInfo; pMainFile = new KeyValues( "gamecontent.txt" ); - #ifdef CLIENT_DLL +#ifdef CLIENT_DLL const char *gamePath = engine->GetGameDirectory(); - #else +#else char gamePath[256]; engine->GetGameDir( gamePath, 256 ); Q_StripTrailingSlash( gamePath ); - #endif +#endif // On linux because of case sensitiviy we need to check for both. const char *paths[] = { - #ifdef _LINUX +#ifdef _LINUX "%s/GameContent.txt", - #endif +#endif "%s/gamecontent.txt" }; bool bFound = false; diff --git a/src/game/shared/mountsteamcontent.h b/src/game/shared/mountsteamcontent.h index ced991542a..32e7972071 100644 --- a/src/game/shared/mountsteamcontent.h +++ b/src/game/shared/mountsteamcontent.h @@ -60,6 +60,8 @@ struct mountableGame_t } }; +void SetupRootSearchPaths( const char *gamePath, lua_State *L ); +void RemoveRootSearchPaths( const char *gamePath, lua_State *L ); void InitializeGameContentMounting(); bool MountGameContentByAppId( int nAppId ); bool UnmountGameContentByAppId( int nAppId ); diff --git a/src/public/lenumerations_shared.cpp b/src/public/lenumerations_shared.cpp index 39be89e91c..6546d80c31 100644 --- a/src/public/lenumerations_shared.cpp +++ b/src/public/lenumerations_shared.cpp @@ -22,6 +22,20 @@ LUALIB_API int luaopen_SharedEnumerations( lua_State *L ) lua_pushenum( L, ShakeCommand_t::SHAKE_START_NORUMBLE, "START_NO_RUMBLE" ); LUA_SET_ENUM_LIB_END( L ); + LUA_SET_ENUM_LIB_BEGIN( L, "SOUND_CHANNEL" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_REPLACE, "REPLACE" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_AUTO, "AUTO" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_WEAPON, "WEAPON" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_VOICE, "VOICE" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_ITEM, "ITEM" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_BODY, "BODY" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_STREAM, "STREAM" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_STATIC, "STATIC" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_VOICE2, "VOICE2" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_VOICE_BASE, "VOICE_BASE" ); + lua_pushenum( L, SOUND_CHANNEL::CHAN_USER_BASE, "USER_BASE" ); + LUA_SET_ENUM_LIB_END( L ); + LUA_SET_ENUM_LIB_BEGIN( L, "RENDER_MODE" ); lua_pushenum( L, RenderMode_t::kRenderNormal, "NORMAL" ); lua_pushenum( L, RenderMode_t::kRenderTransColor, "TRANSPARENT_COLOR" ); diff --git a/src/public/lfilesystem.cpp b/src/public/lfilesystem.cpp index 33ee8b622a..27e17132ea 100644 --- a/src/public/lfilesystem.cpp +++ b/src/public/lfilesystem.cpp @@ -564,9 +564,6 @@ LUA_BINDING_BEGIN( Files, Find, "library", "Find files." ) char const *fn = filesystem->FindFirstEx( path, pathId, &fh ); - char allSearchPaths[MAX_PATH * 50]; - filesystem->GetSearchPath( pathId, false, allSearchPaths, sizeof( allSearchPaths ) ); - lua_createtable( L, 0, 0 ); // files lua_createtable( L, 0, 0 ); // directories diff --git a/src/public/lgameevents.cpp b/src/public/lgameevents.cpp new file mode 100644 index 0000000000..08d4ecc5d7 --- /dev/null +++ b/src/public/lgameevents.cpp @@ -0,0 +1,81 @@ +#include "cbase.h" +#include "luamanager.h" +#include "luasrclib.h" +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CLuaGameEventListener : public IGameEventListener2 +{ + protected: + lua_State *L; + + public: + CLuaGameEventListener( lua_State *L ) + { + this->L = L; + } + + void AddListener( const char *name ) + { + gameeventmanager->AddListener( this, name, false ); + } + + // IGameEventListener2 Interface: + public: + virtual void FireGameEvent( IGameEvent *event ) + { + const char *eventName = event->GetName(); + + LUA_CALL_HOOK_FOR_STATE_BEGIN( this->L, eventName ); + int amountOfArguments = 0; + + // TODO: Other game events + if (Q_strcmp(eventName, "player_spawn") == 0) + { + lua_pushinteger(this->L, event->GetInt("userid")); + amountOfArguments++; + } + else + { + DevMsg("Unimplemented game event called: %s\n", eventName); + } + + LUA_CALL_HOOK_FOR_STATE_END( this->L, amountOfArguments, 0 ); + } +}; + +static CLuaGameEventListener *luaGameEventListener; + +void InitializeLuaGameEventHandler( lua_State *L ) +{ + luaGameEventListener = new CLuaGameEventListener( L ); +} + +void ShutdownLuaGameEventHandler( lua_State *L ) +{ + gameeventmanager->RemoveListener( luaGameEventListener ); + delete luaGameEventListener; +} + +LUA_REGISTRATION_INIT( GameEvents ) + +LUA_BINDING_BEGIN( GameEvents, Listen, "library", "Call a hook for this game event name, when the event occurs." ) +{ + luaGameEventListener->AddListener( LUA_BINDING_ARGUMENT( luaL_checkstring, 1, "eventName" ) ); + return 0; +} +LUA_BINDING_END() + +/* +** Open GameEvents library +*/ +LUALIB_API int luaopen_GameEvents( lua_State *L ) +{ + LUA_REGISTRATION_COMMIT_LIBRARY( GameEvents ); + + return 1; +} diff --git a/src/public/lgameevents.h b/src/public/lgameevents.h new file mode 100644 index 0000000000..1fab8e3bd4 --- /dev/null +++ b/src/public/lgameevents.h @@ -0,0 +1,10 @@ +#ifndef LGAMEEVENTS_H +#define LGAMEEVENTS_H +#ifdef _WIN32 +#pragma once +#endif + +void InitializeLuaGameEventHandler( lua_State *L ); +void ShutdownLuaGameEventHandler( lua_State *L ); + +#endif // LGAMEEVENTS_H diff --git a/src/public/lsounds.cpp b/src/public/lsounds.cpp new file mode 100644 index 0000000000..d22d15526c --- /dev/null +++ b/src/public/lsounds.cpp @@ -0,0 +1,208 @@ +#include "cbase.h" +#include "luamanager.h" +#include "luasrclib.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ISoundEmitterSystemBase *soundemitterbase; + +LUA_REGISTRATION_INIT( Sounds ) + +// LUA_BINDING_BEGIN( GameEvents, Listen, "library", "Call a hook for this game event name, when the event occurs." ) +//{ +// luaGameEventListener->AddListener( LUA_BINDING_ARGUMENT( luaL_checkstring, 1, "eventName" ) ); +// return 0; +// } +// LUA_BINDING_END() + +/*abstract_class ISoundEmitterSystemBase : public IAppSystem +{ +public: + // Init, shutdown called after we know what mod is running + virtual bool ModInit() = 0; + virtual void ModShutdown() = 0; + + virtual int GetSoundIndex( const char *pName ) const = 0; + virtual bool IsValidIndex( int index ) = 0; + virtual int GetSoundCount( void ) = 0; + + virtual const char *GetSoundName( int index ) = 0; + virtual bool GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ) = 0; + + virtual const char *GetWaveName( CUtlSymbol& sym ) = 0; + virtual CUtlSymbol AddWaveName( const char *name ) = 0; + + virtual soundlevel_t LookupSoundLevel( const char *soundname ) = 0; + virtual const char *GetWavFileForSound( const char *soundname, char const *actormodel ) = 0; + virtual const char *GetWavFileForSound( const char *soundname, gender_t gender ) = 0; + virtual int CheckForMissingWavFiles( bool verbose ) = 0; + virtual const char *GetSourceFileForSound( int index ) const = 0; + + // Iteration methods + virtual int First() const = 0; + virtual int Next( int i ) const = 0; + virtual int InvalidIndex() const = 0; + + virtual CSoundParametersInternal *InternalGetParametersForSound( int index ) = 0; + + // The host application is responsible for dealing with dirty sound scripts, etc. + virtual bool AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params ) = 0; + virtual void RemoveSound( const char *soundname ) = 0; + virtual void MoveSound( const char *soundname, const char *newscript ) = 0; + virtual void RenameSound( const char *soundname, const char *newname ) = 0; + + virtual void UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params ) = 0; + + virtual int GetNumSoundScripts() const = 0; + virtual char const *GetSoundScriptName( int index ) const = 0; + virtual bool IsSoundScriptDirty( int index ) const = 0; + virtual int FindSoundScript( const char *name ) const = 0; + virtual void SaveChangesToSoundScript( int scriptindex ) = 0; + + virtual void ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename ) = 0; + virtual gender_t GetActorGender( char const *actormodel ) = 0; + virtual void GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen ) = 0; + virtual void GenderExpandString( gender_t gender, char const *in, char *out, int maxlen ) = 0; + virtual bool IsUsingGenderToken( char const *soundname ) = 0; + + // For blowing away caches based on filetimstamps of the manifest, or of any of the + // .txt files that are read into the sound emitter system + virtual unsigned int GetManifestFileTimeChecksum() = 0; + + // Called from both client and server (single player) or just one (server only in dedicated server and client only if connected to a remote server) + // Called by LevelInitPreEntity to override sound scripts for the mod with level specific overrides based on custom mapnames, etc. + virtual void AddSoundOverrides( char const *scriptfile, bool bPreload = false ) = 0; + + // Called by either client or server in LevelShutdown to clear out custom overrides + virtual void ClearSoundOverrides() = 0; + + virtual bool GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHANDLE& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ) = 0; + virtual soundlevel_t LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHANDLE& handle ) = 0; + + virtual void ReloadSoundEntriesInList( IFileList *pFilesToReload ) = 0; + + // Called by either client or server to force ModShutdown and ModInit + virtual void Flush() = 0; +}; +*/ + +#define GET_FIELD_WITH_COMPATIBILITY( L, ArgumentIndex, FieldName, FallbackFieldName ) \ + lua_getfield( L, ArgumentIndex, FieldName ); \ + if ( lua_isnil( L, -1 ) ) \ + { \ + lua_pop( L, 1 ); /* pop the nil value */ \ + lua_getfield( L, ArgumentIndex, FallbackFieldName ); \ + } + +#define CHECK_FIELD_OR_ERROR( L, ArgumentIndex, FieldName, CheckFunction ) \ + if ( !CheckFunction( L, -1 ) ) \ + { \ + luaL_argerror( L, ArgumentIndex, "expected field '" FieldName "'" ); \ + return 0; \ + } + +#define GET_FIELD_WITH_COMPATIBILITY_OR_ERROR( L, ArgumentIndex, FieldName, FallbackFieldName, CheckFunction ) \ + lua_getfield( L, ArgumentIndex, FieldName ); \ + if ( lua_isnil( L, -1 ) ) \ + { \ + lua_pop( L, 1 ); /* pop the nil value */ \ + lua_getfield( L, ArgumentIndex, FallbackFieldName ); \ + } \ + CHECK_FIELD_OR_ERROR( L, ArgumentIndex, FieldName, CheckFunction ) + +/* + Sounds.Add({ + name = "big_explosion", + channel = CHAN_STATIC, + volume = 1.0, + level = 80, + pitch = {95, 110}, + sound = "phx/explode03.wav" + }) +*/ +LUA_BINDING_BEGIN( Sounds, Add, "library", "Creates a sound script." ) +{ + if ( !LUA_BINDING_ARGUMENT( lua_istable, 1, "soundData" ) ) + { + luaL_argerror( L, 1, "expected table" ); + return 0; + } + + CSoundParametersInternal parameters; + + // For gmod compat we check both lowecase and UpperCamelCase + GET_FIELD_WITH_COMPATIBILITY_OR_ERROR( L, 1, "Name", "name", lua_isstring ); + const char *name = luaL_checkstring( L, -1 ); + lua_pop( L, 1 ); // pop the name value + + GET_FIELD_WITH_COMPATIBILITY_OR_ERROR( L, 1, "Channel", "channel", lua_isnumber ); + parameters.SetChannel( luaL_checknumber( L, -1 ) ); + lua_pop( L, 1 ); // pop the channel value + + GET_FIELD_WITH_COMPATIBILITY( L, 1, "Level", "level" ); + if ( lua_isnumber( L, -1 ) ) + parameters.SetSoundLevel( luaL_checknumber( L, -1 ) ); + else + parameters.SetSoundLevel( SNDLVL_NORM ); + lua_pop( L, 1 ); // pop the level value + + GET_FIELD_WITH_COMPATIBILITY( L, 1, "Volume", "volume" ); + if ( lua_isnumber( L, -1 ) ) + parameters.SetVolume( luaL_checknumber( L, -1 ) ); + else + parameters.SetVolume( 1.0f ); + lua_pop( L, 1 ); // pop the volume value + + GET_FIELD_WITH_COMPATIBILITY( L, 1, "Volume", "volume" ); + if ( lua_istable( L, -1 ) ) + { + lua_rawgeti( L, -1, 1 ); + lua_rawgeti( L, -2, 2 ); + parameters.SetVolume( luaL_checknumber( L, -2 ), luaL_checknumber( L, -1 ) ); + lua_pop( L, 2 ); // pop the volume values + } + else if ( lua_isnumber( L, -1 ) ) + { + parameters.SetVolume( luaL_checknumber( L, -1 ), 0 ); + } + else + { + parameters.SetVolume( 1.0f, 0 ); + } + lua_pop( L, 1 ); // pop the volume value + + GET_FIELD_WITH_COMPATIBILITY( L, 1, "Pitch", "pitch" ); + if ( lua_istable( L, -1 ) ) + { + lua_rawgeti( L, -1, 1 ); + lua_rawgeti( L, -2, 2 ); + parameters.SetPitch( luaL_checknumber( L, -2 ), luaL_checknumber( L, -1 ) ); + lua_pop( L, 2 ); // pop the pitch values + } + else if ( lua_isnumber( L, -1 ) ) + { + parameters.SetPitch( luaL_checknumber( L, -1 ), 0 ); + } + else { + parameters.SetPitch( 100, 0 ); + } + lua_pop( L, 1 ); // pop the pitch value + + // TODO: Check if the file needs to exist, or if we can just create a sound script without a file + soundemitterbase->AddSound( name, "scripts/sounds/lua_procedural.txt", parameters ); + + return 0; +} +LUA_BINDING_END() + +/* +** Open Sounds library +*/ +LUALIB_API int luaopen_Sounds( lua_State *L ) +{ + LUA_REGISTRATION_COMMIT_LIBRARY( Sounds ); + + return 1; +} diff --git a/src/public/lsounds.h b/src/public/lsounds.h new file mode 100644 index 0000000000..134bf7401b --- /dev/null +++ b/src/public/lsounds.h @@ -0,0 +1,7 @@ +#ifndef LSOUNDS_H +#define LSOUNDS_H +#ifdef _WIN32 +#pragma once +#endif + +#endif // LSOUNDS_H diff --git a/src/public/lvphysics_interface.cpp b/src/public/lvphysics_interface.cpp index eee6a63f5c..d429875a3f 100644 --- a/src/public/lvphysics_interface.cpp +++ b/src/public/lvphysics_interface.cpp @@ -1345,13 +1345,6 @@ LUA_BINDING_BEGIN( PhysicsSurfacePropertiesHandle, GetSurfaceIndex, "class", "Ge } LUA_BINDING_END( "integer", "The surface index." ) -LUA_BINDING_BEGIN( PhysicsSurfacePropertiesHandle, ParseSurfaceData, "class", "Parses surface data" ) -{ - lua_pushinteger( L, physprops->ParseSurfaceData( LUA_BINDING_ARGUMENT( luaL_checkstring, 1, "stringData" ), LUA_BINDING_ARGUMENT( luaL_checkstring, 2, "textFileName" ) ) ); - return 1; -} -LUA_BINDING_END( "integer", "The surface data." ) - LUA_BINDING_BEGIN( PhysicsSurfacePropertiesHandle, SurfacePropCount, "class", "Gets the surface property count" ) { lua_pushinteger( L, physprops->SurfacePropCount() ); @@ -1384,6 +1377,13 @@ LUA_BINDING_BEGIN( PhysicsSurfaceProperties, GetGlobal, "library", "Gets the glo } LUA_BINDING_END( "PhysicsSurfacePropertiesHandle", "The global surface properties." ) +LUA_BINDING_BEGIN( PhysicsSurfaceProperties, ParseSurfaceData, "class", "Parses surface data" ) +{ + lua_pushinteger( L, physprops->ParseSurfaceData( LUA_BINDING_ARGUMENT( luaL_checkstring, 1, "stringData" ), LUA_BINDING_ARGUMENT( luaL_checkstring, 2, "textFileName" ) ) ); + return 1; +} +LUA_BINDING_END( "integer", "The surface data." ) + /* ** Open IPhysicsSurfaceProps object */ diff --git a/src/public/soundflags.h b/src/public/soundflags.h index 71ae119299..433d83a09e 100644 --- a/src/public/soundflags.h +++ b/src/public/soundflags.h @@ -16,7 +16,7 @@ //----------------------------------------------------------------------------- // channels //----------------------------------------------------------------------------- -enum +enum SOUND_CHANNEL { CHAN_REPLACE = -1, CHAN_AUTO = 0, diff --git a/src/public/vphysics/lperformance.cpp b/src/public/vphysics/lperformance.cpp index 42b378404f..42debe700a 100644 --- a/src/public/vphysics/lperformance.cpp +++ b/src/public/vphysics/lperformance.cpp @@ -57,20 +57,36 @@ LUA_API lua_Physics_performanceparams_t lua_toperformanceparams( lua_State *L, i LUA_API void lua_pushperformanceparams( lua_State *L, lua_Physics_performanceparams_t *pOutput ) { lua_newtable( L ); + + lua_pushnumber( L, pOutput->lookAheadTimeObjectsVsWorld ); + lua_setfield( L, -2, "LookAheadTimeObjectsVsWorld" ); + lua_pushnumber( L, pOutput->lookAheadTimeObjectsVsObject ); + lua_setfield( L, -2, "LookAheadTimeObjectsVsObject" ); + lua_pushinteger( L, pOutput->maxCollisionChecksPerTimestep ); - lua_setfield( L, -2, "maxCollisionsPerObjectPerTimestep" ); + lua_setfield( L, -2, "MaximumCollisionsPerObjectPerTimestep" ); lua_pushinteger( L, pOutput->maxCollisionChecksPerTimestep ); - lua_setfield( L, -2, "maxCollisionChecksPerTimestep" ); + lua_setfield( L, -2, "MaximumCollisionChecksPerTimestep" ); + lua_pushnumber( L, pOutput->maxAngularVelocity ); + lua_setfield( L, -2, "MaximumAngularVelocity" ); lua_pushnumber( L, pOutput->maxVelocity ); - lua_setfield( L, -2, "maxVelocity" ); + lua_setfield( L, -2, "MaximumVelocity" ); + lua_pushnumber( L, pOutput->minFrictionMass ); + lua_setfield( L, -2, "MinimumFrictionMass" ); + lua_pushnumber( L, pOutput->maxFrictionMass ); + lua_setfield( L, -2, "MaximumFrictionMass" ); + + // GMOD compatibility naming: + lua_pushinteger( L, pOutput->maxCollisionChecksPerTimestep ); + lua_setfield( L, -2, "MaxCollisionsPerObjectPerTimestep" ); + lua_pushinteger( L, pOutput->maxCollisionChecksPerTimestep ); + lua_setfield( L, -2, "MaxCollisionChecksPerTimestep" ); lua_pushnumber( L, pOutput->maxAngularVelocity ); - lua_setfield( L, -2, "maxAngularVelocity" ); - lua_pushnumber( L, pOutput->lookAheadTimeObjectsVsWorld ); - lua_setfield( L, -2, "lookAheadTimeObjectsVsWorld" ); - lua_pushnumber( L, pOutput->lookAheadTimeObjectsVsObject ); - lua_setfield( L, -2, "lookAheadTimeObjectsVsObject" ); + lua_setfield( L, -2, "MaxAngularVelocity" ); + lua_pushnumber( L, pOutput->maxVelocity ); + lua_setfield( L, -2, "MaxVelocity" ); lua_pushnumber( L, pOutput->minFrictionMass ); - lua_setfield( L, -2, "minFrictionMass" ); + lua_setfield( L, -2, "MinFrictionMass" ); lua_pushnumber( L, pOutput->maxFrictionMass ); - lua_setfield( L, -2, "maxFrictionMass" ); + lua_setfield( L, -2, "MaxFrictionMass" ); }