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" );
}