Compare commits

...

703 Commits

Author SHA1 Message Date
BotoX
313c555e2e Add "Restart Current Map" to sm_map menu. 2024-06-09 17:45:27 +02:00
BotoX
e5fed0051b Fix GetClientCount(false) 2024-06-09 17:45:27 +02:00
BotoX
c2a86c77fd Add damageCustom argument to SDKHooks_TakeDamage native. 2024-06-09 17:45:24 +02:00
BotoX
8c534e770b fix IsMapValid behavior change by a2246af121 2024-06-09 17:41:10 +02:00
BotoX
2c86e8099a Add OnEntitySpawned to SDKHooks. 2024-06-09 17:41:10 +02:00
BotoX
03eeb23ab3 Implement per-client randomized menus with MenuShufflePerClient native.
Add MenuSetClientMapping native.
2024-06-09 17:41:05 +02:00
BotoX
a9c85d8842 Changes on sm_*say 2024-06-09 17:39:34 +02:00
BotoX
850ee60a18 Add GetClientIClient native. 2024-06-09 17:39:34 +02:00
Obuss
7cd130904b Logging changes to various base plugins. 2024-06-09 17:39:30 +02:00
BotoX
19019d3a4b Extend function calling API for natives and allow catching exceptions.
Change sourcepawn url.
2024-06-09 17:34:33 +02:00
BotoX
b31375f087 Fix @spec not targeting clients in unassigned team. 2024-06-09 17:33:19 +02:00
BotoX
1d999c3984 Added hack to make plugins open a menu with all possible targets on ReplyToTargetError COMMAND_TARGET_AMBIGUOUS.
Explanation:
There are two clients in the server, one named gene, the other one "Ene ~special characters~".
An admin issues "sm_slay Ene" and gets following error message: More than one client matched the given pattern.
What this hack will do is: Use GetCmdArg(0, ...); to get the command name "sm_slay".
Use GetCmdArgString(...); to get the arguments supplied to the command.
Use GetLastProcessTargetString(...); (which was implemented in this commit) to retrieve the arguments that were passed to the last ProcessTargetString call.
It will then pass this data to the DynamicTargeting plugin through its AmbiguousMenu native.
The plugin will open up a menu on the client and list all targets which match the pattern that was supplied to ProcessTargetString.
If the client selects a menu entry, FakeClientCommand will be used to re-execute the command with the correct target.
2024-06-09 17:33:16 +02:00
BotoX
fdcbdb6918 Add more macros to CDetour. 2024-06-09 17:32:32 +02:00
Headline
d42a8c7c1e
Move Safetyhook mess into a mirror repo (#2163)
* Bring in safetyhook mirror repo

* Add proper submodule

* Update submodule for linux fixes

* Update submodule again

* Change the method of compiling safetyhook (#2167)

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>

* Update submodule

* Update submodule for -fPIC

---------

Co-authored-by: Benoist <14257866+Kenzzer@users.noreply.github.com>
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-06-08 20:05:29 +00:00
Benoist
11d3cf94f7
Add GetFilePermissions (#2177)
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-06-08 14:17:50 +00:00
Nick Hastings
0e4b1ca4cf Revert "Update sourcepawn submodule (#2146)"
This reverts commit 1bcc5b8a44.
2024-06-08 09:26:13 -04:00
suza
1bcc5b8a44
Update sourcepawn submodule (#2146)
Update SourcePawn and AMTL.
2024-06-04 16:24:22 -07:00
Benoist
7df2f8e045
Reconcile the concept of Edict & Networkable across the codebase (#1903)
* Reconcile the concept of Edict & Networkable across the codebase

* There's no need to check this, it's done elsewhere. Also could be null (segfault)

* This was never needed

* Pseudo review changes

Re-added removed null checks, and added new ones.

Changed the error messages in Get/SetProp natives to better reflect reality.

* Don't change the behaviour of GetEntityNetClass

* Overload IGameHelpers::FindServerClass

* Make error messages more accurate

* Fix a dev comment

* Rename FindServerClass

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-06-01 14:07:55 +00:00
1mpulse
9f3584a056
fix VDECODE_FLAG_ALLOWNOTINGAME flag (#2000)
Corrects the check for the VDECODE_FLAG_ALLOWNOTINGAME flag
2024-06-01 13:57:07 +00:00
Benoist
4e73713fab
Fix rare crash with logs (#2161)
* Fix rare crash with logs

* Nullcheck instead

* Update smn_filesystem.cpp

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-05-24 13:10:24 +02:00
Benoist
e07c120cab
CDetour safetyhook (#2162)
* Add safetyhook, remove libudis86

Co-authored-by: bottiger1 <55270538+bottiger1@users.noreply.github.com>

* Add modified CDetour

Co-authored-by: bottiger1 <55270538+bottiger1@users.noreply.github.com>

* Add CDetour [Safetyhook] to build script

* Re-enable loader/core/corelogic, and fix new C++20 error

* Reenable all extensions (except dhooks)

* Make cstrike compile against new CDetour

* Remove unused variable in sdktools output?

* Make sdktools compile against new cdetour

* Downgrade to C++17

* remove auto

* fix compilation on linux

* Re-enable dhooks

* Re-authorise old compilers

* Fix invalid downgrade of std::optional

* readd libudis86 for dhooks only

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
Co-authored-by: bottiger1 <55270538+bottiger1@users.noreply.github.com>
2024-05-21 01:53:44 +00:00
Kaela
e3734803f0
sdkhooks: retain vtable hook until level end (#2094)
* Band-aid for Bulk SDKHooks performance impact on linux

Platform specific changes.

* Oops forgot about header

* Simplified/improved LevelShutdown code
2024-05-18 12:30:20 +01:00
Nick Hastings
7c757ac119 Trigger build for hl2sdk-l4d2 changes 2024-05-15 09:36:23 -04:00
sappho
c5425def7f
Trigger build for L4D2 changes (#2155) 2024-05-14 17:09:03 -07:00
ojlanders
e60c672485
Add OnServerHibernationUpdate forward (closes #1483) (#2151)
* Add OnServerHibernationUpdate forward (closes #1483)

* Clarify hibernation state

* make it 2 forwards instead of 1

---------

Co-authored-by: Odin Landers <ojl9576@rit.edu>
2024-05-07 01:44:56 +00:00
rtldg
908ffdb523
make RenameFile() on Windows also replace the file (#2060)
* make RenameFile() on Windows also replace the file

* Add MOVEFILE_COPY_ALLOWED to MoveFileExA
2024-05-05 22:20:46 +00:00
Rushaway
d56dc62e03
i18n: Czech Translations (#2120) 2024-05-05 21:19:11 +00:00
Kit o' Rifty
f08f1ef0d6
Fix passing variant_t parameters + fix codegen stack on Win64 (#2117)
* Fix 64-bit JIT not allocating stack space for params on stack
Fix wrong cond check for passing objects by value

* Fix wrong FireOutput detour proto on win64
2024-05-05 21:17:59 +00:00
bottiger1
5cb2d68fd7
AcceptEntityInput 64bit fix (#2149)
* Fix AcceptEntityInput on 64 bit by using struct from SDK instead of pointer math.

* Fix from Malifox. Not sure what it does but it is supposedly related to the AcceptEntityInput crash.

* Fix episode1 and darkm sdk paths
2024-05-05 20:06:06 +00:00
caxanga334
96d0959db1
Update HL2SDK Manifests Submodule (#2150) 2024-05-05 17:14:15 +00:00
Artvin
5a6b2c8ebf
64bit gamedata fixes (#2141)
* save as of now

* save as of now

* progress

* hint towards comments

Please for the love of god, put comments for updating these in the future will be even more annoying.
i dont even know what LevelShutdown it wants.

* forgot

* fix

* fixes

* Update engine.ep2v.txt

* Update engine.ep2v.txt

* Update sm-tf2.games.txt

---------

Co-authored-by: Benoist <14257866+Kenzzer@users.noreply.github.com>
Co-authored-by: Nicholas Hastings <nshastings@gmail.com>
2024-05-05 16:13:39 +00:00
Adrian-Stefan Mares
343f64f6b7
Fix IsClientSourceTV in L4D2 with a custom tv_name (#2143) 2024-04-29 00:17:10 +01:00
Oliver John Hitchcock
f9ad35badf
Stop EntRefToEntIndex returning garbage if a bad parameter is passed (#1323)
* Stop EntRefToEntIndex returning garbage if a bad parameter is passed

Seen multiple bad usage of this function that works only because whatever was passed in was returned as it wasnt an entity reference.
This code should have worked and would be expected to have returned something invalid but instead the the input was returned which allowed the code to work when really it is bad code.
See for one such case https://discordapp.com/channels/335290997317697536/335290997317697536/736518488314871868

* Update documentation of EntRefToEntIndex

Added the error text saying what shall be returned when a invalid parameter is passed.

* Validate entity index instead of just returning INVALID_EHANDLE_INDEX

Not sure if it needs this much validation but this just mirrors how IsValidEntity works, so the entity index returned should be valid else INVALID_EHANDLE_INDEX is returned.

* EntRefToEntIndex improve doc comments to better represent functionality

---------

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2024-04-25 23:19:04 +00:00
Nick Hastings
60b6a8dfb0 Revert "Trigger build for hl2sdk-tf2 changes"
This reverts commit ee51162c3f.
2024-04-22 21:31:14 -04:00
Nick Hastings
00f3970036 Revert "Trigger build for hl2sdk-tf2 changes"
This reverts commit 123ad7aab1.
2024-04-22 18:24:19 -04:00
Alienmario
7d78c141e7
Fix SDKHooks IEntityListener (#2138) 2024-04-21 19:21:47 +00:00
Nick Hastings
123ad7aab1 Trigger build for hl2sdk-tf2 changes
(https://github.com/alliedmodders/hl2sdk/issues/231)
2024-04-21 12:04:44 -04:00
Nick Hastings
ee51162c3f Trigger build for hl2sdk-tf2 changes
https://github.com/alliedmodders/hl2sdk/issues/231
2024-04-20 14:04:26 -04:00
Adrian-Stefan Mares
af93d819c6
Handle INVALID_FUNCTION mismatches at plugin boundaries, redux (#2136) 2024-04-19 21:56:48 -04:00
CookieCat
d426d1f04f
x32 Windows Gamedata Fixes for 4/18/2024 TF2 Update (#2137)
* sm-tf2-games

* FireOutput
2024-04-19 18:00:30 -07:00
ojlanders
6cac489fce
Prevent recursion in sm_ServerCommandEx (fixes #967) (#2133)
* Prevent recursion in sm_ServerCommandEx (fixes #967)

* Update core/smn_console.cpp to fix style

Co-authored-by: Erin <asherkin@limetech.io>

---------

Co-authored-by: Odin Landers <ojl9576@rit.edu>
Co-authored-by: Erin <asherkin@limetech.io>
2024-04-18 11:42:37 +00:00
Peak
5c507cc35c
Implement File.Size (#2131)
* Implement file size method for already open files

* Switch from fseek/ftell to fileno/fstat size reading
2024-04-13 18:14:52 +00:00
Rain
dbec0b165c
Neotokyo: Add "SetOwnerEntity" gamedata (#2126)
This adds game support for `SetOwnerEntity`, `SetEntityCollisionGroup`.
2024-03-21 00:11:59 +00:00
Rushaway
4e8b66bf99
fix: sorted alphabetically, remove duplicate 'ko' (#2121)
* fix: sorted alphabetically, remove duplicate 'ko'

* 'KoreanA' is a valid value
2024-03-05 01:20:40 +00:00
CookieCat
8dcbe14ea0
Throw an error when attempting to remove worldspawn in RemoveEntity/RemoveEdict (#2104)
* Check for worldspawn in RemoveEntity and RemoveEdict

* A little more info
2024-02-20 16:14:00 +01:00
Peak
06dcb991e8
Allow topmenu config file to sort with subcategories (#1757) 2024-02-18 11:46:48 +01:00
Mart
e262064fd8
Update game.nucleardawn.txt (#2116)
add missing GetAttachment offset for ND
2024-02-14 23:22:26 +00:00
dependabot[bot]
a80b9c5c0f
Bump actions/cache from 3 to 4 (#2103)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 14:01:26 +01:00
Benoist
638aae0040
Fix sdktools gamedata (#2113)
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-02-08 00:02:01 +00:00
Benoist
35ac74c7a7
Update gamedata for l4d2 (#2111)
* update gamedata for l4d2

* undo findentitybyclassname sig change

* update gEntList offset accordingly

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-02-02 21:10:42 +00:00
Ryan Blackham
7bb9da2a26
Fixed typo in reservedslots.sp (#2109) 2024-02-01 10:36:28 +01:00
Benoist
b5b9b8388c
Fixup entinfo offset + sdktools tf2 x64 gamedata (#2108)
* fixup entinfo offset

* added missing tf gamedata

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-01-29 01:08:33 +00:00
Benoist
e7e92d4e40
update manifests (#2106)
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-01-26 21:18:14 +00:00
Benoist
33e869a4e5
Update gamedata for TF2 x64 (#2105)
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2024-01-26 13:30:08 +00:00
NotnHeavy
0bc9aa46e6
Update checkout-deps files to install hl2sdk-mock (#2102) 2024-01-22 15:30:15 +00:00
Angel Bot
80ad76a563
Fix teleportation offsets for tf2classic (#2100) 2024-01-01 23:55:23 +00:00
NotnHeavy
7e29f22f8f
Fix typo in ArrayList.Clone docs (#2098)
very useful update of: grammar in comment
2023-12-22 20:35:26 +01:00
dani
a9eb3a0d45
Add gamedata for Pre-Fortress 2 (#2091)
* Added gamedated for pf2

* added support for beta testing

---------

Co-authored-by: Nicholas Hastings <nshastings@gmail.com>
2023-12-18 23:59:41 +00:00
Ivan Zaitsev
ab6b45b81c
Add void function prototypes to NativeCall and MenuHandler (#2081)
* Add void prototype to NativeCall typeset

`void` prototype can be used for simple natives which don't return any value.
This is done to silence compiler warnings 209 (`function has explicit 'int' tag but does not return a value`) and 242 (`function "NativeCallback" should return an explicit value`).

* Make MenuHandler into typeset and add void prototype

This can be used for basic menu handlers that don't return modified item styles and don't redraw menu items.
2023-12-18 23:57:58 +00:00
bt
385eae892c
Add gmod9 gamedata. (#2093) 2023-12-18 23:52:54 +00:00
dependabot[bot]
5f309f45f8
Bump actions/setup-python from 4 to 5 (#2095)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 23:18:36 +00:00
dependabot[bot]
a2e5e2a442
Bump actions/upload-artifact from 3 to 4 (#2097)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 23:17:52 +00:00
Nicholas Hastings
b42e5ac922
Convert AMBuild scripts to use hl2sdk-manifests (#2096)
* Add hl2sdk-manifests submodule

* Update AMBuild scripts to utilize hl2sdk-manifests

* Add build*/ to .gitignore
2023-12-18 23:15:44 +00:00
Nick Hastings
544204654d Hack fix for broken --gen=vs 2023-12-16 12:19:10 -05:00
sappho
5c971fd51b
add gamedata for tf2classic (#2088)
* add gamedata skeleton for tf2classic

* Update game.tf2classic.txt

* Update game.tf2classic.txt

---------

Co-authored-by: azzy <67557558+azzyr@users.noreply.github.com>
2023-12-05 00:50:08 +00:00
B3none
6507d947cc Updated sourcepawn.php to latest array syntax 2023-11-30 21:58:49 -08:00
B3none
30415e299e Updated ProfFileParser.class.php to PSR-12 2023-11-30 21:58:49 -08:00
Mart
8eeea2aba1
Update funcommands.games.txt (#2085)
Changes for Nuclear Dawn

*Correct beacon team colors (Consortium = blue / Empire = red)
*Change beacon .vmt sprite to an existing one similar to the usual "laser.vmt" (white and 32x64 size)
2023-11-28 21:59:54 +00:00
Nick Hastings
bb1431cd3b Fix Windows FireOutput signature for Nuclear Dawn (fixes #2084) 2023-11-27 09:53:25 -05:00
rop_rop
a2aec9b42e
Update Hebrew translation phrases (#2050)
* Update basebans.phrases.txt

added all the missing Phrase

* Update basebans.phrases.txt

update of the missing phrase as a parameter
2023-11-24 16:56:14 +01:00
Nick Hastings
ef74b51c8b Probably fix build 2023-11-20 19:54:41 -05:00
Nick Hastings
e74aa099bc Add debugging to bootstrap script 2023-11-20 19:40:09 -05:00
Nick Hastings
8140cb2d6b Tweak build script 🐍 2023-11-20 19:37:22 -05:00
Nick Hastings
d981e252d2 Fix regression that breaks loading on mcv, blade, and csgo (fixes #2082) 2023-11-20 09:23:02 -05:00
Nick Hastings
e6a04abc9e Remove unused var 2023-11-20 09:21:00 -05:00
Nick Hastings
c90477a720 Fix warning in build script 2023-11-20 09:20:38 -05:00
Benoist
11c8084ccd
Deprecate CreateInterface (#2001)
* Deprecate CreateInterface

Co-authored-by: KitRifty <4492504+KitRifty@users.noreply.github.com>

* Expose the 'newer' data

* Update smsdk_ext.cpp

---------

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
Co-authored-by: KitRifty <4492504+KitRifty@users.noreply.github.com>
2023-11-11 16:59:22 +00:00
Nick Hastings
7747beb4cf Add remaining SDKHooks gamedata for Dystopia 2023-11-07 19:35:34 -05:00
Rain
9fa7421122
Update gamedata for Dystopia (#2080)
Add the offsets for OnTakeDamage and OnTakeDamage_Alive for the game Dystopia, for Linux and Windows platforms. This change adds game support for the SDKHook_OnTakeDamage, and its variants.
2023-11-07 13:20:45 +00:00
bauxite
f2ca671d1f
Update basetriggers.sp for Dystopia compatibility (#2075)
* Update basetriggers.sp for Dystopia compatibility

Dystopia has a built-in command called nextmap which breaks due to the Sourcemod nextmap command. This modification is intended to restore Dystopia compatibility.

* Update basetriggers.sp

* Update basetriggers.sp

* Update basetriggers.sp

* Update basetriggers.sp
2023-11-06 16:10:46 +00:00
sappho
d4da21b07f
Add more context to Format etc documentation (#2079) 2023-11-06 16:07:00 +00:00
David Anderson
4b2d8b53fa Fix macOS scripting SDK build.
This also enables universal spcomp binaries with arm64 support.
2023-11-04 23:01:44 -07:00
David Anderson
7bfd5e521e Update SourcePawn.
This updates float.inc and smn_float.cpp to compensate for alias
removal.
2023-11-03 17:46:48 -10:00
David Anderson
060c832f89 Update SourcePawn.
This brings in a few breaking changes.

One, INVALID_FUNCTION is now 0 instead of -1. This is long overdue.
Plugins should transparently work except in two cases:

  1. Third-party extensions that have a hardcoded test for -1 will no
     longer work. A new API has been provided for this,
     GetFunctionByIdOrNull.
  2. If a plugin "framework" uses INVALID_FUNCTION anywhere in its
     exported API, then all plugins using that framework need to be
     recompiled together, so they agree on the value of
     INVALID_FUNCTION.

Hopefully the damage here is minimal. The core plugin version has been
bumped to 7 to try and limit conflicts.

Second, braceless functions are no longer supported. There wasn't really
any way around this and it's better to bite the bullet now. This affects
source compatibility, but not binary compatibility.

Third, the "using" keyword is no longer implemented. SourceMod now has a
Handle methodmap again. Plugins compiled against this new methodmap will
require a "Handle.~Handle" native, which 1.12 now provides.
2023-11-02 18:38:30 -10:00
PlayBoy31
8c9fd3746b
Fix missing submodules (#2074)
* add --recursive for missing submodules
2023-11-02 21:02:15 -07:00
Nick Hastings
314184beea Merge branch 'master' of https://github.com/alliedmodders/sourcemod 2023-10-21 19:03:09 -04:00
Nicholas Hastings
17462cbe5a Fix build using wrong dir in bootstrapping 2023-10-21 19:02:51 -04:00
David Anderson
ae4e043c3e Enable C++17. 2023-10-21 19:02:51 -04:00
David Anderson
6def79ff23
Merge pull request #2063 from alliedmodders/cxx17
Enable C++17.
2023-10-19 20:33:14 -07:00
Jonathan
1ba7024a3a
Fix Japanese translations (#2053) 2023-10-17 12:50:55 +02:00
rop_rop
44aaaf8b10
Update Hebrew playercommands.phrases.txt (#2051)
added the missing Phrase
2023-10-17 12:35:36 +02:00
XeroX
62754333cd
Increase lastMap buffer (#2062)
Fixes #2061
2023-10-17 12:31:04 +02:00
Rain
8938a29904
Ignore chat triggers for interactive ban reason (#2067)
* Ignore chat triggers for interactive ban reason

If the admin types a chat trigger for a command while BaseBans is
waiting for them to type a ban reason, don't treat that message as the
ban reason.

This fixes a problem where the admin decides to cancel their ban with
"!abortban", "/abortban" etc, but ends up accidentally banning the
player with the ban reason "!abortban" (or by attempting to access any
other command via a chat trigger before typing the ban reason).

* Optimize

Check if we're actually waiting for chat reason before calling the native
2023-10-16 19:43:00 +02:00
Nick Hastings
2c6ef5825f Update from Clang 13 to Clang 14 for Linux build 2023-10-16 13:21:02 -04:00
sappho
b090aa36bf
Add support for open fortress (#2057)
* Run timers every tick instead of arbitrarily on 100ms thinks, adding a significantly higher amount of precision

* undo force push

* add open fortress support

* undo leftover timersys changes

* Add fireoutput sig and remove engine gamedata

* Update master.games.txt

* Update master.games.txt
2023-10-16 00:29:44 +00:00
David Anderson
cb1dae5aab Fix minimal rebuilds on buildbot. 2023-10-13 21:28:33 -07:00
David Anderson
c9e824632f Enable C++17. 2023-10-13 21:25:24 -07:00
David Anderson
0436fb0ab3 Fix linkage. 2023-10-13 20:59:55 -07:00
David Anderson
767c5cf468 Fix build. 2023-10-13 15:32:45 -07:00
David Anderson
f01ad5317a Fix linker issues on Linux. 2023-10-13 15:26:05 -07:00
David Anderson
fac3138ff5 Fix typo. 2023-10-13 14:14:47 -07:00
David Anderson
ab9e86910c Switch bootstrap.pl to Python. 2023-10-13 14:04:56 -07:00
peace-maker
e88e9342ef
Update SourcePawn (#2052)
* Update SourcePawn

* Fix using SourceMod version as SourcePawn version
2023-10-06 22:47:11 +02:00
dysphie
e3a01b0e1a
Update game.nmrih.txt (#2056) 2023-10-02 23:24:09 +00:00
Kit o' Rifty
07d928dbad
Fix sound flags enumeration (#2040)
* Fix sound flags

* Change to bit shifts like in SDK
2023-09-27 17:03:03 +02:00
Boink
a402b3cceb
Prevent commands from being run on the client with sm_play (#1832)
* Prevent command injection

* Empty to commit to try to kick CI.

* Improve filename sanitisation

---------

Co-authored-by: Fyren <fyrenmoo@gmail.com>
2023-09-27 16:51:07 +02:00
幸運心
99dbe06d8a
Japanese translations (#2042)
* Japanese translations

Japanese translation corrections.

* Fix phrase format parameter index off-by-one

---------

Co-authored-by: peace-maker <peacemakerctf@gmail.com>
2023-09-27 16:36:11 +02:00
Mikusch
29694ec13b
Add block parameter to various ArrayList functions (#1656)
* Add block parameter to FindString

* Add block parameter for GetString & GetArray

* Fix buffer overflow issues

* Fix wrong return

* Add size parameter to SetArrayString
2023-09-27 16:05:43 +02:00
Mikusch
1e8db957bf
Add more functions to ArrayStack (#2019)
* Add more functions to ArrayStack

* Make formatting consistent with other functions

* Don't remove the element for TopArray

* Replace 4 with sizeof(cell_t)
2023-09-27 15:43:04 +02:00
Rain
0656696251
Neotokyo: Add gamedata for LookupEntityAttachment & GetEntityAttachment (#2049)
Add the "LookupAttachment" and "GetAttachment" signature and offsets for
Neotokyo, which adds game support for the SM LookupEntityAttachment and
GetEntityAttachment natives.
2023-09-25 22:19:47 +00:00
dependabot[bot]
af04c964fe
Bump tibdex/github-app-token from 1.8.0 to 2.1.0 (#2048)
Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1.8.0 to 2.1.0.
- [Release notes](https://github.com/tibdex/github-app-token/releases)
- [Commits](b62528385c...3beb63f4bd)

---
updated-dependencies:
- dependency-name: tibdex/github-app-token
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 17:35:50 +02:00
Nick Hastings
bc81361319 MCV: fix Linux tier0/vstdlib bin names 2023-09-24 13:31:55 -04:00
Nicholas Hastings
853bcf703a
Update MCV platform support. (#2046) 2023-09-24 16:23:10 +00:00
dependabot[bot]
c81248004a
Bump actions/checkout from 3 to 4 (#2045)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-13 18:29:18 +02:00
throwaway47777
79787cb868
Fix admin-flatfile.sp includes (#2043)
* Move admin-flatfile to plugins dir

* Fix admin-flatfile sp includes
2023-09-13 18:28:35 +02:00
peace-maker
20aae06f12
NPOTB: Update Github Actions using dependabot (#2035) 2023-07-31 23:38:49 -07:00
hydrogen-mvm
a20eb08dc6
Fix replay workaround not triggering (#2033) 2023-07-29 20:53:17 +00:00
Nicholas Hastings
8d60a46358
Revert "Update sourcepawn (#2011)" (#2032)
This reverts commit cb8541548b.
2023-07-29 16:01:25 +00:00
Nick Hastings
4c376cbcb7 Update PLAPI version for NormalSHook signature change (MAXPLAYERS increase) 2023-07-27 08:15:02 -04:00
sappho
d83c498a8f
[1.12] update maxplayers define for 07-25-23 tf2 update (#2026) 2023-07-26 20:13:31 +00:00
Mikusch
531b053096
TF2: Add TFCond_ImmuneToPushback condition (#2023) 2023-07-26 02:10:08 +00:00
Mikusch
0dafe04899
TF2: Update StunPlayer signature (#2024) (fixes #2022) 2023-07-26 02:08:48 +00:00
rtldg
7170a8f11c
Fix typo in DHookRaw docs (#2020) 2023-07-21 09:58:14 +02:00
peace-maker
2ef874dbc9
Run basic native tests using hl2sdk-mock in CI (#2015)
This tests the sorting.inc API similar to the existing test plugin in
the plugins/testsuite folder. Tests fail if the srcds output contains "FAIL".
2023-07-12 22:58:28 +02:00
peace-maker
cb8541548b
Update sourcepawn (#2011)
* Update sourcepawn

* Remove #pragma rational Float
2023-07-12 22:55:40 +02:00
peace-maker
46e3de8d24
Fix minor translation typos (#2017)
No need to know the languages for those.
2023-07-12 13:38:12 +02:00
Corey D
5addaffa56
Add ParseTime (strptime) native (#1697)
* Add GetTimeStamp (strptime) native

* Fix description

* Fix typos

* Fix issues

* Rewrite ParseTime

* Fix docstrings

* Fix ParseTime

* Clarify docs
2023-07-04 13:42:45 +02:00
Erin
f40ae82df4
Clear sm_nextmap so we don't get stuck in a loop (#1545)
* Pure C++ solution

* Pure SourcePawn solution
2023-07-03 20:48:01 +02:00
Dominik Adrian Grzywak
6ebf04b75d
Add missing Polish translations (#2004) 2023-06-15 10:17:39 +02:00
Lukáš Čeremeta
e93dd7af3f
Update Slovak basetriggers.phrases.txt (#2002)
* Update Slovak basetriggers.phrases.txt

Added missing phrase

* Update basetriggers.phrases.txt

* Update basetriggers.phrases.txt

Remove #format
2023-06-13 10:11:23 +02:00
Lukáš Čeremeta
4dc2383fe3
Update Slovak nominations.phrases.txt (#2003)
* Update Slovak nominations.phrases.txt

Added missing phrase

* Update nominations.phrases.txt

* Update nominations.phrases.txt

Remove #format
2023-06-13 10:10:50 +02:00
bottiger1
5f827089d3
Fix mismatched memory release in regex clearmatch (#2005)
subject is created with strdup which should be freed with C free and not C++ delete
2023-06-12 22:34:57 +02:00
Kroytz
8a7f8c5cbd
Fix translation for SChinese (#1996)
* Fix Trans: funcommands

* Fix trans: mapchooser

* Add missing phrase for basecomm

* Update common.phrases.txt

* Update nominations.phrases.txt

* Update basetriggers.phrases.txt

* Incorrect param pos
2023-05-25 15:28:42 +02:00
walliski
dae8aba554
Add missing Finnish translations (#1992)
Credits to "rapu chan" and "mäyrä77" for helping with translations.
2023-05-25 15:25:46 +02:00
walliski
b9029dedff
Update Swedish translations (#1990)
* Add missing Swedish translations

* Remove extra whitespaces

Matches the whitespace around strings with what is found in the English
translations.

* Fix format of error message

Matches the English translations, which also matches what the code is
looking for.

* Change 'bana' to 'karta'

To use the same word for the English word "map" in all translations.
2023-05-25 15:24:17 +02:00
Rushaway
1a375bcc17
fix: Latvian translations (#1989)
* fix: Latvian translations

https://github.com/orgs/alliedmodders/projects/1/views/1?pane=issue&itemId=24307197

Try to solve issue: Includes a "#format" key
And Phrase missing in common.phrases.txt

* Fix + Add missing Arabic translations

https://github.com/orgs/alliedmodders/projects/1/views/1?pane=issue&itemId=24307177
Thanks to arabic teachers `Skynet #2058` & `x sooonta#8948`

* Revert "Fix + Add missing Arabic translations"

This reverts commit 899c0fb783be99020ac2bef3b824766aee485581.
2023-05-25 15:22:42 +02:00
Suza
eaccec3e4a
Add missing Italian translations (#1988)
* Update plugin.basecommands.txt

* Update common.phrases.txt

* Update nominations.phrases.txt

* Update basetriggers.phrases.txt

* Update plugin.basecommands.txt

* Update common.phrases.txt

* Update nominations.phrases.txt

* Update plugin.basecommands.txt
2023-05-25 15:21:21 +02:00
Rushaway
5fff988a8f
Add missing Portuguese translations (#1987)
https://github.com/orgs/alliedmodders/projects/1/views/1?pane=issue&itemId=24307202
Thanks to 'Ş Ŧ Ř Ɨ Ҝ € Ř'#1692 for translations
2023-05-25 15:20:23 +02:00
Rushaway
1a1bb54c4f
Add missing Danish translations (#1986)
* Add missing Danish translations

https://github.com/orgs/alliedmodders/projects/1/views/1?pane=issue&itemId=24307184

* fix: better vocabulary

Co-Authored-By: walliski <5192434+walliski@users.noreply.github.com>

---------

Co-authored-by: walliski <5192434+walliski@users.noreply.github.com>
2023-05-25 15:18:22 +02:00
Dysphie
97f2fc9d7f
Increase Cookie.GetInt/SetInt buffer size (#1995)
Current implementation does not account for negative sign or null term
2023-04-22 01:19:36 -07:00
Vauff
2e1235a585
A2S_Rules fix: Move MTU patch value to gamedata (#1829)
* A2S_Rules fix: Move MTU patch value to gamedata and increase it

* Revert MTU value back to 5k
2023-04-19 15:28:40 +00:00
Corey D
426c1a090f
Fix CommandListener ignoring Plugin_Handled (#1819) 2023-04-19 16:44:34 +02:00
Corey D
50b4ad4e11
Make KeyValues.Rewind traversal-stack clearing optional (#1993) 2023-04-17 13:10:02 +01:00
Rushaway
a330d0a1bf
Add missing french translations (#1985) 2023-04-15 18:18:44 +01:00
peace-maker
4e6774befb
Add sanity check for syntax errors in translations (#1978)
The pull request should be blocked on basic typos breaking the SMC file.
2023-04-09 19:25:50 +02:00
crashzk
b1b993989a
Update Brazilian Phrases (#1974)
* update: Translations rockthevote.phrases.txt

* update: Translations nominations.phrases.txt

* update: Translations basetriggers.phrases.txt

* update: Small changes in translations

* fix: Small corrections in the translation

* update: lower case

* fix: Minors

* fix: Translations Slot Reserved & Fun Votes

* Update funvotes.phrases.txt
2023-04-09 15:13:59 +00:00
Mikhail Buchka
2e59202ea4
Added missing phrases and parameter (#1977) 2023-04-09 15:13:46 +00:00
aept
4a6d29c9e2
Add missing latam phrases (#1968)
* Add missing latam phrases

* Fix latam phrases

Fixed some latam phrases to fit Latam dialect, previously were Spanish from Spain.
2023-04-09 15:09:43 +00:00
rcon_password
e746fdf1fb
missing translation added (#1982) 2023-04-09 14:55:25 +00:00
rcon_password
cac7793f15
translations updated (#1981)
I removed the word Aptauja since in this context it isn't the right word to use https://tezaurs.lv/aptauja:1 and changed it to this word https://tezaurs.lv/balso%C5%A1ana:1 

Example: I was asked  by a stranger on the street to participate in the "Aptaujā" 
Example 2: I went to the biggest shopping mall in Latvia to participate in the "Balsošānā", the year's best actor.
2023-04-09 14:55:16 +00:00
rcon_password
e0c95674c8
missing translation added + fix (#1979)
Added missing translation + Can't Nominate Current Map translation was changed because The sentence in Latvian doesn't sound good + the chosen word wasn't the best option in this sentence.
2023-04-09 14:55:06 +00:00
rcon_password
bc42cef765
missing translation (#1980) 2023-04-09 14:54:54 +00:00
Nicholas Hastings
680a198fa2
Fix quotes in UA translation. 2023-04-07 21:30:20 +00:00
peace-maker
38f7be7664
Update Ukrainian phrases (#1970)
Thank you to VAC#1643 on Discord!
2023-04-07 23:18:16 +02:00
peace-maker
94f5b5d06c
Add missing german phrases (#1966) 2023-04-07 06:59:48 +02:00
XeroX
f66a904c31
Update SDKCall documentation for string return type (#1961)
* Update SDKCall return information

This adds the information that if the return value is a string then SDKCall will return the number of bytes written.

* Update description

* Add info if the string is null
2023-04-02 23:46:53 +02:00
Forgetest
8e0039aaec
[DHooks] Fix NaN return when superceding pre-detour (#1967)
* Update hook.cpp

* Update hook.cpp
2023-03-31 18:19:04 +02:00
Kyle Sanderson
dd1e5997f3
spanish fixups (#1965)
* Update basetriggers.phrases.txt

* Update rockthevote.phrases.txt

* Update nominations.phrases.txt
2023-03-30 14:05:55 +02:00
Corey D
bc6e920213
Improve HasEntProp performance (#1908) 2023-03-29 21:40:30 -07:00
Michael
a28c3cac9b
cstrike.csgo: Add native to retrieve loadout slot of weapon (#1241)
* Add native to retrieve loadout slot of weapon

* Mention the native is for CS:GO only

---------

Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2023-03-29 21:27:45 -07:00
fakuivan
6670b93cc6
keyvalues: adjust KvSavePosition return value to implementation (#757)
The current ``KvSavePosition`` definition does not reflect the behaviour seen in [core/smn_keyvalues.cpp#L973-L981](404e96ad45/core/smn_keyvalues.cpp (L973-L981)). This can cause problems if some procedure on sp expects ``KvSavePosition`` to always add one node to the stack, no matter if there is no higher node.

Co-authored-by: peace-maker <peace-maker@wcfan.de>
2023-03-29 21:06:36 -07:00
aept
8826663186
translations: add missing phrases to Spanish language (#1964)
* Added corresponding format parameters

* Added missing phrase

* Added missing phrase to basetriggers
2023-03-29 20:43:03 -07:00
Corey D
c5087d7a39
database: flip to recursive_mutex to allow nested locks (#1937)
* Fix crash from db locking

* Change db lock to use recursive_mutex
2023-03-29 20:03:03 -07:00
FlaminSarge
5ba9816377
nominations: implement map not in pool phrase (#597)
New English text is “Map ‘%s’ is not in the nominations pool.”
2023-03-29 18:39:54 -07:00
peace-maker
4eecc80b9a
Fix translations workflow (#1963)
* Fix translations workflow

* Temporarily trigger workflow for PRs

* Trigger without a PR

* Just work please

* Update github-app-token dependency

* Update workflow step name

* Disable on PRs again
2023-03-30 02:28:03 +02:00
peace-maker
008fbf78a5
Cleanup the translation files (#1962)
* Remove obsolete slapslay.phrases.txt

That plugin isn't in the stock arsenal since forever.

* Remove "Dead Player Rename" phrase

Unused since #313

* Remove nominations phrases from rockthevote

Those phrases were moved to their own nominations.phrases.txt

* Remove "Next Map" phrase from nextmap.phrases.txt

It was moved to basetriggers.phrases.txt

* Remove unused phrases from common.phrases.txt

Only "Slapped Player" was moved to funvotes.phrases.txt

* Delete empty phrase files

* Fix workflow python version selection

3.10 would select Python 3.1 and fail

* Deduplicate "Please select a map" phrase

Only keep it in the plugin.basecommands.txt file and remove it from the basevotes.phrases.txt.

Both plugins using the phrase load the basecommands phrases.

* Allow manual triggering of translations action

* Actually use the nextmap phrases

They were never referenced in the plugin. The "ago" phrase doesn't work the way it is and needs more rethinking.
2023-03-30 01:13:40 +02:00
Kyle Sanderson
48150e0c7a
Bring languages into the tree (#1625)
* translations: bring languages into tree

* Update translation phrases changed since 2021

* Update packaging script to include all translations

* Update languages.cfg

* Add Latin American Spanish translations

This is a copy of spanish for now.

* Ignore "en" when looking for translation folders

English is the default and doesn't use a subfolder.

* Only add each translation folder once

Korean "ko" is in there twice.

* Compare language coverage to english

All phrases are compared to the english baseline files and any differences
are reported. The differences are pushed to a Github Project as well for
an easier overview.

Thank you to @nosoop for sharing the Python SMC parser!

* Add link to README

---------

Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2023-03-29 16:23:05 +02:00
peace-maker
d8fd60b562
Update clang-8 CI run to Ubuntu 20.04 (#1960)
The Ubuntu 18.04 runner image is deprecated and will be unsupported on 2023/04/01. It appears Ubuntu 20.04 has a package for clang-8, so just do the switch.
2023-03-28 13:50:19 +02:00
sappho
178658912e
fix mismatched delete [] in regex extension (#1941)
* fix mismatched delete []

* strdup needs to be free'd, not deleted
2023-03-27 15:50:31 +02:00
Mikusch
5539484f92
Add support for float modulo operator (#1953) 2023-03-19 16:19:42 +00:00
data-bomb
a9a1939f75
Add LookupAttachment signature for ND (#1942)
Adds the LookupAttachment signature for Nuclear Dawn
2023-03-04 05:05:55 +00:00
Alienmario
850f96b986
Allow void return type in timer callbacks (#1916) 2023-03-02 21:28:42 -08:00
Dysphie
693e584dcd
Fix LookupAttachment signature for NMRiH (#1940) 2023-03-01 16:31:56 +00:00
Nick Hastings
397b70add0 Enable ShowMenu support for Reactive Drop (see #1938)
Note: Currently only works on beta version of game
2023-02-26 12:39:22 -05:00
Corey D
a0eb6a9550
Fix LookupAttachment signature (#1933) 2023-02-13 02:02:48 +00:00
Nicholas Hastings
6bd49ff415
Protect against server crash when DHooks cannot load from SDKHooks not being loaded. (#1930) 2023-02-09 00:10:14 +00:00
Corey D
43cdc65708
Add Ghidra MakeSig script (#1926) 2023-02-05 04:46:52 +00:00
Nicholas Hastings
cd6886319c
Fix crash with IgniteEntity on MCV (#1924)
* Fix crash with IgniteEntity on MCV.

* Formatting.
2023-02-04 16:09:46 +00:00
Poggu
6e839a95c6
Fix EntityFactoryCaller signature (#1925) 2023-02-04 16:06:38 +00:00
El Diablo
2dcee81f22
Linux [SDKTOOLS] Sigscan for FireOutput FIX (#1923)
Addie said:
test the signatures:
\x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x08
or
digby's \x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x2A\x65\xA1

\x55\x89\xE5\x57\x56\x53\x81\xEC\x8C\x01\x00\x00\x8B\x55\x08 fixed the issue for linux server.
2023-02-04 14:46:57 +00:00
GAMMACASE
a253d35bcd
Gamedata update after 2/2/2023 CSGO update (#1921) 2023-02-03 17:38:37 +00:00
Poggu
02e9d214b4
Update Military Conflict: Vietnam gamedata (#1915)
Revert whitespace removal


Revert ignite offset
2023-01-30 23:04:53 +00:00
Nicholas Hastings
30a2160839
Fix gamedata library search order in some cases. (#1914) 2023-01-29 20:24:34 +00:00
rtldg
d57d7e7401
Enable math functions in sqlite (#1886) 2023-01-29 17:30:15 +00:00
Impact
1f5b735dbd
Add note about automatic unhook (#1910) 2023-01-22 10:23:40 +01:00
Nick Hastings
d5a26adc49 Regression fix for CS:GO GivePlayerItem.
Regressed in https://github.com/alliedmodders/sourcemod/pull/1887
2023-01-20 10:09:22 -05:00
Rushaway
221d34f809
fix: Print full map name in Log instead of args (#1907) 2023-01-15 13:32:41 -05:00
Nicholas Hastings
8922ed0c5d
Add TE_WriteEnt and TE_ReadEnt natives to SDKTools. (#1905) 2023-01-14 14:48:05 +00:00
nosoop
27b1817b10
Update TF2 gamedata for version 7757534 (2023-01-05) (#1901) 2023-01-05 23:54:37 +00:00
Nick Hastings
3a488f7041 Fix typo in b77e8c50 causing regression in loading on some games.
Fixes #1898
2023-01-01 20:04:56 -05:00
ambaca
8f7a1641fc
Friendly Fire support L4D(2) games. (#1530)
Support L4D(2) Friendly Fire In Basetriggers.sp #1522

- Games have cvar mp_friendlyfire min. and max. bounds set to 1
- Removing mp_friendlyfire lower bound and set to 0, cvar have no purpose on friendly fire damage.
- FF works from z_difficulty and each difficult level have FF damage ration cvars:
survivor_friendly_fire_factor_easy
survivor_friendly_fire_factor_normal
survivor_friendly_fire_factor_hard
survivor_friendly_fire_factor_expert

Update added PRINT_TO_ things from previous update.

Co-authored-by: Bacardi <>
2022-12-29 14:00:58 +00:00
Nicholas Hastings
b0799d3336
Move menu sound selection from core config to gamedata. (#1896) 2022-12-29 04:10:51 +00:00
nosoop
2130c60fd9
Implement raw entity handle accessors (#1830)
* Implement raw entity handle accessors

* Fix FromPseudoAddress calls on 64-bit

* fixup: Remove unnecessary ent deref

* fixup: Correct param index in thrown error
2022-12-29 03:57:47 +00:00
Nicholas Hastings
ecb707e38d
Add support for other engine binaries in game configs (#1414). (#1626)
* Add support for other engine binaries in game configs (#1414).

* Add engine bin path for CRC bin lookup and filter out addons from GAMEBIN.

* MAX_PATH -> PLATFORM_MAX_PATH.

* Fix library lookup on Linux.

Before this, there was a bad assumption that, like on Windows, POSIX module
handle pointers were within the module's address space (and thus usable
with dladdr). That's not true!

Instead, to get a usable address on all platforms, we'll do a lookup of the
CreateInterface function that exists in all modules. This also has the
(arguable) benefit of further locking this implementation to modules owned
by the game.

To get a valid address inside the module now on both p
2022-12-28 22:58:30 +00:00
Nicholas Hastings
7e94bfb307
Fix asm.c compiler warnings on Windows. (#1897) 2022-12-28 22:50:51 +00:00
Rain
51bba0b894
Fix incorrect behaviour in SDKHooks_DropWeapon's "bypassHooks" parameter (#1877) 2022-12-27 19:31:02 +00:00
Nicholas Hastings
775d4f04f2
Add missing set of CBaseEntity::Teleport param on newer games (#1894)
* Add setting of missing CBaseEntity::Teleport param on newer games

* Rearrange SOURCE_ENGINE defines of games between Portal 2 and CS:GO.
2022-12-27 19:29:22 +00:00
peace-maker
4320d7b17a
Get sdktools and sdkhooks/dhooks to load on hl2sdk-mock (#1892)
* Build SDKTools for hl2sdk-mock

* Fix loading of extensions using advanced naming scheme for hl2sdk-mock

* Fix error on missing sv_visiblemaxplayers convar

* Build SDKHooks for hl2sdk-mock

* Fix x86_64 SourcePawn VM filename

* SDKHooks: Ignore missing IEntityListeners list in hl2sdk-mock
2022-12-22 18:39:12 +01:00
TrueProfesional
174bf307a2
Add weapon_bayonet to the list of blocked knives (#1758) 2022-12-20 22:17:22 +00:00
Benoist
f8349e846a
Fix SetEntityHealth to support 'any' entities (#1846)
Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
2022-12-20 22:07:57 +00:00
zer0.k
ef7d3abefd
Add missing null pointer check to protobuf messages (#1883) 2022-12-20 21:50:57 +00:00
Nicholas Hastings
b77e8c50ac
Add support for Military Conflict: Vietnam (#1887) 2022-12-20 21:46:18 +00:00
clague
0d6179299c
Fix ReadMapList ignoring file's last modified time (#1891) 2022-12-20 21:26:57 +00:00
Nicholas Hastings
515df38b72
Throw error in ShowHudText or ShowSyncHudText if HudText params not yet set (#1890) 2022-12-20 01:26:38 +00:00
Nicholas Hastings
c5e69900f9
Add "sm_dsay" command to basechat for sending HUD messages (#1889)
* Add "sm_dsay" command to basechat for sending HUD messages

* Add missing SetHudTextParams call
2022-12-20 00:00:43 +00:00
Nicholas Hastings
2d5bc4007f
Add clarification to documentation for hud messages and engine dialogs (#1888)
with regard to game support.
2022-12-19 23:49:48 +00:00
zer0.k
3a315e88c2
Update MM:S version in CI, dependency checkout scripts and sample extensions (#1885)
* Update MM:S version in CI, dependency checkout scripts and sample extensions

* Update MM:S version in CI, dependency checkout scripts and sample extensions

* Fix EOL problems
2022-12-19 18:15:45 +01:00
nosoop
6c5a3fdaf2
entitylump: Output separator as spaces instead of tabs (#1873)
On NMRiH and possibly other games that use the Maphacks system,
entries that are modified using that system are rendered with tab
characters stripped out - see CNMRiHMapHackManager::GetEntDataString.

That results in there being no separators at all between keys and
values, as Maphacks receives the serialized string from Entity Lump
Manager.

This commit changes the key / value separator character to use
spaces instead.

This discovery upsets me greatly.

Fixes #1833.
2022-12-05 13:14:54 +01:00
peace-maker
02f188899e
Add CommandIterator.ConVarFlags property (#1869)
* Add CommandIterator.ConVarFlags property

Allow to access the convar flags `FCVAR_*` value from the iterator.
The CommandIterator.Flags property should have been called `AdminFlags`,
but it's too late to change that now.

* Switch admin help menu to use CommandIterator.ConVarFlags

No need to lookup the convar again.

* Deprecate CommandIterator.Flags

Rename it to .AdminFlags now that we have two kinds of flags exposed
on the iterator.
2022-12-04 12:24:56 +01:00
peace-maker
aab8c6ac9f
Fix SDKHook_[Use|Spawn|GetMaxHealth] callback result value handling (#1872)
* Fix SDKHook_Use callback result handling

The returned result of the last callback in the list was used instead
of the highest value. This differs from the behavior of the other hooks.

* Fix SDKHook_Spawn callback result handling

The returned result of the last callback in the list was used instead
of the highest value. This differs from the behavior of the other hooks.

* Fix SDKHook_GetMaxHealth callback result handling

The returned result of the last callback in the list was used instead
of the highest value. This differs from the behavior of the other hooks.
The returned health is only changed if no other plugin wants to block the callback.
2022-12-04 12:12:42 +01:00
Corey D
5d391fda07
Add SMCParser.ParseString (#1817) 2022-12-02 16:55:08 +01:00
vanz666
8538233985
Expose custom sdktools trace types from hl2sdk (#1822)
* Update trnatives.cpp

* Update sdktools_trace.inc

* Update trnatives.cpp
2022-12-02 14:11:05 +01:00
Corey D
3b4a343274
Add natives to get chat triggers (#1816) 2022-12-02 13:55:32 +01:00
Erin
f90b7ade76
Trigger build for TF2 SDK update 2022-12-02 08:50:18 +00:00
Nick Hastings
83a29cf82b Trigger build against SDK update 2022-12-01 22:37:50 -05:00
Nick Hastings
89bd4d7329 Update TF2 gamedata. 2022-12-01 19:33:09 -05:00
Batfoxkid
ed662dc8cf
Block Hidden Commands from Help Command (#1831)
Allows using `FCVAR_HIDDEN` to block commands from appearing in `sm_help` and `sm_searchcmd` which helps with creating alias to commands without filling up the list with those aliases.

For example:
```sourcepawn
RegAdminCmd("sm_setmyperk", MyCommand, ADMFLAG_CHEATS);
RegAdminCmd("sm_setmyperks", MyCommand, ADMFLAG_CHEATS, _, FCVAR_HIDDEN);

RegConsoleCmd("ff2_boss", MyCommand);
RegConsoleCmd("ff2boss", MyCommand, _, FCVAR_HIDDEN);
```
2022-12-01 22:04:59 +01:00
Spirrwell
6574dd8273
Update PVKII FireOutput Linux Signature (#1866) 2022-11-27 03:18:19 +00:00
zer0.k
63f8ea89ca
Return the full list of exts/plugins with "sm exts/plugins" client commands (#1862) 2022-11-25 19:00:48 +00:00
nosoop
34c8220e5d
Ensure gameconfig file uniqueness when reading master.games (#1859)
The extended gameconfig format reads the master gameconf file twice;
once each for the base engine and actual engine.  The file list
isn't checked for duplicates, so 'common.games.txt' is loaded in
twice, resulting in any 'common' file values potentially overriding
values listed under '#default' in other files due to
bShouldBeReadingDefault.  This happens in the case when matching
game versions by CRC (such as public game branches).

Required for #1857.
2022-11-25 16:47:11 +00:00
peace-maker
3595525f12
Update Github Action workflow versions (#1858)
The currently used versions use deprecated features and Node 12 which is EOL. Switch to newer versions as adviced.

https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-07 15:09:54 +01:00
Maxime Leroy
4989666d72
Add logging to all basevote vote results (#1794) 2022-10-30 23:26:17 +01:00
Erin
f7fda0023c
Enable CI on release branches (#1854) 2022-10-29 17:36:04 +01:00
Erin
13be211e9a
Bump minimum MM:S version for build to 1.12 (#1855)
PVKII changes require at least MM:S 1.12 to build SM.
2022-10-29 15:48:38 +01:00
Spirrwell
f6b39720ec
Make sure 'pvkii' gets 'GetDataDescMap' offset (#1852) 2022-10-27 21:57:21 +00:00
Maxim Telezhenko
764e38a58f
Fix DHooks jit code stack memory alignment (#1849) 2022-10-26 20:41:40 +02:00
Spirrwell
e34e9b9869
Split PVKII into its own engine branch (#1847)
* Initial PVKII branch support

* Change PVKII code to 23, DOTA uses 22 in MM

* Bunch more SE_PVKII preprocessor that was missed

* Add some missing SOURCE_ENGINE_PVKII cases

* Update PVKII FireOutput signature for Linux

* Update checkout-deps with 'pvkii'

* Fix FireOutput signature for PVKII, accidentally used one from newer build

* Change PVKII code to 10, and bump others by 1

* Only check against SE_SDK2013 here, like Metamod

* More SE_PVKII preprocessor cleanup
2022-10-24 19:27:32 +00:00
Nick Hastings
721f348684 Trigger build for hl2sdk-csgo update 2022-10-23 18:24:24 -04:00
Vauff
9ddcd335af
Correct missed team offsets in CheckRestartRound (#1844) 2022-10-22 09:30:33 -05:00
Nick Hastings
eda9a42ee2 Trigger build for hl2sdk-csgo update 2022-10-21 22:28:35 -04:00
Vauff
22a964d89a
Update gamedata for 2022/10/21 CS:GO update (#1842) 2022-10-21 20:13:57 -05:00
nosoop
855ece1d3b
entitylump: Fix behavior of append (#1836)
This change ensures that the iterator values used by `std::distance`
is correct.  Having the emplace within leads to the possibility of
`m_Entities.begin()` being invalidated due to reallocations.
2022-09-20 16:24:08 +02:00
Vauff
568da23c96
Allow gamedata to use vscript binary (#1826) 2022-09-20 09:46:03 +02:00
Bara
4015f9c1c9
Replace old link with newer working one (#1837) 2022-09-19 22:35:49 +02:00
nosoop
21740d9a26
Add functions for working with entity lumps (#1673) 2022-09-05 15:44:58 -07:00
Impact
05aa3fd39d
Update CreateDirectory (#1813)
* Update CreateDirectory docs

Add default arguments and a note about deep paths.

* Update note

* Add note about use_valve_fs
2022-08-27 01:59:43 +02:00
Vladimir
3100e23b87
Add support for gamedata lookups from soundemittersystem library (#1787)
* Add `soundemittersystem` library to game config signatures

* Remove `GAMEBIN` by `soundemittersystem` module load
2022-08-04 16:32:26 +02:00
David Anderson
da3e868df5
Merge pull request #1812 from alliedmodders/revert-pbproxy
Revert "Introduce a pbproxy library to solve macOS linker issues."
2022-07-31 11:50:56 -07:00
David Anderson
63e0e2bc82 Fix build. 2022-07-31 11:22:10 -07:00
David Anderson
a35ecde538 Revert "Introduce a pbproxy library to solve macOS linker issues."
This reverts commit e5ddbd9886.
2022-07-31 11:17:42 -07:00
David Anderson
81ad870def Revert "Fix use-after-free when creating custom user messages"
This reverts commit 15450a6d0c.
2022-07-31 11:16:50 -07:00
eyal282
24f1c89b96
Add Clientprefs helpers for integers and strings (#1727)
* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc

* Update clientprefs.inc
2022-07-31 11:59:25 +02:00
Kevin Yonan
a761194917
Update operator% forwards to newdecls (#1763) 2022-07-28 18:12:00 -07:00
Corey D
9321229321
Add GetClientOriginalLanguage (#1810)
Creates a native which will return the language the client connected with.
2022-07-28 18:10:51 -07:00
Accelerator
e5ebd65176
Exposed SDKCall_Engine call type to use CVEngineServer methods (#1648)
* Update vdecoder.h

* Update vcaller.cpp

* Update sdktools.inc
2022-07-26 22:14:49 +02:00
Dysphie
5f7b22d2a4
Allow using sm_nominate without args from console (#1803) 2022-07-26 22:09:48 +02:00
David Anderson
52da989762 Update SourcePawn on master. 2022-07-09 18:32:51 -07:00
42
625c7a98f2
Fix support for SDKCall returning non-networked entity (#1797) 2022-07-08 14:33:43 +00:00
Mikusch
278998f7d0
SDKTools: Add explode parameter to ForcePlayerSuicide native (#1782)
* Expose CommitSuicide params

* A single space

* Validate param count

* Set force param to true by default
2022-07-07 14:14:50 +02:00
peace-maker
c29e185d2a
Bump version to 1.12 for manual builds (#1795) 2022-07-05 15:13:52 +02:00
David Anderson
5c66a78763 Bump master to 1.12. 2022-07-02 17:02:08 -07:00
peace-maker
5e3a189642
Log a notice if the geoip database gets too old (#1791)
Since we ship an ancient version of the database, help server operators keep track of the database version.
2022-06-30 08:22:51 +02:00
peace-maker
7b89f5fab3
Update SQLite library to 3.38.5 (#1792)
Bring in the changes of the last four years of SQLite development.

Fixes #1728
Fixes #1592
2022-06-30 08:22:28 +02:00
Nick Hastings
b0e7407552 Trigger build against hl2sdk-csgo changes. 2022-06-26 11:01:39 -04:00
Nick Hastings
407c24cb14 Trigger build against hl2sdk-csgo changes.
(cherry picked from commit 5714e7695a653dcade1832cb482855e77b5f13dd)
2022-06-24 10:00:36 -04:00
Nick Hastings
65cb76e2f6 Merge branch 'master' of https://github.com/alliedmodders/sourcemod 2022-06-24 10:00:13 -04:00
dysphie
678ad74bf4 Add sdktools gamerules support for NMRiH (#1784) 2022-06-24 10:00:02 -04:00
David Anderson
12e20eecf7 Update SourcePawn. 2022-06-24 10:00:02 -04:00
Margen67
a5ae01a2f4 .gitmodules: Make submodules shallow (#1769) 2022-06-24 10:00:02 -04:00
Vauff
614c7d8332 Expand ShowHudText message buffer for Protobuf games (#1777)
* Expand ShowHudText message buffer for CS:GO

* Expand buffer on Blade Symphony too
2022-06-24 10:00:02 -04:00
peace-maker
9121472061 DHooks: Error on argument passflags for detours (#1773)
The passflags are only supported by SourceHook for virtual hooks and are ignored for detours with DynamicDetours. This caused confusion, so throw an error when trying to set e.g. the DHookPass_ByRef flag on detour arguments.
2022-06-24 10:00:02 -04:00
peace-maker
54fd778830 DHooks: Fix changing of byref vector parameters (#1772)
We always created a new vector object instead of changing the passed in vector directly. This works for the function being called using our changed values - but the caller doesn't see the changed values if it's passing a vector by reference.

Only create a new vector if there isn't one being passed in and set the values directly in the passed in vector otherwise.
2022-06-24 10:00:01 -04:00
Deathreus
37c2a83523
Add a PluginIterator methodmap (#1779)
* Add a PluginIterator methodmap

* Follow convention

* Update sourcemod.inc

* Turn method ReadPlugin into property Plugin

* Requested change

* Update sourcemod.inc

* Curse you VSC

* Follow behavior of other iterators instead of the natives

* Fix a stray space

* Implement a hacked CPluginIterator

* Oops

Copy paste go brr

* Revert a change made before the custom impl
2022-06-24 11:46:03 +02:00
Nick Hastings
28a5d4b342 Merge branch 'master' of https://github.com/alliedmodders/sourcemod 2022-06-21 21:48:36 -04:00
Nick Hastings
0dcfdf3d7e Update TF2 gamedata. 2022-06-21 21:48:25 -04:00
dysphie
0f7f9dad97
Add sdktools gamerules support for NMRiH (#1784) 2022-06-20 16:47:11 +00:00
David Anderson
4276301499 Update SourcePawn. 2022-06-18 15:35:30 -07:00
Margen67
d41ffaad7e
.gitmodules: Make submodules shallow (#1769) 2022-06-15 13:49:57 +02:00
Vauff
5c0ae11a46
Expand ShowHudText message buffer for Protobuf games (#1777)
* Expand ShowHudText message buffer for CS:GO

* Expand buffer on Blade Symphony too
2022-06-06 12:45:35 +00:00
peace-maker
7424deefb9
DHooks: Error on argument passflags for detours (#1773)
The passflags are only supported by SourceHook for virtual hooks and are ignored for detours with DynamicDetours. This caused confusion, so throw an error when trying to set e.g. the DHookPass_ByRef flag on detour arguments.
2022-05-31 14:58:36 +02:00
peace-maker
c92354debb
DHooks: Fix changing of byref vector parameters (#1772)
We always created a new vector object instead of changing the passed in vector directly. This works for the function being called using our changed values - but the caller doesn't see the changed values if it's passing a vector by reference.

Only create a new vector if there isn't one being passed in and set the values directly in the passed in vector otherwise.
2022-05-31 14:55:47 +02:00
Nick Hastings
50eca8e60c Update Reactive Drop gamedata (closes #1771) 2022-05-30 20:28:55 -04:00
Headline
c570150318
Lift raw pointers out of DatabaseConfBuilder (#1637)
* Lift raw pointers out of DatabaseConfBuilder

* Maybe: not
2022-05-24 11:41:15 +02:00
David Anderson
3bafc1e2f4 Update SourcePawn. 2022-05-11 13:20:41 -07:00
Vauff
e28bf30b7d
Update WriteBaselines signature for CS:GO update (#1766) 2022-05-09 21:08:11 -07:00
Bone
7becdc48d3
fix WeaponPrice offset on windows (#1765) 2022-05-07 06:43:13 -04:00
Psykotikism
7a3d4e70bf
Add "DispatchKeyValueInt" stock (#1764) 2022-05-06 14:53:57 +01:00
Arron Vinyard
6e73aba250
Remove unnecessary timer typeset entry (#1735) 2022-05-03 19:34:20 -07:00
Arron Vinyard
5797411b2b
NPOTB: Use camel casing for variables in adminhelp.sp (#1750)
This is a stylistic change to ensure more adhered-to consistency throughout base plugins
2022-05-03 18:45:45 -07:00
Sikari
d2c4257c36
Prevent workshop prefix from showing in nominations results menu (#1737)
Prevents workshop prefixes from being shown instead of the display names in results menus when items are disabled
2022-05-03 02:10:10 -07:00
domino_
b057580a62
Add a OnPlayerRunCmdPre forward (#1760)
Adds an OnPlayerRunCmdPre forward in order for plugins to be able to hook OnPlayerRunCmd with the guarantee that none of the parameters have been modified by other plugins. As such, OnPlayerRunCmdPre's parameters cannot be modified and are read-only. 

Plugins that wish to use OnPlayerRunCmdPre can maintain backwards compatibility with SourceMod 1.10 by falling back to OnPlayerRunCmd if the Pre variant was never fired.
2022-04-25 14:00:53 -07:00
Mikusch
a1ad9e1acf
Fix TF2_OnIsHolidayActive forward not getting called after map change (#1752)
* Fix TF2_IsHolidayActive forward not getting called after map change

* Rename function to Unhook
2022-04-22 11:49:46 +02:00
Mikusch
a877a4475b
SDKTools: Clear gamerules pointer on level shutdown (#1755)
* Clear gamerules pointer on level shutdown

* Move LevelShutdown up
2022-04-22 11:48:21 +02:00
Alienmario
441259e36a
Add recent gamedata for BMS, HL2DM (#1756) 2022-04-21 21:49:19 +00:00
Kyle
ab8bbbd118
Honor AUTOLOAD_EXTENSIONS in clientprefs include (#1718)
* Optional autoload extensions

* revert autoload for cstrike/tf2
2022-04-21 13:44:24 +02:00
peace-maker
39d604ae6c
DHooks: Allow setting CBaseEntity* param to NULL #1751 (#1754)
* DHooks: Allow setting CBaseEntity* param to NULL #1751

The param had to be a valid entity and wasn't allowed to be set to NULL. Behave similar to SetReturn which maps INVALID_ENT_REFERENCE (or -1) to NULL.

* Update include documentation
2022-04-20 15:32:37 +02:00
peace-maker
a7cb35c2af
Add -m flag to checkout-deps to avoid downloading MySQL (#1753)
If you just want to build an extension or SourceMod core and not the MySQL extension, don't download the large MySQL library archive.
2022-04-20 14:34:47 +02:00
XeroX
ff558b32e5
ZPS Update offsets for 3.2.4 (#1746)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.

* ZPS: Add missing offsets.

Adds support for OnTakeDamage_Alive, GetMaxHealth, Blocked, Reload and GroundEntChanged.
I wasn't aware these were missing entirely or I would have pushed them with the previous PR.

* ZPS Add missing offsets.

Adds support for GivePlayerAmmo.
Wasn't aware these were missing the first place.

* Fix ForcePlayerSuicide not properly working in ZPS

ZPS requires the second bool parameter to be true otherwise it won't do anything for players in the lobby or delayed for players on either team 2 or team 3.

* Fixed breaking code for other mods.

Added bForce which is set to true for zps.

* Update offsets for ZPS 3.2.4

Updates the offset
2022-04-16 14:07:46 +00:00
XeroX
6b588fff61
ZPS: Add missing offsets (#1719)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.

* ZPS: Add missing offsets.

Adds support for OnTakeDamage_Alive, GetMaxHealth, Blocked, Reload and GroundEntChanged.
I wasn't aware these were missing entirely or I would have pushed them with the previous PR.

* ZPS Add missing offsets.

Adds support for GivePlayerAmmo.
Wasn't aware these were missing the first place.

* Fix ForcePlayerSuicide not properly working in ZPS

ZPS requires the second bool parameter to be true otherwise it won't do anything for players in the lobby or delayed for players on either team 2 or team 3.

* Fixed breaking code for other mods.

Added bForce which is set to true for zps.
2022-04-16 14:07:16 +00:00
MartLegion
3f3f1b8914
Moved pvkii section for better readbility (#1744)
Moved pvkii section above for better readbility
(requested by psychonic)
2022-04-12 14:09:33 +00:00
MartLegion
05e48ef866
Added PVKII game with related team colors (beacon) (#1743)
Spectators = Yellow
Pirates = Red
Vikings = Green
Knights = Blue
2022-04-12 13:57:18 +00:00
Gaben
6e1f095186
Allow h-flag admins to bypass vote delay (#1733) 2022-04-12 11:19:54 +01:00
Arron Vinyard
852703ccca
Add GetCmdArgFloat(Ex) stocks (#1742) 2022-04-12 11:17:05 +01:00
David Anderson
01203a5a44 Update SourcePawn. 2022-03-09 18:00:54 -08:00
Mikusch
96ae65a96c
Remove misleading note on DynamicHook.RemoveHook docs (#1725) 2022-03-07 12:34:25 -05:00
James Dickens
9df93b0708
Fix Int64ToString producing incorrect output (#1723) 2022-03-04 03:03:35 -08:00
rtldg
f307e44b79
Correct some function docs that return char count (#1721) 2022-02-25 15:20:12 -08:00
V
dc8a22a76a
Inline one-expression single-use functions (#1700) 2022-02-25 10:39:45 +01:00
XeroX
8579e873ca
ZPS: Update Offsets & Signatures for 3.2 (#1717)
* Update offsets for ZPS 3.2

Zombie Panic! Source Version 3.2 released and these are the updated offsets.

* Update offsets for 3.2

ZPS 3.2 has been release and these are the updated offsets / signatures.
2022-02-19 14:33:15 +00:00
Corey D
addfb3c8a1
Add MAX_AUTHID_LENGTH (#1696) 2022-02-15 10:21:35 +00:00
Erik Minekus
358bcca3a2
Database.Format destination buffer should not be marked const (#1714) 2022-02-14 07:39:03 -05:00
Corey D
b209279589
Add bitwise SetBitFlags for AdminId and GroupId (#1677)
* Add bitwise SetFlags for AdminId

* Inline SetFlags natives
2022-02-12 13:14:57 +01:00
XeroX
5479084039
ZPS: Add LookupEntityAttachment and GetEntityAttachment Gamedata (#1706)
* Add LookupAttachment and GetAttachment

Offset and Signature for Zombie Panic! Source.

* Fix Gamerules not being available

This fixes the issue that would throw an error when trying to use SDKCall with type SDKCall_GameRules.

* Add SetOwnerEntity offset

Add SetOwnerEntity offset
2022-02-08 17:56:31 -08:00
Asher Baker
95ab60c4eb
Stop SQLite results being used before being fetched (#1709)
The DB API requires FetchRow to be called before accessing any values
from a row, but the SQLite driver did not enforce that requirement and
alowed accessing the first row immediately. A plugin developer hit this
when developing against SQLite locally but using MySQL in production,
where the API misuse threw an error as expected.

Resolves #1691
2022-02-06 16:34:14 +00:00
Asher Baker
2111e90540
Fix FindSendPropOffs with SendPropArray props (#1708)
FindSendPropOffs is deprecated and FindSendPropInfo behaves correctly
here, but there are still a lot of old plugins using FindSendPropOffs.
One of the SendPropArray props broken by the changes here is
m_hViewModel which there are known plugins in the wild accessing.
2022-02-06 15:56:52 +00:00
Alienmario
dc9c52bfd6
Enable nextmap for Black Mesa (#1695) 2022-02-04 13:50:26 +00:00
Ҝℴţأķ
f5461df28c
Remove hardcoded question mark from sm_vote (#1699) 2022-02-03 10:56:25 +00:00
iNilo
e5afdb4181
adminhelp.sp -> Switch to the new CommandIterator() (#1388)
* Switch to the new CommandIterator() methodmap

`ReadCommandIterator` 
->
```
CmdIter.GetName(Name, sizeof(Name));
Flags = CmdIter.Flags;
CmdIter.GetDescription(Desc, sizeof(Desc));
```

* PeaceMaker fixes

* Don't fetch unused properties

Co-authored-by: Michael Flaherty <michaelwflaherty@me.com>
Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2022-02-01 19:13:06 +01:00
Arron Vinyard
250dc1b206
Update dhooks.inc documentation for consistency (#1658)
* Update dhooks.inc documentation for consistency

- Modifies whitespace (change tabs to spaces for non initial indents, fix alignments, create consistency with rest of SM docs)
- Change `/*` to `/**` for consistency and to indicate comment doc
- Removes incorrect `@noreturn` doc

* Split long comments across multiple lines

* Remove `@noreturn` in IGameConfigs.h

* Remove `@noreturn` from IGameHelpers.h

* Remove `@noreturn` from asm.c

* Add `@noreturn` to ThrowError

* Add `@noreturn` and `@error` to ThrowNativeError
2022-02-01 18:46:59 +01:00
komashchenko
f5f26e2dfe
Fix GetGameSoundParams in CSGO (#1631)
* Fix GetGameSoundParams CSGO

* Added preprocessor comments

* Revert InternalPrecacheScriptSound
2022-02-01 17:14:53 +01:00
peace-maker
f7c54e9027
Fix silent error on large ArrayList startsize (#1705)
If there isn't enough memory to resize the ArrayList to the startsize on construction, throw an error instead of ignoring the OOM.

Fixes #1551
2022-01-31 22:07:32 +01:00
dysphie
1ce828b6c8
Add NMRiH support for new collision and attachment natives (#1702)
- Add "GetAttachment" offset
- Add "SetOwnerEntity" offset
- Add "LookupAttachment" signature
2022-01-26 19:51:37 +01:00
Corey D
c471e41dc7
Add ArrayStack.Clear native (#1676) 2022-01-22 03:26:21 -08:00
Mikusch
afc9310704
Add LookupEntityAttachment & GetEntityAttachment natives (#1653)
Using the virtual `CBaseAnimating::GetAttachment(int, matrix3x4_t &)` was a deliberate choice because virtual offsets are generally easier to maintain than signatures. The `matrix3x4_t` is converted to world position and world angles internally.
Some of the other overloads are also inlined on a few games, making this the best choice.

Since this call can only be used on classes inheriting `CBaseAnimating`, we check if the `DT_BaseAnimating` SendTable exists on the entity, throwing a native error if it doesn't.
This safeguard could be greatly improved with a call to `CBaseEntity::GetBaseAnimating`, but would require more gamedata (maybe something to consider for the future?)
2022-01-03 13:13:54 +00:00
Corey D
bf898dd45f
Fix ArrayList return types (#1679)
ArrayList.SetString and ArrayList.SetArray both have int return types but their methodmap natives use void.
2021-12-27 12:17:04 +01:00
naydef
7d6eb2bd81
Fix crash in non-bypass-hooks DropWeapon implementation (#1672)
Fixes #1670
2021-12-15 10:41:55 +00:00
Nick Hastings
547ac5b026 Fix helpers module path lookup on newer Perl versions. 2021-12-13 13:16:14 -05:00
Nick Hastings
7328ce657b Missing ;s. 2021-12-13 12:59:20 -05:00
Nicholas Hastings
14c9ac8fdb
[Buildbot] Delete package after successful upload. (#1665)
* [Buildbot] Delete package after successful upload.

* Use autodie.

* ;
2021-12-13 17:52:26 +00:00
Nick Hastings
93cd78a6c5 Remove dead code. 2021-12-13 10:00:42 -05:00
Nick Hastings
b62f332611 Use SteamWorks C++ API in CStrike RulesFix, for better interface compatibility. 2021-12-13 09:34:00 -05:00
Asher Baker
47f135534d Upload libsourcepawn Breakpad symbols as well 2021-12-12 15:04:40 +00:00
Asher Baker
bb095097a2 Upload libsourcepawn debug symbols 2021-12-12 14:43:32 +00:00
David Anderson
c873e1aafb Update SourcePawn/AMTL to fix build. 2021-12-11 16:28:04 -08:00
Fyren
455bb14589 Update SP. 2021-12-11 04:13:59 -05:00
Headline
e786ff54f7
Prevent enum shadowing & pin sourcemod for build fixes (#1661) 2021-12-10 14:22:04 -08:00
David Anderson
af3c10b173 Update AMTL. 2021-12-09 18:14:28 -08:00
David Anderson
351ac171d2 Merge branch 'fix-menu-vote-handler' 2021-12-09 18:13:30 -08:00
David Anderson
96c74651db Fix vote menu handler to work with direct arrays. 2021-12-09 18:10:35 -08:00
Benoist
329d587214
Fix ShouldCollide originalResult behaviour (#1657) 2021-11-30 09:24:56 +00:00
David Anderson
4e3df76358 Update SourcePawn.
Fix fix an hl2sdk-mock build error.
2021-11-23 00:11:48 -08:00
Stanislav Polshyn
161084e6f8
gamedata: Add SetEntityOwner L4D1 offset (#1649) 2021-11-22 16:46:07 -08:00
Benoist
b38c9824fe
sdktools: Add EntityCollisionRulesChanged & SetEntityOwner natives (#1620)
* Add EntityCollisionRulesChanged & SetEntityOwner natives

* fix win build, and unpushed changes

* Fixed bad world loop

* Requested changes + csgo offsets

* small copy paste mistake

* Strip the debug log lines

* Tiny clean up in comments

* line

* <dvander> try again

* sdktools: add default Param for owner.

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2021-11-21 23:03:35 -08:00
Adrián
4e4c2a7bb0
menusys: Correct GetMenuExitBackButton returncode (#1646) 2021-11-20 16:52:40 -08:00
peace-maker
1c30b88069
Cache copy of library on first symbol/signature lookup (#1642)
Always perform signature searches on an unaltered copy of the binary. This avoids signature mismatches if the same function is detoured twice and thus the first bytes of the function were replaced by the detour.
2021-11-18 17:55:32 +01:00
David Anderson
31f6c0cb18 Dedent some menu code for readability. 2021-11-17 18:19:40 -08:00
Peace-Maker
9cb693d8a3 Ignore all build* folders in .gitignore
I frequently have one like `buildwin` and `buildlin` for the different OS in WSL.
2021-11-17 13:17:39 +01:00
Peace-Maker
896e92264f Use SourceMod's version number 2021-11-17 13:17:39 +01:00
Peace-Maker
f32b210ec9 Print OS errors if mprotect fails
This helped catch a bug in DHooks before, so it's worth adding it in here as well.
2021-11-17 13:17:39 +01:00
Peace-Maker
8f7ecf66a4 Use JMP patching of CDetour 2021-11-17 13:17:39 +01:00
Peace-Maker
581068c09b Add copyright headers 2021-11-17 13:17:39 +01:00
Peace-Maker
0f5f1a814e Import DHooks + Dynamic Detouring
This is the latest DHooks version from 1314f2d1b4
2021-11-17 13:17:39 +01:00
Adrián
18f9d65938
Fix translations getting truncated prematurely (#1640) 2021-11-16 16:09:30 +00:00
Nick Hastings
589fc96bc9 Update Contagion SDKHooks gamedata. 2021-11-15 21:26:23 -05:00
Nick Hastings
5787fca029 Disable Reserved Slots plugin on Contagion.
(It doesn't have sv_visiblemaxplayers).
2021-11-15 20:53:09 -05:00
Nick Hastings
09ea982bbd Make separate copy of makesig script for IDA 7.x. 2021-11-15 20:52:37 -05:00
Mikusch
e3569edb65 Update Team Fortress 2 gamedata
Shift virtual offsets up by 3
2021-11-15 20:43:51 -05:00
Nick Hastings
3db7116620 Update Contagion SDKTools gamedata. 2021-11-15 20:31:54 -05:00
dysphie
83672a6ce0
Fix "ForcePlayerSuicide" linux offset in NMRiH (#1627) 2021-11-12 09:19:41 +01:00
David Anderson
90fec5c6df Add support for ASAN. 2021-11-11 23:41:42 -08:00
David Anderson
257535daf2 Update SourcePawn.
This fixes a few more regressions found in the new compiler.
2021-11-07 18:16:20 -08:00
David Anderson
eb0878be2b Update SourcePawn.
This fixes a recent stability regression.
2021-11-02 10:51:00 -07:00
Nick Hastings
f229badbfc Fix server binary name for CRC lookups. 2021-11-01 13:04:23 -04:00
Nick Hastings
9a6866d14a Invert CRC to match most popular implementations. 2021-11-01 13:04:23 -04:00
dysphie
8991b557ab
Update gamedata for NMRiH 1.12 (#1623)
* Fix NMRiH's sdktools gamedata

* Fix NMRiH's sdkhooks gamedata

* Fix GroundEntChanged linux offset
2021-11-01 01:42:45 +00:00
David Anderson
be60a55aa3 Update SourcePawn. 2021-10-31 12:30:51 -10:00
David Anderson
10795fbed5 Switch to clang-8.
This is the new minimum supported Clang version going forward.
2021-10-27 09:44:19 -10:00
FortyTwoFortyTwo
9d7e720f33 Fix compiling with tf2_stocks 2021-10-27 07:51:33 -04:00
David Anderson
7605940994 Fix build. 2021-10-26 23:33:00 -07:00
David Anderson
94ff26bdf1
Fix error return of FormatNativeString. (#1613) 2021-10-26 22:31:58 -07:00
David Anderson
2a06680252 Update to the new SourcePawn compiler.
This imports the brand new SourcePawn compiler. The new compiler is much
faster to compile and generates significantly improved code around
array generation and array access.

There are a number of compatibility changes in the new compiler. Most of
these are due to improved type checking and error detection. The full
list of notes can be found here:

https://github.com/alliedmodders/sourcepawn/blob/master/docs/upgrading-1.11.md

Additionally, .smx files generated by the new compiler will NOT load on
earlier versions of SourceMod, including earlier versions of 1.11. Old
plugins will continue to load as normal.
2021-10-26 17:45:48 -10:00
Nick Hastings
38a069b97a Throw error if unknown SDK call type specified. 2021-10-26 17:32:25 -04:00
Nick Hastings
4b58b9f673 Reorder SDKCallType enum for better compatibility with existing plugins. 2021-10-26 17:32:02 -04:00
dragokas
ffc23a1a1e Exposed SDKCall_Server call type to use CBaseServer methods 2021-10-26 17:18:16 -04:00
David Anderson
cab60f7385 Bump handle limit to 1MB.
This bumps the handle bits to 20 and reduces the serial/cookie bits to
12. A warning is emitted if a single owner creates more than 100k
handles.

Tested on mock srcds with sm_dump_handles.
2021-10-26 07:05:34 -10:00
Kyle
92ce0fe814 Update TerminateRound signature for CSGO 2021-10-26 09:46:21 -04:00
Vladimir
79d594aca3
Add OnClientLanguageChanged() forward (#1597) 2021-10-25 19:45:24 +01:00
Asher Baker
a343410793
Fix build against released MM:S branches (#1607)
`SOURCE_ENGINE_MOCK` is part of the MM:S API and only available in 1.12
which is the dev branch of MM:S. We can build the mock SM build without
it anyway, but this is still a little gross as it is just missing loader
support which could be confusing.

This also changes AMBuildScript to first look for a checkout of MM:S
1.11 which has been the stable branch for a while, 1.10 is still allowed
as a fallback as that seems to still build without issue.
2021-10-25 19:42:51 +01:00
Nick Hastings
3332da3489 Fix up OnClientSettingsChange logic order.
- Fixes bot name cache not getting updated (see #1579)
- Fixes IClientListener::OnClientSettingsChanged not being called for bots.
2021-10-24 10:07:55 -04:00
Nick Hastings
1a65b308d1 Add support for other Steam ID formats to admin-sql-threaded (#1520). 2021-10-24 09:57:03 -04:00
Nick Hastings
f9309c83d2 Don't cache replay_enable value when evaling whether to use FileExists hook.
Speculative fix for #1581
2021-10-24 08:54:15 -05:00
Nick Hastings
636161e183 Mark unloaded extensions as not fully loaded (fixes #1574). 2021-10-24 08:52:32 -05:00
Charles
897878f8ae Removed incorrect parameter RemoveChangeHook desc 2021-10-23 11:28:23 -05:00
Charles
7650cee459 Removed incorrect parameter in description 2021-10-23 11:28:23 -05:00
Corey D
30c764e4b3 Use words instead of quotes for better clarity
As suggested by sneak-it
2021-10-23 11:25:10 -05:00
Corey D
dc92335fbe Missed Database.Escape 2021-10-23 11:25:10 -05:00
Corey D
8c625ca87d Clarify SQL_EscapeString usage
It's hard to see the difference between 2 single-quotes and 1 double-quote unless you highlight it.
2021-10-23 11:25:10 -05:00
Nick Hastings
536750b428 Add missing parameters for CSWeaponDrop. 2021-10-22 23:42:45 -04:00
GAMMACASE
11e7bb10f1
Fix CS_OnCSWeaponDrop after latest CSGO update (21/10/21) (#1602) 2021-10-22 16:20:54 +01:00
Vauff
f7cd28cfd3 Update HandleCommand_Buy_Internal sig and related offsets for CS:GO Win 2021-10-21 21:21:36 -05:00
Vauff
83c7bdf872 Update CSWeaponDropBB signature for CS:GO Linux 2021-10-21 19:46:20 -05:00
Nick Hastings
97383028e5 Add option to not bypass hooks with TakeDamage and DropWeapon natives. 2021-10-13 12:56:26 -04:00
Nick Hastings
4a6d263dad Remove obsolete core-legacy checks. 2021-10-13 12:56:26 -04:00
Nick Hastings
549e881ba4 Fix attempting to link win32 libprotobuf on win64. 2021-10-13 12:05:51 -04:00
Nick Hastings
c72ea03a86 Use MM:S master for Windows builds (already doing for Linux/Mac). 2021-10-13 10:36:36 -04:00
Nicholas Hastings
9fed0724f7 Typo fix. 2021-10-13 09:57:09 -04:00
Nick Hastings
ed96da1afc Fix incorrect param types in CS:GO GNI (otherwise broken for x64). 2021-10-12 20:56:51 -04:00
Nick Hastings
da3146e294 Remove manual ptr math in vnatives for params not mapped to cell params. 2021-10-12 20:56:51 -04:00
Nick Hastings
5aa0cdfd45 Fix Blade Symphony GiveNamedItem call on x64. 2021-10-12 20:56:51 -04:00
Nick Hastings
0be7813398 Add ValveType_Object type.
This is for the few cases where we have function parameters to objects that
don't fit our other predefined types (CBaseEntity, CBasePlayer, string).

Most calls currently pass those as POD (which is incorrect, but works on
x86), or one of the other pointer types (which is also incorrect, and can
lead to confusion, but works on x86 and x86-64).

This type only works when manually buffering the parameter for calls, and
is not supported for return types.
2021-10-12 20:56:51 -04:00
Nick Hastings
de84f47503 On Linux, link against server libs instead of client (fixes META_CONPRINT). 2021-10-12 20:56:51 -04:00
Nick Hastings
115e3d4392 Update multiple SDKTools calls to be x64-safe [sizeof(void*) != sizeof(int)] 2021-10-12 20:56:51 -04:00
Nick Hastings
9f101ce67f Add missing WriteBaselines signatures for Blade Symphony. 2021-10-12 20:56:51 -04:00
Nick Hastings
9295bc4fbb Fix crash with IBinTools calls on x64 for functions with void return. 2021-10-12 20:56:51 -04:00
Nick Hastings
9e083ec668 Use new GetIServer call and updated IServerTools calls on Blade Symphony. 2021-10-12 20:56:51 -04:00
Nick Hastings
68c8857410 Enable more CS:GO engine logic for Blade Symphony. 2021-10-12 20:56:51 -04:00
Nick Hastings
9323d1094f Blade Symphony gamedata updates, round 1. 2021-10-12 20:56:51 -04:00
Nick Hastings
dc2967b2e7 Fix CS:GO transposing of GiveNamedItem last param and return. 2021-10-12 20:56:51 -04:00
Nick Hastings
6d85c09e69 Enable 64-bit builds for Blade Symphony. 2021-10-12 20:56:51 -04:00
Nick Hastings
6a8177145d Add .vs and .vscode dirs to .gitignore. 2021-10-12 20:38:44 -04:00
dysphie
baf686c6c4
Fix NMRiH's 'GivePlayerAmmo' offset on Windows (#1593) 2021-10-06 20:20:04 +00:00
naydef
34c5eed867
Update float.inc (#1591) 2021-10-02 20:23:43 +00:00
A1m`
59840685a4
Fix 'GiveNamedItem' in the game left4dead2. (#1590)
* Fix 'GiveNamedItem' in the game left4dead2.

The method 'CCSPlayer::GiveNamedItem(char const*, int, CBaseEntity*)' does not work in game left4dead2, any given weapon and object immediately falls to the ground, besides, the code is missing a parameter to call this method (even with the added parameter does not give a weapon to hands). Another method 'CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*)' works great, besides it makes it possible to give out all the items that exist in the game.

* Add x64 support

Add x64 support

* Changed argument type to bool.

Changed argument type to bool.
2021-10-02 14:45:18 +00:00
David Anderson
c4e33cab8c Update bootstrap.pl 2021-09-27 20:27:21 -07:00
David Anderson
bf0741e48a Use master for Metamod:Source builds. 2021-09-27 20:27:21 -07:00
David Anderson
51b5bbac0d Don't require hl2sdk-mock if sdks=all 2021-09-27 17:11:33 -07:00
pedrotski
03699a4a9e
Update CS:GO Reload gamedata (#1585) 2021-09-23 11:44:15 +01:00
nosoop
57a38636fc
TF2Tools: Prevent CalcIsAttackCriticalHelper* from being called twice (#1573)
* Ensure CalcIsAttackCriticalHelper only gets called once

* fixup: move origReturnValue up
2021-09-23 12:00:52 +02:00
David Anderson
a5f99c57e1 Add support for hl2sdk-mock. 2021-09-22 12:36:44 -07:00
David Anderson
1b0c7bc1b8 Add support for hl2sdk-mock. 2021-09-22 12:36:44 -07:00
GAMMACASE
d84fd76070
Update gamedata after latest CSGO update (22/09/21) (#1583) 2021-09-22 09:37:22 +01:00
Nick Hastings
677697a168 Trigger build for TF2 SDK INetChannel changes. 2021-09-16 22:07:57 -04:00
Nick Hastings
0fd4fa7066 Trigger build for TF2 SDK changes. 2021-09-16 21:05:01 -04:00
Bara
0932d78d26 Update sourcepawn 2021-09-13 23:44:14 -07:00
Vladimir
106f807b68
Fix crash in FindSendPropInfo() when the prop was DPT_DataTable (#1575) 2021-09-06 18:42:04 -07:00
Asher Baker
c3e6428ff7
Revert "Switch internal SM concept of frames to use Think (#1540)" (#1572)
This reverts commit b383302128.
2021-08-25 19:59:57 +01:00
Asher Baker
78cb89938d
Remove OnEntitySpawned forward (#1571)
`OnEntitySpawned` is 1.11 only, so this is fine given our API stability guarantees.

Unfortunately the forward name clashes with quite a few plugins using the same name for their SDKHook callback. Normally we'd just put up with this but there are difficult to solve binary compatibility issues where those plugins will get the callback double-called, and there is a separate issue where the forward isn't called for all entity spawns (unlike the SDKHook), so most plugins can't switch to the forward anyway.

Resolves #1558.

This reverts commit 7bab9cc344.
2021-08-23 21:36:20 +01:00
Asher Baker
f503139fae Update SourcePawn 2021-08-23 21:33:05 +01:00
Asher Baker
5ea096e61e
Support reading legacy sendprop arrays (#1550)
This rounds out the work started in #1548 to complete support for
reading the older SendPropArray type array netprops, along with bringing
SDKTools' GameRule netprop code in sync with core to add string array
support.

There aren't many SendPropArray type props around but this opens up a
few interesting opportunities for plugin developers, particularly in
L4D2 with manipulation of the EMS HUD.

Tested reading the `m_vCPPositions` array in TF2, and reading/writing
the `m_szScriptedHUDStringSet` EMS HUD netprop in L4D2. Closes #1386.
2021-08-23 21:21:11 +01:00
Vladimir
f4ff2ad45a Add missing return values in plugins 2021-08-21 14:11:20 -07:00
David Anderson
4fb57dc4e3 Update SourcePawn. 2021-08-07 16:46:53 -07:00
Asher Baker
b8c5303b05
Always use our cached name value (#1544)
In #545 we started automatically fixing up invalid UTF8 characters
caused by truncated names from Steam, but since the dawn of time CPlayer
has preferred directly returning the engine's name pointer if we have
once available, so our corrected name is almost never used.

Lightly tested in CS:GO and TF2 with no ill effects. Fixes #1315
2021-08-02 11:58:05 +01:00
Asher Baker
3c79701208
Catch exceptions from TraceRay filters/enumerators (#1557)
When a custom TraceRay filter / EnumerateEntities enumerator callback
throws an exception we currently continue execution and then return
execution to the calling code as if there were no problems. This
currently causes a heap tracking issue in SourcePawn, but even ignoring
that it is likely the wrong behaviour and differs from our other
synchronous callbacks.

This change causes the exception to be caught, immediately terminates
the trace / enumeration, and propagates the exception state back to the
calling plugin correctly. The implementation here is based on how
SortCustom1D handles exceptions.
2021-08-02 11:57:36 +01:00
David Anderson
e82f88dfa9 Update SourcePawn to 1.11-dev tip of tree. 2021-08-01 14:21:48 -07:00
Asher Baker
296deb95e6
Return array type info with FindSendPropInfo (#1548) 2021-08-01 19:44:18 +01:00
Asher Baker
c6917296d3
Fix out of bounds write in CDataPack::Write*Array (#1554)
WriteCellArray and WriteFloatArray were allocating N+1 slots, but due to
a copy-paste error were writing N+2 slots. Much later in the process the
CRT would catch this and cause a crash - this was pretty painful to
debug but thankfully running SRCDS in CRT debug mode caught it much
sooner in CDataPack::RemoveItem.
2021-07-28 22:19:16 +01:00
Maxim Telezhenko
b3672916de
Reduce code size for strict dependencies on mapchooser (#1528) 2021-07-22 14:37:43 +02:00
David Anderson
7c3cb49dfa Fix minimal rebuild. 2021-07-20 16:39:48 -07:00
Asher Baker
b383302128
Switch internal SM concept of frames to use Think (#1540)
This has been asked for and debated in some form since Valve introduced
hibernation into the Source engine. The changes here are based on quite
a deep dive into the engine's frame/think logic (mainly in CS:GO which
has "legacy" hibernation and TF2 which has modern "frameless" ticking)
and all seem to be sane.

I think I've managed to maintain all the oddities around time keeping,
and the simulated bool (even though we don't really use it for anything)
should have a sane value. There is a slight behaviour change for
anything needing exact timings as we're now run earlier in the frame
before gpGlobals are updated, this should generally be fine but it might
affect some plugins such as bhop timers that are trying to be extremely
precise (often more precise than the underlying data they're using).

We'll probably want to add a native for plugins to detect if the server
is not completely simulating so they can opt out of work, but I think
defaulting to having things work like this makes more sense than adding
a 2nd set of per-frame forwards and natives (#540), and this makes
timers and any extension callbacks work automatically.
2021-07-19 19:12:09 +01:00
Asher Baker
32d951e312
Detect invalid menu item selections in L4D-based games (#1543)
Some games have implemented CHudMenu::SelectMenuItem to close the menu
even if an invalid slot has been selected, which causes us a problem as
we'll never get any notification from the client and we'll keep the menu
alive on our end indefinitely. For these games, pretend that every slot
is valid for selection so we're guaranteed to get a menuselect command.
We don't want to do this for every game as the common SelectMenuItem
implementation ignores invalid selections and keeps the menu open, which
is a much nicer user experience.

Fixes #1385
2021-07-18 20:57:13 +01:00
Asher Baker
f8f5a18d67
Fix vprof crashing in some games (#1541)
Some engines are very sensitive to exactly when in a frame vprof is
enabled, the vprof commands use a special command registration method
to defer their execution to the start of the next frame. Instead of
starting/stopping vprof directly ourselves, use the built-in commands
to ensure that the timing is correct and the server does not crash.

Fixes #1162
2021-07-18 19:08:36 +01:00
Asher Baker
2d241316c7
Make all command lookups case-insensitive (#1542)
SM internally maintained both a case-sensitive and a case-insensitive
lookup method for commands, where the case-sensitive hashmap was used as
a fast path, and case-insensitive iteration over a list used as the slow
path if a command was not found in the hashmap. But only command
dispatch handling used this dual path approach, chat triggers for
example only did a loopup in the hashmap.

Over the years Valve has made more and more of the command dispatch
logic case-insensitive to the point where all console commands are now
case-insensitive, so maintaining case sensitivity when using chat
triggers does not make a lot of sense. There are somewhat popular
plugins that attempt to "correct" this behaviour - but at least one is
having issues after the previous case-sensitivity fixes for commands -
see #1480.

We still have to keep the list around for the sorted help use case and
command iteration, but this PR changes the hashmap to use a
case-insensitive hashing policy (as previously done for convars, and
more recently for game command lookup) and changes all by-name lookup to
exclusively use the hashmap (as there is no need to fall back to the
list any more).

Tested a bunch in TF2, I don't know of any games that still have a
case-sensitive command dispatch pipeline to test. I think the worst case
would be that we'd accept a chat command in the "wrong" case then fail
to execute the underlying command. If that turns out to be an issue in
practice, we should be able to fix it easily enough by replacing the
command name in the buffer with the correct casing of the command we
looked up.

Also fixed a couple of very minor Lookup vs. Key issues in NameHashSet
(noted in #1529) that were being masked due to CharsAndLength's
converting constructor. I tried to make the constructor explicit to
avoid this happening in the future but HashTable's add function relies
on being able to do an implicit conversion so that wasn't possible. We
might want to just rely on the implicit conversion up here as well, but
it doesn't really matter either way.

Fixes #1480, #1529
2021-07-18 19:05:06 +01:00
Asher Baker
6a2ac9800b
Track the creating plugin for convars (#1537)
Similar to the recent work for commands, track and expose the creating
plugin for convars. The first plugin to register a given cvar becomes
the owner until that plugin is unloaded. If a plugin attempts to
register a convar that was already registered and the originally
registering plugin has been unloaded, that plugin becomes the owner.
This isn't quite as nice as the way commands shift "ownership" as
plugins are unloaded, but we don't have a sane data structure currently
to implement that, and it seemed like a lot of unnecessary work as there
shouldn't really be multiple plugins with conflicting cvars.

Closes #1492
2021-07-18 17:19:27 +01:00
Asher Baker
39aa75436e
Fix reading/writing string_t array netprops (#1538)
When a netprop is an array the name resolves to the outer DataTable
array, which we then need to recurse into to find the actual prop.

For string_t props we need their sendprop info to call the proxy
function to get their real storage address, but when accessing an array
we were trying to read the prop off the outer DataTable prop, rather
than the real string_t prop. Fix this by using the pProp variable that
FIND_PROP_SEND helpfully provides for us.

Tested by writing/reading the `m_szCrosshairCodes` array, which got
changed to a string_t prop sometime since #1372.

Fixes #1484
2021-07-18 02:01:46 +01:00
Vladimir
f708842e09
Fix conflict with extensions using SteamWorks (#1531)
We were accidentally changing a process-wide global variable when trying to fetch a working ISteamGameServer interface.

Co-Authored-By: komashchenko <komashchenko@users.noreply.github.com>
2021-07-18 00:01:24 +01:00
Asher Baker
86af9601bd
Fix reading/writing float variant-based props (#1536)
When variant support was added for props, the validation checks in the
float related functions weren't updated to allow them.

Tested with the plugin from the forum thread with a spawned
`math_counter`.

Fixes #1501
2021-07-17 20:53:43 +01:00
Asher Baker
5b7c9c5845
Handle detour patches across page boundaries (#1535)
On Linux if a detour crossed a page boundary we would only change the
memory protection of the first page (as we were aligning the address as
required, but not taking into account the length).

I don't have an easy way to test this but it looks correct. `addr + len`
doesn't appear to need to be aligned though, so another option could be
to use `(addr - startPage) + length` as len.

Also fixed a non-zero offset being passed into CDetour's ApplyPatch
function - this is never done internally anywhere, but it doesn't hurt
to fix it.

Fixes #984
2021-07-17 20:53:25 +01:00
Asher Baker
7f2fdf3fe1 Add OnMapInit forward and deprecate OnLevelInit
The change in behaviour to the OnLevelInit forward params isn't obvious
when compiling a plugin, deprecate it to make it a lot more obvious that
something has changed.

Some plugins rely just on the timing of OnLevelInit rather than doing
anything with the entity lump, for these plugins offer a new OnMapInit
forward that is implemented in core rather than sdkhooks. If / when we
offer a new entity lump manipulation API in the future this'll be the
forward where it can be used to make changes.
2021-07-17 20:52:51 +01:00
Asher Baker
70c9a6528a Remove entity lump manipulation from OnLevelInit
Newer Source engine versions now use a dynamically allocated buffer for
the map entity lump, and some maps have over 16MB of entity data - far
larger than our 2MB limit.

There is no sane way we can currently handle this, so just remove the
functionality from the forward until a more comprehensive API can be
designed.

Fixes #1470
2021-07-17 20:52:51 +01:00
Vladimir
54364d213d
Fix output hooks when caller/activator are flipped (#1411)
Co-authored-by: Asher Baker <asherkin@limetech.io>
2021-07-17 15:30:09 +01:00
XeroX
4d6b9895d3
Use display name for currentmap chat trigger (#1512)
This increases the buffer for the map to be consistent with the other calls to GetCurrentMap. Also `currentmap` now uses the map's display name similar how `nextmap` uses it.
2021-07-17 11:46:22 +01:00
komashchenko
f927455778
Removing old GeoIP.dat (#1533) 2021-07-17 11:32:02 +01:00
David Anderson
6928d21bcf Fix crash in FrameIterator. 2021-07-16 18:33:10 -07:00
David Anderson
38eecd5ece Switch to python3 for ambuild. 2021-07-11 22:35:01 -07:00
Adam Short
5aedb73aae
sdktools: Rename SetCollisionGroup to SetEntityCollisionGroup (#1513)
Better aligns the with other natives - makes more sense.
2021-07-10 13:10:26 -07:00
nosoop
387b85406e
logic: Add ability to skip mprotect with StoreToAddress (#1523)
* Implement StoreToAddress param to optionally set memory page permissions

* Update comment
2021-07-10 12:45:00 -07:00
Einyux
823b55c22a
sdkhooks: Allow overriding SDKHook_Think (#1397)
* Allow to block Think()

* Update SDKHookCB comment
2021-07-10 12:42:17 -07:00
Asher Baker
fdfb8837d1 Update SourcePawn 2021-07-06 10:12:23 +01:00
Asher Baker
d7c359c412 Update SourcePawn and AMTL 2021-07-01 22:40:03 +01:00
Asher Baker
82c51dbe75
Fix sm_dump_admcache with command group overrides (#1519) 2021-07-01 22:19:50 +01:00
Asher Baker
77259acf9e
Add SourcePawn debug metadata options to core.cfg (#1412) 2021-06-30 23:20:28 +01:00
Asher Baker
7816379aae
Update SourcePawn (#1518)
This fixes a number of parsing issues and compiler crashes, deprecates enum multipliers, fixes relative include paths on Windows, adds a `static_assert` statement for compile-time checks, and introduces new Linux profiling support.

The relative include paths change will probably break some non-portable projects that relied on the old broken behaviour, but the fix is straightforward. The `#include` directive is now always relative to the file where it is used, whereas on Windows it was previously relative to the initial input file passed to the compiler if `/` was used instead of `\` in include paths.
2021-06-30 18:29:29 +01:00
peace-maker
d192527707
Fix unpacking of GeoLite2 database (#1516)
Apparently you can't unpack and uncompress in one go in Windows and tar -C doesn't affect the input file.
2021-06-30 12:32:40 +01:00
Accelerator
b0563a493c
Update GeoIP2 extension to new GeoLite2 .mmdb database format (#1245)
* Add support for Maxmind GeoIP2 database files (#913).

* Copy/paste error.

* Mark GeoipCode3 as deprecated.

* Fix build when compiling with AMBuild.

* Replace loose libmaxminddb files with submodule.

* Fix Linux build.

* One more hack for submodule.

* Actually fix Linux build.

* GeoIP2 extension to sourcemod

* Update basevotes

When a player leaves during a voteban, he will be banned anyway. Also added a cvar with a ban time setting.

* Update basevotes.sp

* Update AMBuilder

* ke::AString to std::string

* Update extension.cpp

* Update AMBuilder

* Added coordination natives

Added GeoipLatitude, GeoipLongitude, GeoipDistance natives.

* Create osdefs.h

* Update maxminddb_config.h

* Update extension.cpp

* Update extension.cpp

* Added automatic search for database file

* Fix automatic search for database file

* Update extension.cpp

* Update geoip.inc

* .gitmodules revert

* Update geoip.inc

* Update libmaxminddb to version 1.5.2

* Update extension.cpp

* Check language in the DB

* Removed langCount variable

* Determination of the client's language

* Update geoip.inc

* Update geoip.inc

* Update extension.cpp

* Update geoip.inc

* Update extension.cpp

* space instead of tab in .inc

* Update extension.cpp

* Update geoip.inc

* Optimizing length measurement region code

* Update package script to fetch the new GeoLite2 database

This package is the last CC-BY-SA licensed GeoLite2-City database extracted from https://src.fedoraproject.org/rpms/geolite2 from december 2019.

This doubles the download size for SM packages, but it's what we have to deal with atm :(

* Fix potentially returning uninitialized memory in GeoipRegionCode

If the lookup failed, we'd copy back whatever is on the stack in the ccode buffer.

Co-authored-by: Nick Hastings <nshastings@gmail.com>
Co-authored-by: Headline <michaelwflaherty@me.com>
Co-authored-by: Accelerator74 <dmitry@447751-accele74.tmweb.ru>
Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2021-06-30 13:01:12 +02:00
Stanislav Polshyn
c38d45b9e7
gamedata: SetCollisionGroup signatures for L4D (#1514) 2021-06-29 01:13:56 -07:00
Vladimir
f603b7aec3
Add StringToInt64() and Int64ToString() natives (#1511) 2021-06-28 21:51:49 +01:00
Adam Short
a9d3cf4574
sdktools: Add SetCollisionGroup native (#1461) (#1507)
* Add SetCollisionGroup native

* Add newline to end of file

* Fix gamedata locations

* Remove extra spaces

* Fix gamedata formatting

* Add Windows signature for CS:GO

* Fix native doc comment

* Revert formatting change

* Change references of client -> entity

* Moved CallWrapper into method

* typofix

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2021-06-24 15:04:10 -07:00
rtldg
3b2fa89926
sdktools: Clean non-ingame clients from NormalSHook (#1450) 2021-06-24 14:32:47 -07:00
FlaminSarge
0d956b229f
Update basetriggers ShowFriendlyFire to use the same chat text visibility system as ShowTimeLeft (#1494)
This system is a bit sketchy but I didn't want to rework the entire thing. (If this were to be reworked, it should probably use ReplyToCommand for a lot of this).

This also allows the server to call ff and get a result.
2021-06-24 15:45:56 +01:00
Natanel Shitrit
d73f4e7eca
Documentation corrections and additions (#1498)
* Added `@error` documentation for every function that's missing it.
* Changed tabs to spaces (text alignment)
2021-06-24 15:45:01 +01:00
Accelerator
2778b132f0
Fix matchmaking_ds for gamedata (#1504)
* Fix matchmaking_ds for gamedata

* Update logic_bridge.cpp

* More corrected search for matchmaking_ds

* Fix compile error

* Better method for find matchmaking_ds

Code by psychonic https://github.com/alliedmodders/sourcemod/pull/1504#issuecomment-867310412
2021-06-24 14:00:59 +00:00
Mikusch
1eae765dc3
Update Team Fortress 2 gamedata (#1509) 2021-06-22 19:19:06 +01:00
Vladimir
267eb90da5
Fix not working MENU_DEBUG (#1506)
Replace g_Logger to logger for MENU_DEBUG
2021-06-21 12:11:35 +01:00
Sebastian K
8075ce8371
Add FlagBitsToString - Converts a bit string to a string of flag characters (#377)
* Add new method - Converts a bit string to a string of flag characters

* New syntax

* Set tags

* Fix tags

* Change method name

* Remove for - set null only once
2021-06-17 13:44:50 +02:00
nosoop
d01c72f79b
Update CGameConfig members to use std::string instead of fixed-size char arrays (#1495) 2021-06-03 11:27:16 -05:00
Vladimir
8f73e5e5a1 Update PluginSys.cpp 2021-05-24 14:30:16 -07:00
Vladimir
62142197c1 Add OnNotifyPluginUnloaded forward 2021-05-24 14:30:16 -07:00
Vladimir
ff43e60831 Revert "Add HookPluginUnload() and UnhookPluginUnload() functions"
This reverts commit 2c2d219f3f07d31ba84aa88acfae353c82e75dec.
2021-05-24 14:30:16 -07:00
Vladimir
6b9037790a Revert "Update smn_core.cpp"
This reverts commit ec8b9e1d51b91ca7dd56a4b63bdb732fe944dcee.
2021-05-24 14:30:16 -07:00
Vladimir
babc6abc64 Revert "Update PluginSys.cpp"
This reverts commit dd10098844562112c4b57e97da6b20e4cf4ff30b.
2021-05-24 14:30:16 -07:00
Vladimir
66d932c79f Revert "Update sourcemod.inc"
This reverts commit dbaa470e074fb3716ff4b467f6148c90b50d4220.
2021-05-24 14:30:16 -07:00
Vladimir
7be2f50a98 Update sourcemod.inc
Typo
2021-05-24 14:30:16 -07:00
Vladimir
499f7160a4 Update PluginSys.cpp
Fix not passing Plugin Handle
2021-05-24 14:30:16 -07:00
Vladimir
7aca0cc77f Update smn_core.cpp
Fix parameter number
2021-05-24 14:30:16 -07:00
Vladimir
c874703136 Add HookPluginUnload() and UnhookPluginUnload() functions 2021-05-24 14:30:16 -07:00
Impact
e6129ab2d9
Update FindEntityByClassname docs (#1491)
Makes it a little bit clearer that `startEnt` isn't just an index to start searching from and that passing an invalid one can throw an error
2021-05-21 09:45:42 +01:00
Impact
4a43ac1bdd
entities.inc: Clarify EntIndexToEntRef / EntRefToEntIndex errors (#1370) 2021-05-20 16:07:11 -07:00
Natanel Shitrit
6b7fd8c3dc
clientprefs: Add defines for Cookie name and description max length (#1463) 2021-05-20 15:52:52 -07:00
peace-maker
845c20ad93
Start using Github Actions (#1488)
* Start using Github Actions

Build on windows and linux. Cannot build for macos,
since the builders only support xcode 10+ which dropped
x86 support.

* Build sourcepawn tooling as separate package

Upload build artifacts containing only spcomp and the includes.
This adds a new `--scripting-only` flag to configure.py which skips
everything and goes straight to building spcomp and packaging the
include folder with it.

* Only run the workflows for the master branch

* Split common operations into PackageHelpers file

Don't duplicate the code for packaging releases for the tooling-only packages. Instead use a common `PackageHelpers` class which provides the functionality common to both packages.

This replaces the explicit list of files to package with a directory scan, so we don't have to list them all.

The pgsql sql-init-scripts were missing from the release package before, so they were added here as well. Three scripts from the testsuite were missing from the explicit list (mapdisplayname, floats, findmap), so they're now included.

* Fix Python 2 compatibility

os.scandir is Python 3 only.
2021-05-09 01:46:08 +02:00
Vauff
47c050d5b6
Update TerminateRound signature for CS:GO windows (#1486) 2021-04-29 01:24:26 -07:00
peace-maker
1fbe5e1daa
A2S_Rules fix: Only change host_rules_show if patching was successful (#1459)
If lookup of the patch location failed, don't turn on responding to A2S_Rules queries. Otherwise we'll see the log getting spammed with too large packet sizes again. #1447
2021-04-19 09:33:36 +02:00
yourmnbbn
2b1cc43355
Correct documentation in adt_trie.inc (#1465) 2021-04-04 15:11:30 -07:00
Arthurdead
62cb6a0458
Add sdk'less sample_ext and update -std to 14 (#1456) 2021-03-29 22:12:45 +01:00
Arron Vinyard
ca1dcc9bed
Correct spelling in sample_ext build script (#1454)
I have optimizaied the spelling
2021-03-24 21:05:14 +01:00
Vladimir
5a72644486
Add ConVar.GetDescription() method (#1449)
* Add ConVar.GetDescription() method

Issue #1432

* Update basecommands.sp

Add prints description of ConVar

* Revert "Update basecommands.sp"

This reverts commit ad485069a837f602bdeeeb50f9e02452b3860ecd.

* Remove GetConVarDescription() function
2021-03-21 15:10:17 +01:00
Vladimir
e552466886
core: Call SetGlobalTarget in PrintToConsole (#1448)
Issue #1443
2021-03-20 22:24:06 -07:00
Corey D
65043baf5d
gamedata: Add support for hexadecimal offsets (#1426)
* Allow hexadecimal and octal offsets

* Fix typo

* Allow hexadecimal input in Addresses "read" and "offset" values

Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2021-03-15 12:46:04 -07:00
Deathreus
46c54f829c
core: Call ConVarQueryFinished on client disconnect (#1384)
* Execute ConVarQueryFinished if client disconnects

So people passing handles to the extra data can close them, adds a new return value to check if this happened

* Update based on suggestion

Co-authored-by: Asher Baker <asherkin@limetech.io>

* Update based on notes

* Normalize

* Pass along cookie handle instead of invalid

Co-authored-by: Asher Baker <asherkin@limetech.io>
2021-03-15 12:44:03 -07:00
steph
4dd5ab7576
Prevent duplicate map changes in randomcycle.sp (#1428) 2021-03-14 01:02:42 -08:00
MartLegion
ee27a48714
Update clientprefstest.sp to newdecls (#1442) 2021-03-13 23:41:36 -08:00
peace-maker
e5342afe2a
Add PostgreSQL database driver (#32)
* Add PostgreSQL DBI extension

Bug #3849
This adds a postgresql database driver called "pgsql".
The ambuild script changes could be very wrong ;)

* Add pgsql support to clientprefs

This was originally made by Lyfe in bug 3849! Thanks for that.
PostgreSQL supports the 'IF NOT EXISTS' clause when creating tables
since version 9.1, so i've switched to use that.

* Added pgsql support to sql-admin-manager

* Add --pgsql-path to ambuild configure

* More ambuild fixes

* Compile libpq with ambuild

* Try to generate postgres configs

* Add dummy config file

* More ambuild fixes

* Get errorcode and readable error in preparequery

No need to stop getting the errorCode, if the error string should be
retrieved. Just return both!

* Fix indentation in AMBuilderPGSQL

* Try to patch and configure postgres through ambuild

* Revert "Try to patch and configure postgres through ambuild"

This reverts commit 68dfc82b8eb0ce11f621af67ec247b5f47e4a189.

* Update to use PostgreSQL 9.4

* Move postgres preparation into seperate script

Fetching, preparing and configuring of the postgresql sourcecode
is done in a seperate prepare_postgresql.sh script now.

People can use this script for their manual builds,
if they don't want to use checkout-deps.sh.

* Add patch to configure.in again

Remove the version check for autoconf in postgres 9.4 too.

* Nit: Support older objdirs for hasPgSql ambuild option

* Update to use PostgreSQL 9.4.6

* Use newer AMTL and PGSQL 9.6

* Fix threaded queries

When running the thread part of a query, it already gets the atomic
lock. Don't try to get it twice in that case.

Use a seperate lock to protect access to the lastInsertID and
lastAffectedRows variables.

* Fix SetCharacterSet racing with threaded queries

Same fix like in the mysql driver.

* Use ke::AutoLock for lastInsertID mutex

* Fix fetching one more row than available

Don't try to access a row that doesn't exist when iterating rows.

* Improve sanity checks on column access

Don't call libpq functions with out of bounds column indices.

* Let QuoteString return false if quoting failed

* Fix build for x64 support changes and update to PGSQL 9.6.9

* Fix linux build

* Fix building of connection options string

snprintf doesn't support overlapping buffers.

* Update to PostgreSQL 9.6.15

* Fix crash after freeing first IQuery object

* Fix code crunch

* Fix memory leak, cleanup

* Nuke MSVC project and Makefile

* Remove unsupported numeric error codes

* Use STL threads

* Add prebuilt libpq for Linux and Windows

* Restore and fix version.rc file

* Add PostgreSQL build instructions

* Add prebuilt libpq for Mac

* Add win64 libpq build

This is version 9.6.15 since that's what I still had laying around.

* Fix buildscript

* Fix x64 build on linux and mac

Co-authored-by: Erik Minekus <tsunami@tsunami-productions.nl>
2021-03-11 11:21:51 +01:00
Peace-Maker
3c30f7b971 Fix crash when creating threads with Thread_AutoRelease
Setting the Thread_AutoRelease flag (default when using IThreader::MakeThread) caused a use-after-free after running the thread body.
2021-03-11 09:48:39 +01:00
Headline
99c39b1d57
Update IPlugin reference on cmd hook removal (#1439) 2021-03-10 15:21:57 -08:00
Nicholas Hastings
ae485c3115
Add A2S_Rules fix for CS:GO to CStrike extension. (#614)
* Add A2S_Rules fix for CS:GO to CStrike extension.

* Dont force set the cvar. Add checks before patching.

* Remove incorrect and useless check.

* Remove checking value as it is in the signature.

* Update build script changes to AMBuild 2.1 API to fix build.

* Fix bad check and ConVarRef being resolved too early.

* Whoops. This line is kinda important. Got nuked when refactoring.

* Remove unused variable.

* Updatet gamedata.

* Switch to "Addresses" gamedata lookup

* Add linux64 gamedata

* Fix mac build

Co-authored-by: Ruben Gonzalez <drifter01620@gmail.com>
Co-authored-by: Peace-Maker <peace-maker@wcfan.de>
2021-03-08 22:42:32 +00:00
Scags
6c2be9bdcc
Add sm_dump_datamaps_xml (#1409) 2021-03-08 14:28:43 -08:00
BotoX
1a11d92fd9
core: Add client id to MultiTargetFilter forward (#1070)
* Added client id to MultiTargetFilter forward.

* Add CapabilityProvider for MultiTargetFilter client param
2021-03-08 10:04:11 -08:00
Loïc
52c4d08e15
Improve logging on map/generic votes (#1362) 2021-03-08 05:02:20 -08:00
peace-maker
6ea1e39ee4
core/sm: Harden plugin loading path requirements (#1437)
* Harden plugin loading path requirements

Restrict loading of plugins to the `sourcemod/plugins` folder and require the `.smx` file extension.

Symlinks inside the `plugins` folder are fine.

This behavior was abused as part of justCTF 2020 in the PainterHell challenge by cypis. Thank you!

* Restrict extension loading to extensions folder

* Add NULL file extension check in LoadExtension

hi @KyleS
2021-03-07 14:33:33 -08:00
Deathreus
f9633a5f6f
Universalize a single call to srand() on map init (#1422) 2021-03-07 04:54:27 -08:00
peace-maker
5bc48b2ab3
Prefer python3 over python(2) in checkout-deps (#1424)
* Prefer python3 over python(2) in checkout-deps

We require Python 3 and don't want to accidentally use Python 2 if both are available.

* Use python3 in travis build

* Install python3-pip package on travis trusty image

* Fix pip detection in checkout-deps.sh
2021-03-07 13:50:15 +01:00
Arron Vinyard
70b6e96f95 Fix documentation constant reference 2021-03-07 13:45:16 +01:00
Peace-Maker
91a1fd074b Fix sql injection in sql-admin-manager plugin
This bug was found as part of justCTF 2020 in the PainterHell challenge by cypis. Thank you!

Admins with the root flag could inject their own queries towards the admin database connection.

The sql-admin-manager plugin is disabled by default.
2021-02-02 11:20:02 +01:00
Impact
a615c139e6
Update note about non-existing SQL_MoreResults (#1416) 2021-01-14 14:50:15 +00:00
komashchenko
978fa6b252
Fix detour HandleCommand_Buy_Internal (#1406) 2020-12-18 12:10:10 +00:00
c0rp3n
24f90449ad [CS:GO] Remove control chars from gamedata 2020-12-16 16:17:14 +01:00
Peace-Maker
ba8753836e Cleanly remove all hooks on extension unload
Allows to e.g. reload sdktools during runtime without crashing. Useful for fast development cycles.
2020-12-16 15:52:48 +01:00
kidfearless
e59fd9de96 Fix missed old syntax parameter in menus 2020-12-13 20:12:07 +01:00
Loïc
6c4079cb94 Fix error description
As coded in aae7161273/core/logic/smn_players.cpp (L1405), its a "client not in game", not "client not connected".
2020-12-13 20:10:11 +01:00
Natanel Shitrit
c2b806563e
Update 'CScore' offset for CS:GO (#1394) 2020-12-04 17:13:10 +00:00
Vauff
15d912df38
Update TerminateRound signature for CS:GO linux (#1392) 2020-12-04 00:10:51 +00:00
Einyux
a191a907e9
Add new trie native: ContainsKey() (#1390) 2020-11-30 20:51:47 -08:00
Arron Vinyard
0caa349f6a
Update cookie funcs to return newdecl Cookie (#1379) 2020-11-07 13:16:29 -08:00
Headline
1cd0efad41
NPOTB: Fix no SDK target exception (#1359) 2020-11-07 13:14:58 -08:00
Mustafa Enes AKDENİZ
9d49bbfaf0
Fixed unauthorized menu usages (#1374)
* Fixed unauthorized menu usages

Let's say we have kick flag but don't have admin menu flag.
When we type !kick, menu opens with a back button, when we click back button, we go parent menu without admin menu flag

* Update ban.sp
2020-11-05 11:34:00 +01:00
Peace-Maker
3eb2cd2971 Remove verbose hl2sdk-X not found warning
None of the available usage options have any use for such a warning.
2020-10-28 20:55:00 +01:00
Nicholas Hastings
8e7d41ec02
Add NULL check to GetEntPropString return. (#1376)
This can be NULL for non-interned strings that don't have a value set.
2020-10-27 18:51:12 -04:00
Mustafa Enes AKDENİZ
94ea925152 Improvements on !admin menu flags (#1364)
* Improvements on !admin menu flags

Lets say we have override the sm_unmute command and changed it to ADMFLAG_CUSTOM1.
Then create an admin, we gived our admin ADMFLAG_Chat flag, admin can't use sm_unmute command cause it doesnt have access to this command.
But if admin go into "!admin" menu then, he will able to run sm_unmute on "player command" menus

* removed unauthorized menu items

* Deleted Whitespace and ITEMDRAW_DEFAULT
2020-10-24 23:52:35 +00:00
Nicholas Hastings
18d93ff677
Sendprop string fixes (#1372)
* Fix reading SendProp non-interned strings.

* Make Get/SetEntPropString for SendProps use same macros as other funcs.
2020-10-23 11:26:54 +00:00
David Anderson
d48cf93a94 Fix builder.target usage. 2020-10-22 23:41:01 -07:00
Mr. Silence
104b6b878a
[ZPS] Gamedata update for sdktools/sdkhooks (#1369)
* Gamedata update for ZPS 3.1

Signed-off-by: Mr.Silence <Silenci0@users.noreply.github.com>

* Offset updates for ZPS 3.1

Signed-off-by: Mr.Silence <Silenci0@users.noreply.github.com>

* Updated offsets and signatures.

Signed-off-by: Mr.Silence <Silenci0@users.noreply.github.com>
2020-10-20 03:58:35 +00:00
Erik Minekus
90b68f095f
Fix spcomp64 filename for Windows #1368 2020-10-19 19:02:20 +02:00
Asher Baker
6a4364d404
Update credits (#1367) 2020-10-16 21:41:21 +01:00
peace-maker
5e819f6596
Update SourcePawn (#1365) 2020-10-14 18:15:35 -07:00
Mikusch
eef96da641
tf2: Add TFCond_PowerupModeDominant (#1361)
* Add TFCond_PowerupMode_Dominant

* comma

* Consistency
2020-10-02 17:33:57 -07:00
Rostu13
c38b392fdf
sdktools: permit symbol signatures on win32 (#1346)
* Fix sig scanner for windows

* fix linux build

* Update vcaller.cpp

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-10-02 17:33:05 -07:00
David Anderson
e0d9dfb68e
sourcepawn: uplift FakeNative to DynamicNative. (#1338)
This removes calls to CreateFakeNative.
2020-10-02 16:42:31 -07:00
Scags
589d6df75d
gamedata: implement GetMemSig (#1345) 2020-10-02 16:40:13 -07:00
Scags
6fd9d1ce11
gamedata: automate reparsing on load (#1348)
* Add sm_reload_gamedata

* Remove redundant cast

* Automate gamedata reparsing

* Update GameConfigs.cpp

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-10-02 16:25:27 -07:00
Headline
ef36604666
datapack: remove legacy cache (#1357) 2020-10-02 16:04:30 -07:00
Headline
db56b14637
loader: preserve binary path for amd64 (#1358) 2020-10-02 16:01:30 -07:00
Scags
b14c18ee64
Fix invalid sm_dump_netprops_xml output (#1360) 2020-10-01 14:28:07 -07:00
Michael Busby
2c94ab3b1c
Update L4D2 Gamedata for 2.2.0.0 (#1351) 2020-09-24 18:52:24 +01:00
Sikari
f12b3a2e13
Fix "Command Group" override type admin flags (#1349) 2020-09-07 10:41:59 +01:00
Asher Baker
ea3f55f030
Remove OnEntitySpawned C++ listener (#1342)
This is causing crashes with existing extensions using entity listeners.

Currently no one is asking for a C++ hook here, if it is desired it
needs to be done with proper versioning of the listener interface, which
is going to be a little bit more complicated without an initial hard
break.

https://forums.alliedmods.net/showpost.php?p=2715337&postcount=789
https://discordapp.com/channels/335290997317697536/335290997317697536/748101258186850334

This is a partial revert of #1078.
2020-08-26 12:53:31 +01:00
David Anderson
6e2c5a66b3 Remove use of the Dep API.
This is going away.
2020-08-24 20:48:57 -07:00
David Anderson
0bed34e0c7 Add x64 to official Windows builds. 2020-08-20 23:17:21 -07:00
David Anderson
f0d8a70b38 Fix tabs in BreakpadSymbols. 2020-08-19 00:43:12 -07:00
David Anderson
9acf2b5cda Use Python 3.8 on the Windows buildbot. 2020-08-19 00:39:54 -07:00
David Anderson
d49b92603a Another BreakpadSymbols fix. 2020-08-19 00:12:40 -07:00
David Anderson
aa01a22416 Fix BreakpadSymbols again. 2020-08-19 00:09:08 -07:00
David Anderson
50e43a9f98 Fix BreakpadSymbols. 2020-08-18 23:45:30 -07:00
David Anderson
ed325c7208 Trigger a full rebuild. 2020-08-18 23:42:36 -07:00
David Anderson
aac2c4a080 Trigger full reconfigures when requested. 2020-08-18 23:29:24 -07:00
David Anderson
785c6aa1cf Update to AMBuild 2.2.
This is a pretty big diff because SourceMod had lots of multi-arch
workarounds that can now go away. I've also changed 'x64' to 'x86_64' in
many places since this is how AMBuild normalizes it, and it's far too
late to pick the shorter string, so we might as well suck it up.

The --target-archs parameter has been replaced with --targets. It works
the same way.

The default behavior for SDK inclusion is now "present" instead of
"all" since this lowers the burden of storing many SDKs. Official builds
will still be made with --sdks=all.
2020-08-18 23:09:43 -07:00
Accelerator74
1b86c8564d
sdktools_voice: implement ClientSpeaking forwards (#1247)
* Update basevotes

When a player leaves during a voteban, he will be banned anyway. Also added a cvar with a ban time setting.

* Update basevotes.sp

* Added VoiceHook functions

* Update extension.h

* Update extension.h

* Code optimization

* Added support for more engines

* Invalidate timers

* Fixes

* Update extension.cpp

* Update voice.cpp

* correct Forward event type to ET_Ignore.

* sdktools_voice: de-implement IsClientSpeaking

* sdktools_voice: Add notice/unbind IsClientSpeaking

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-08-18 06:02:34 -07:00
PerfectLaugh
031f80f6e1
sdkhooks: assign velocities in CTakeDamageInfo(Hack) (#1322)
* Attempt to correct SDKHooks_TakeDamage

* Define function on both csgo and non-csgo

* Remove unneeded macros

* Fix velocity copy on CTakeDamageInfoHack init
2020-08-06 20:46:01 -07:00
BotoX
6f21138489
menusys: add MenuShufflePerClient native (#1073)
* Implement per-client randomized menus with MenuShufflePerClient native.

* Add MenuSetClientMapping native.

* fix remaining issues

* fix build issues from rebase

* Update MenuStyle_Base.cpp

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-08-06 20:31:00 -07:00
BotoX
7bab9cc344
sdkhooks: add OnEntitySpawned fwd. (#1078)
* Add OnEntitySpawned to SDKHooks.

* nitpicking

* Add CapabilityProvider SDKHook_OnEntitySpawned

* (unrelated) nits

* nit relocation

* unqualified relocation - my mistake.

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-08-06 20:01:07 -07:00
peace-maker
510bd261f8
core: Add Insurgency support for amd64 Windows (#1295)
* Add basic Insurgency support on Windows x64

This allows SourceMod to load on x64 Insurgency. There are still a lot of variable truncation warnings that have to be dealt with.

* Fix 32bit builds

* Compile MySQL extension as well

The hack for __iob_func being removed from the core runtime, but required by the old mysql we're building against can be simplified a lot due to the `_ReturnAddress` intrinsic available since MSVC 2015.

* Don't include the offset we want to extract in the signature
2020-08-06 19:53:06 -07:00
nosoop
c5619f887d
core: Add support for networked CUtlVector (#1330)
* Add offset-reading capability for networked CUtlVector instances

* Use strncmp instead of strstr

* Use strcmp instead of strncmp

Co-authored-by: Asher Baker <asherkin@limetech.io>

* Move CSendPropExtra_UtlVector offset to gamedata

* Update name of offset + add gamedata entry

* tiny nits

Co-authored-by: Asher Baker <asherkin@limetech.io>
Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-08-06 19:15:20 -07:00
Kruzya
bb25b03884
Fix parameter name in SQL_SetCharset documentation (#1329) 2020-08-03 11:22:30 +01:00
WildCard65
9bbbf60268 Updated 'checkout-deps' to check for 'pip'/'pip3' prior to installing 'AMBuild' 2020-07-31 15:49:44 -07:00
Peace-Maker
37355f9c57 Remove cloning of Dota 2 SDK in CI
SourceMod doesn't support Source 1 Dota 2 anymore and neither Source 2. Speed up CI by only cloning the hl2sdks that are tested.

This adds a `-s sdk1,sdk2` parameter to the checkout-deps.sh script similar to the `-SDKs` option of the powershell script to select specific sdks to update instead of all.
2020-07-30 11:37:00 -07:00
PerfectLaugh
af76f757b5
Update SDKHooks_TakeDamage for CS:GO changes (#1319)
See alliedmodders/hl2sdk#77
2020-07-25 03:45:35 +01:00
Headline
b7650b11d6
NPOTB: Add x64 builds to travis-ci (#1321) 2020-07-24 16:48:22 -07:00
Headline
a0d06b3209
Fix Linux x64 libpcre.a linkage failure (#1320) 2020-07-24 16:29:43 -07:00
PerfectLaugh
c52edbd863
NPOTB: Trigger hl2sdk-csgo changes (#1316) 2020-07-24 00:19:58 -07:00
David Anderson
7355e34946 Fix AppVeyor. 2020-07-15 20:18:55 -07:00
Headline
5fa25e70ad
trie: implement clone() method (#852)
* Add Clone() for StringMap

* Fix for std::string addition

* trie: broken return key.

* clonetrie: correct handle leakage.

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-07-14 20:11:23 -07:00
Kyle Sanderson
b8ae4e617b
NPOTB travis-ci: address warnings / info. (#1311)
* Update .travis.yml

* travis-ci: test cache folder damage.

* travis-ci: cache invalidates checkout-deps.
2020-07-14 19:15:26 -07:00
Kyle Sanderson
353ced0e41
gamedata: align with core project values. (#1310)
* Rename blacklist.plugins.txt to blocklist.plugins.txt
* gamedata: adjust gamedata name.
* packagescript: adjust filename.
2020-07-14 18:40:28 -07:00
David Anderson
ed9f214256
appveyor: relocate in-tree and convert to MSVC2015. (#969)
* Move appveyor in-tree and move to MSVC2015.

* appveyor: sync.

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* Update appveyor.yml

* appveyor: align SDKs with travis-ci.

* Update appveyor.yml

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-07-13 17:56:12 -07:00
Headline
939bdaf669
regex: update pcre to 8.44 (bug 6650, r=KyleS) (#1309)
* Update OSX PCRE dependency to 8.44

* Add OSX build instructions

* Update Windows PCRE dependency to 8.44

* Update Linux PCRE dependency to 8.44
2020-07-13 15:35:17 -07:00
Headline
2d2ba818e7
Add pcre lib for Windows x64 builds (#1307) 2020-07-11 16:08:14 -07:00
Headline
2653a450fc
handlesys: Output allocation timestamp during panic (#1110)
* Track Handle creation time

* Move ConVar operations outside of loop

* We support bee's here

* Catch windows awfulness

* Prevent Character Truncation

* Add timestamp info to memory leak dump

* Remove last line and adjust new leak dump output

* KyleS fixes

* Fixed width output

* Create invalid parameter failure redirection helper

* Fix rebase regression

* Update sm_invalidparamhandler.h

* Update HandleSys.cpp

* Update HandleSys.cpp

* Update HandleSys.cpp

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-07-09 18:21:45 -07:00
Impact
a065773b6d
Update OnLibraryAdded and OnLibraryRemoved docs (#1303)
`OnLibraryAdded` and `OnLibraryRemoved` are called whether or not a optional dependency exists
2020-07-09 10:09:59 +01:00
Miikka Ylätalo
4e0ae0cb5e
Change int[] to any[] for data r/w functions (#1221) 2020-07-09 10:09:13 +01:00
Ҝℴţأķ
611bad4036
ArrayStack: add Clone method (#1304)
* Provide ArrayStack.Clone method

* Clean definition for old syntax.

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-07-08 20:59:17 -07:00
eyal282
4a4b9ce7f0
cookies: Align output with sm_help (#977)
* Update clientprefs.sp

* Update clientprefs.sp

* Update clientprefs.sp

* Update clientprefs.sp

* Update clientprefs.sp

* Update clientprefs.sp

* Create natives.sp

* Delete natives.sp
2020-07-08 20:38:35 -07:00
stickz
100f1e56ca
mapchooser: Add option for persistent map storage (#1183)
* Add option for persistent previous map storage

* Fix spacer

* Recall previous maps before CreateNextVote()

* Remove MAPCHOOSER_TXT define

* nits and bits

* Update mapchooser.sp

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-07-08 19:56:26 -07:00
nosoop
69ae224938
testing: Add stock AssertStrEq (#1185)
* Add AssertStrEq to testing include

* Remove unnecessary last param
2020-07-08 19:18:39 -07:00
stickz
287628bfee
mapchooser: Clear map history on limit change (#1197)
This commit fixes a bug where if the value of `sm_mapvote_exclude` is reduced, the change may not take effect right away.
2020-07-08 18:36:50 -07:00
42
f27dc2f4f4
sdktools: Throw error when invalid address passed to SDKCall (#1265) 2020-07-08 18:27:10 -07:00
42
1282f13442 Update TF2 CanBeAutobalanced Gamedata 2020-07-03 19:42:07 -04:00
Erik Minekus
47514c7708
Do not require quotes around message in sm_psay (#1300) 2020-07-01 18:58:20 +01:00
Nicholas Hastings
b364bf8b06
Merge pull request #1102 from nosoop/sf11
TF2: Add new condition from Scream Fortress XI
2020-06-29 09:01:59 -04:00
Nicholas Hastings
dea3ef70a2
Add string_t SetEntPropString support for ep1 (fixes #1287) (#1299) 2020-06-28 20:09:57 -04:00
Nick Hastings
1cc7d37189 Fix Dark Messiah build. 2020-06-28 13:41:27 -04:00
42
3164af7e34
Fix TFResourceNames using array based enum struct (#1154) 2020-06-28 01:34:59 +00:00
Nick Hastings
4c8103a4e1 Add string_t SetEntPropString support for ep1 (fixes #1287) 2020-06-25 21:20:56 -04:00
Nick Hastings
2ebbf2774b Update Fortress Forever gamedata. 2020-06-25 12:49:31 -04:00
Peace-Maker
15450a6d0c Fix use-after-free when creating custom user messages
When creating our own "owned and local" protobuf message in `StartProtobufMessage`, `m_FakeEngineBuffer` is used to track that message. In `EndMessage` the message is optionally converted to a "private" one with the right abi on osx and passed to the engine's `SendUserMessage`. On linux and windows the same message as in the `m_FakeEngineBuffer` is passed though without conversion. `engine->SendUserMessage` has a vtable hook which sets `m_FakeEngineBuffer` to the passed argument.

`m_FakeEngineBuffer` frees the message it previously held, since it's "owned" from `StartProtobufMessage`, but that's the same one that's passed in as argument so a use-after-free in the engine happens when the now-freed message pointer is forwarded to the real `SendUserMessage` in the engine.

The message created in `StartProtobufMessage` wasn't free'd at all when hooks are blocked too. This fix moves the message buffer into a local variable which is destroyed at the end of the function.

Fixes #1286 and #1296
2020-06-23 10:32:55 -07:00
Headline
832519ab64
Prevent multiple calls to SDK_OnAllLoaded (#1293) 2020-06-21 23:32:11 -07:00
Headline
4e2806c951
Notify plugin reloads on next frame (#1292) 2020-06-21 23:31:52 -07:00
Nicholas Hastings
7e0dd1fd41
Update TF2 CanBeAutobalanced gamedata. 2020-06-17 00:50:43 +00:00
Accelerator74
2d971a9fb7
Prevent voteban evading & add ban length cvar (#1249) 2020-06-13 16:00:44 -07:00
David Anderson
3b386379dd
Use more STL for vector insertion/removal. (#1284) 2020-06-13 15:57:02 -07:00
David Anderson
7b887ee9f6 Update SourcePawn.
This will fix a utf8 regression reported in issue #1286.
2020-06-02 13:33:57 -07:00
David Anderson
67f0e4be60 Update SourcePawn and AMTL. 2020-06-01 13:22:29 -07:00
David Anderson
80acff8d7d Replace ke::LinkedList with std::list. 2020-05-31 23:19:41 -07:00
David Anderson
d5d4d78023 Update SourcePawn and AMTL to fix the Mac build. 2020-05-31 22:57:14 -07:00
David Anderson
5d94f0bea8 Replace ke::Vector with std::vector. 2020-05-31 11:35:51 -07:00
David Anderson
e5ddbd9886 Introduce a pbproxy library to solve macOS linker issues.
On SDKs which use protobufs, the engine has objects compiled against a specific
version of protobuf. Normally this is fine, we take care on Linux to use the
same C++ ABI. On macOS however, we use libc++ to enable C++11 functionality,
whereas the protobuf library has been compiled with libstc++. These ABIs are
not compatible.

To address the problem, we introduce PbHandle. PbHandle is a wrapper around
protobuf::Message with two added pieces of state: whether or not the handle
"owns" the message (and can free it in its destructor), and whether or not
the handle was created by the engine (private) or created by SourceMod
(local).

Whenever we transfer a protobuf::Message pointer to SourceMod, we must take
care to convert it to a Local version first. Whenever we transfer a protobuf
pointer to the engine, we must convert it to a Private handle.

For platforms with no ABI differences (almost all of them), the handle is a
no-op. The private and local localities are compatible and no translation
takes place.

On macOS, CS:GO does require translation. SourceMod loads a tiny shim
library that contains a copy of the protobuf sources compiled against the
game's ABI. It then provides serialization and deserialization methods.
SourceMod must not interact with the game's protobuf objects without first
going through this proxy library.

Note that PbHandle is not quite like unique_ptr. It can be converted into a
PbHandle that does not destroy the underlying object. This is mainly because
UserMessages.cpp has rather complex state, so it is useful to track locality
without destroying an object. An unowned PbHandle must not outlive the
owning PbHandle.
2020-05-30 22:13:07 -07:00
David Anderson
d525b466ec Use C++11 for macOS and CS:GO. 2020-05-30 22:13:07 -07:00
David Anderson
333227fad8 Build csgo-x64 on travis. 2020-05-30 22:13:07 -07:00
David Anderson
288a781555 Fix startup crash. 2020-05-30 19:10:23 -07:00
David Anderson
979e410efc Update AMTL, replace ke::Deque with std::deque. 2020-05-30 12:44:02 -07:00
Fyren
75fa198321
Change bootstrap.pl and startbuild.pl to also take CXX. (#1280) 2020-05-27 21:18:49 -04:00
David Anderson
49669f6585 Revert "Fix linking on Linux."
This reverts commit acf8782786.
2020-05-26 20:04:55 -07:00
David Anderson
acf8782786 Fix linking on Linux. 2020-05-25 21:35:50 -07:00
David Anderson
6d2e0aa684 Fix Windows build. 2020-05-21 00:11:23 -07:00
David Anderson
032a30f676 Fix mac build, part 2. 2020-05-20 23:17:15 -07:00
David Anderson
c9f574c27b Fix mac build. 2020-05-20 22:50:41 -07:00
David Anderson
b725196a26 Replace AString with std::string. 2020-05-20 17:57:18 -07:00
David Anderson
301bafa3f5 Replace more Move/Forward with STL variants. 2020-05-19 12:56:28 -07:00
David Anderson
7a3e4054c7 Enable exception handling in C++ code.
It turns out this was already enabled on MSVC (due to /EHsc), but let's
enable it on other platforms as well.

Exception handling comes with a huge caveat: SourceMod and SourcePawn
are not exception safe. Not only do they predate usable STL (C++11),
they often predate C++03, and sometimes even C++ itself. There are many
places we do not use RAII, or where we accumulate state in a way that
cannot be interrupted.

By enabling exceptions, we are NOT inviting general try/catch. We are
still assuming that a `throw` anywhere within SourceMod will ultimately
result in a crash.

However, as we enable more and more STL, we are losing the ability to
gracefully handle constructor failures and malloc failures. So try-catch
is now enabled. It should only be used in the narrowest of
circumstances:

 - When an exception can be thrown by a library call, and
 - There is no way "a priori" to tell if an exception will be thrown
(for example, std::bad_alloc or std::system_error), and
 - Handling the exception is meaningful.

Generally malloc failures should not be considered meaningful. Once
memory is exhausted, the program will crash or be OOM-killed, so there's
no point in handling the failure. However, cases where the allocation
amount is variable may be meaningful to handle. A simple example would
be CDataPack, where if a plugin leaks entries, it's better to handle
this gracefully given that vector growth is geometric. Another example
might be reads of a massive file or network request into a buffer.

These cases should be rare, given that memory pressure is usually
fatal to srcds anyway. But if you've decided to handle an exception,
the try-catch block should be as narrow as possible. For example,
the following is erroneous:

    ke::Maybe<SomeGiganticThing> object;
    try {
        object.init();
    } catch (const std::bad_alloc&) {
    }

`ke::Maybe` is not threadsafe, and this can leak. Basically, do as
little as possible in try blocks, and use them sparingly, because
they're very difficult to audit.

We are also not inviting use of `throw`, as auditing it is even more
complex than try/catch. It is better to abort(), or use boolean
returns and two-stage object initialization.
2020-05-19 12:21:57 -07:00
David Anderson
7d7253c9cc Update AMTL; replace AutoPtr/UniquePtr with STL. 2020-05-18 18:19:16 -07:00
David Anderson
c2df49ee33 Rename ke::Lambda to ke::Function. 2020-05-17 12:33:52 -07:00
David Anderson
a253e175bb Replace all uses of AMTL threads with STL threads.
This also rewrites the work loop for threaded queries. It has been
simplified significantly.
2020-05-16 22:35:56 -07:00
David Anderson
15023777f4
Merge pull request #1266 from alliedmodders/threads-3
Pare down ThreadSupport and remove ancient thread code.
2020-05-14 10:45:50 -07:00
David Anderson
ff018a9a5d Improve Travis coverage.
Our official builds use clang-3.4 (for macOS) and clang-3.8 for Linux.
Linux uses libstdc++-4.9. Make sure these two compilers are being tested
and that libstdc++-4.9 is being used for STL.

Add a macOS builder to get coverage there. This will use a newer
clang than we actually use, but as opposed to the linux builder will
test the platform-specific bits.

Finally, use the latest GCC and clang versions from a bionic image, so
we have some coverage of a popular distribution.
2020-05-13 19:09:20 -07:00
David Anderson
f76cb94511 Pare down ThreadSupport and remove ancient thread code.
This patch removes almost all of the existing platform-specific
ThreadSupport code, as well as code derived from it. It is now
implemented on top of C++11 threads and is much simpler.

This is the first inclusion of STL in SourceMod. Mac and Windows are
allowed to dynamically link to their respective implementations. On
Linux, libstdc++ is statically linked, except in the cases where it was
already dynamically linked (csgo, blade).

IEventSignal has been retained because sourcemod-curl-extension relies
on it. As written, it is impossible to use as a condition variable,
because the caller does not have access to the underlying mutex. There
is no way to make this API safe or non-racy, so extensions relying on
it should switch to C++11 threads.

ThreadWorker is now pared down and does not interact or inherit from
BaseWorker in any way. Basic functionality has been tested. Since it is
not used anywhere in SourceMod, or seemingly in any repository on
GitHub, it's unclear whether it should even exist. But it has been
tested in this patch.

This change bumps the minimum macOS version to OS X 10.7, and the
minimum C++ standard level to C++14.
2020-05-13 00:35:29 -07:00
David Anderson
87cc42d348 Fix build failures with clang 10. 2020-05-12 23:04:55 -07:00
wanted241
5177cfdf97
Fix unnecessary ConCommand cache misses (#1256) 2020-05-08 15:28:45 -07:00
Deathreus
13621a1274
Add an array operations to CDataPack (#1219) 2020-05-08 15:23:48 -07:00
peace-maker
bc89e54f6d
NPOTB: Always use hl2sdk-proxy-repo in checkout-deps (#1236) 2020-05-08 15:12:21 -07:00
Arron Vinyard
5ed2f79217
Replace GetCmdArgs with args param (#1229) 2020-05-08 15:09:44 -07:00
Vladimir
3696a4cd9e
Correct parameter detail in OnEntityDestroyed (#1237) 2020-05-08 15:08:34 -07:00
Arron Vinyard
8259bd316a
Alert players of map history console output (#1242) 2020-05-08 15:04:57 -07:00
⭐ B3none
6717f45469
Standardize some spacing in translation phrases (#1254) 2020-05-08 14:55:05 -07:00
Scags
44615b7ade
Prevent uncessary re-tagging for address functions (#1250) 2020-05-08 14:54:01 -07:00
Loïc
f3200b2232
Fix GetDataDescMap not work on Day Of Infamy (#1263)
During the Split Day of Infamy to separate engine build #718 on 3 Nov 2017, it was forgotten to add the engine doi the list.
2020-05-07 12:20:29 +01:00
42
881cbcd45d
Add new TF2 Holiday Soldier (#1257) 2020-05-06 23:00:24 +00:00
Loïc
30a4032067
Fix timelimit not correct for Black Mesa (#1262)
* Fix timelimit not correct for Black Mesa

Black Mesa is particular and use timelimit in seconds instead of minutes

* Update TimerSys.cpp

* Update TimerSys.cpp

* Update TimerSys.cpp

* Update TimerSys.cpp
2020-05-06 22:59:43 +00:00
peace-maker
5597fc56d3
Fix crash when ArrayList runs out of memory (#1235)
The allocation size was still updated to the bigger size even if memory allocation failed. Trying to write to the supposedly available new space would overflow the heap and crash. Fixes #1233
2020-04-30 17:59:54 +01:00
Erik Minekus
adcc0efda6
Fix matching Regex against an empty string (#1253)
Removed the offset check from MatchRegex, as this
is already handled by pcre_exec.
2020-04-29 00:37:45 +00:00
Andrew
d044b13ce4
datapack: free all elements on clear (#1251)
* Fixed memory leak

When a pack was cleared or destroyed the String and Raw types could cause memory leaks. This happens when "position" is sitting at the end of the vector and can never get past the "if (pos >= elements.length())" statement. This means there is a memory leak in any plugin that clears/destroys a pack with strings and doesn't set the position to length-1 or less beforehand.

* datapack: Fix delete op on CDataPackType::Raw.

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-04-27 18:09:13 -07:00
Loïc
d42c304a55
Bump version for non-VCS builds (#1243) 2020-04-27 15:50:03 +01:00
Loïc
d876f04baf
Update Function Offsets For PVKII 0.4.2.2 (#1227)
* Update Function Offsets For PVKII 0.4.2.2

-Updates offsets for Linux/Win/Mac
-Update func sig FireOutput for Linux/Win/Mac
2020-04-22 01:03:34 +00:00
Loïc
593552f8d6
Update Function Offsets For PVKII 0.4.2.2 (#1226)
* Update Function Offsets For PVKII 0.4.2.2

-Updates offsets for Linux/Win/Mac
2020-04-22 01:03:22 +00:00
thewavelength
25462071df
Make GetStringTableData native binary-safe (#1232)
Replace StringToLocalUTF8 with LocalToString and memcpy to make this binary compatible and update the documentation.
2020-04-14 17:51:39 +01:00
Nick Hastings
1d98c3a782 Fix Linux SetClientName/SetUserConVar gamedata on Nuclear Dawn (Fixes #1225). 2020-04-08 17:47:49 -04:00
Tom
7f239bb931
Prevent clients from spamming global chat using sm_nominate (#1217) 2020-03-21 17:24:45 +00:00
peace-maker
23efe26655
Update Contagion gamedata again (#1216)
Updates vtable offsets after Patch 2.1.2.2 again. #1171
2020-03-18 16:04:41 +00:00
Asher Baker
ece447182f
Fix basecomm failing to load on games without sv_alltalk (#1212) 2020-03-11 21:24:16 -07:00
Asher Baker
6465bd83a4 Update for latest Blade Symphony SDK 2020-03-11 22:36:25 +00:00
rumblefrog
e941c1acea
Update Black Mesa gamedata (#1208) 2020-03-10 20:20:08 +00:00
Asher Baker
ecad8f25a8
Revert "csgo: enable SayText + raise msg limits (#1118)" (#1209)
This reverts commit 4a8e0799bd.
2020-03-08 13:27:13 +00:00
Asher Baker
48c3a9f2fa
NMRiH gamedata update (#1204) 2020-03-05 18:37:09 +00:00
Asher Baker
d59edc5d0a
Use GetCmdArgInt(Ex) in base plugins (#1203) 2020-03-04 22:07:00 +00:00
Asher Baker
17440fc4be
Update Reload offset (#1202)
Update Reload offset
2020-03-04 21:56:08 +00:00
Asher Baker
bff8585411
Add an option to build against no SDKs (#1201) 2020-03-04 21:52:07 +00:00
Asher Baker
6a307bfcee
Restore the frame pointer on Linux (#1200)
Looks like the default here changed when we upgraded the Linux build server.

This is causing issues when debugging crash dumps.
2020-03-04 21:43:13 +00:00
Headline
604942f0e7
Add helper stocks for getting numerical command arguments (#1194) 2020-03-04 13:17:10 -08:00
Kyle Sanderson
22eeb2f3a5
DarkM: build-fix for engine msg caching (#1195)
* DarkM: build-fix for engine msg caching

* style + promote ptr casting to uintptr_t.

* sync type to uintptr_t in pm.h

* return of the uint32_t

* update header.

* oh, right, unsigned int...
2020-03-03 07:13:03 -08:00
BotoX
cc6059a4b7
engine: Implement message buffering. (#1071)
* Avoid losing console messages.
Buffers up to 16k bytes of SVC_Print if buffer would overflow, then sends chunks every frame.
Sends up to 2048 bytes per frame and does not split messages.

* UNTESTED! Switch to ke::LinkedList<ke::AString> for PrintfBuffer.
Switch from OnGameFrame to FramAction.
Fix compiling on Episode1 by essentially disabling the feature.

* UNTESTED! Cleanup on disconnect, passthrough for >= 2048 msgs

* try reference for CPlayer.

* fix

* remove m_PrintfStop

* remove m_PrintfStop

* ensure empty queue when netchan drops

* flip to serials.

* serials

* style

* Update PlayerManager.cpp

* lift consts to header.

* remove local const references

* ep1 static const

* flip to queue - fix serial on resched.

* Update PlayerManager.h

* Update PlayerManager.cpp

* Update PlayerManager.h

* am-deque.h

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-02-27 16:21:31 -08:00
BotoX
91480b6b91
sdktools: correct various ancient EntityOutput issues. (#1074) 2020-02-26 16:33:52 -08:00
GAMMACASE
4a8e0799bd
csgo: enable SayText + raise msg limits (#1118)
Rised limits for SayText and HintText protobuffs in csgo, also switched from TextMsg to SayText in csgo.
2020-02-26 16:31:50 -08:00
Arthurdead
bcd5e40842
sdktools: expose additional tr sdk capabilities (#1145)
* add the rest of the trace enumerate funcs

* fix ident
2020-02-26 16:19:55 -08:00
2251307218
9fb6430313
sdkhooks: correct velocity issues with dropweapon (#1159) 2020-02-26 16:11:17 -08:00
stickz
68e45f3583
mapchooser: Replace existing map entry from exclusion list (#1184) 2020-02-26 15:59:35 -08:00
Arron Vinyard
a1ed47be87
IsServerProcessing: improve grammar / present|correctness. (#1188)
Wording of comment was a bit off.
2020-02-26 15:54:19 -08:00
Ҝờţأķ
ded3867605
regex: add/document missing offset param (#1175)
* Regex Little Changes

* Prevented to triple and double call `strlen`.
* More informative message on `if (offset >= len)`.
* Add missing parametr in navite `MatchRegex`.

* Regex Little Changes v2

* Using `strdup` instead `strcpy`.
* Replaced NULL to nullptr.
* Removed note about MatchOffset.

Co-Authored-By: Headline <headline@users.noreply.github.com>

* Removed padding.

Co-Authored-By: Headline <headline@users.noreply.github.com>

* Removed more padding.

Co-Authored-By: Headline <headline@users.noreply.github.com>

Co-authored-by: Headline <michaelwflaherty@me.com>
2020-02-26 15:52:04 -08:00
David Anderson
2b6833f65d
Merge pull request #1167 from Scags/teleport-defaulted
Add default values to TeleportEntity
2020-02-26 13:17:45 -08:00
SM9
4d38f11367 Clarify detour creation errors (#1191)
A logic error here meant that it wasn't printing the failing sig name in the common case
2020-02-26 19:14:51 +00:00
Juice
a3dd25f354
Update csgo InfoChanged offset (#1192) 2020-02-26 18:46:09 +00:00
komashchenko
4b0e73ca5a
WriteBaselines gamedata update (#1189) 2020-02-25 10:21:04 +00:00
MartLegion
cd37354634
Change sm_beacon to use game-specific team colors (#1187)
Added game color config & specific settings for L4D/L4D2

Created the following keys:

"Team1Color"	"75,255,75,255"
"Team2Color"	"255,75,75,255"
"Team3Color"	"75,75,255,255"
"Team4Color"	"255,128,0,255"
"TeamUnknownColor"	"255,255,255,255"

Added a specific setting for L4D/L4D2 game:

"Team2Color"	"75,75,255,255"
"Team3Color"	"255,75,75,255"
2020-02-23 14:03:00 +00:00
Michael
8a5d0a58e4
Fix unnecessary ConVar cache misses (#1177)
Fixes #1166
2020-02-11 00:39:19 -08:00
42
1a71f4fbde
Fix TF2_MakeBleed using incorrect custom damage type (#1163) 2020-02-08 17:39:47 -08:00
Bara
1534f8749b
NPOTB: Clarify forward declarations in sdhooks.inc (#1152) 2020-02-08 17:35:40 -08:00
proobs
4ea85a9291 Add new CSWeaponID knives (#1126) 2020-02-08 17:28:38 -08:00
peace-maker
739c07ca9b
Fix heap corruption in CUtlVector destructor (#1165) 2020-02-08 15:36:21 -08:00
thorgot
452338dc11
Fix incorrect nomination response (#1161) 2020-02-06 22:53:59 -08:00
sneak-it
b6a6387f16
Prevent basecommands from printing to disconnected clients (#1138) 2020-02-06 22:32:16 -08:00
proobs
4a8c869b7e
Update Forward Creation to Newer GlobalForward Methodmap (#1143) 2020-02-06 22:27:44 -08:00
peace-maker
c052eb3969
Update Contagion gamedata (#1169) 2020-02-06 22:11:05 -08:00
Deathreus
48ed38a8c1
Fix documentation in some DataPack methods (#1164) 2020-02-06 22:05:32 -08:00
naydef
fba71ed24d
Deprecate IsSoundPrecached (#1172)
Fix #1170
2020-02-06 10:33:44 -08:00
Scags
625650c160
Add default values to TeleportEntity 2020-02-01 02:53:59 -06:00
Impact
9f4c6c61d9 Add comment about common.phrases to FindTarget (#1155)
* Add note about common.phrases

* Update helpers.inc

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
2020-01-14 15:00:27 -08:00
bottiger1
9d978f5581 Correct GetSteamAccountID validation parameter (#1158).
Correctly forward the validated parameter within CPlayer::GetSteamAccountID.
2020-01-14 14:43:07 -08:00
Erik Minekus
664b352559 Fix Documentation Typo in functions.inc (#1156) 2020-01-12 12:23:08 -08:00
GAMMACASE
cfa4998ac1 WriteBaselines gamedata update after latest csgo update (#1153) 2020-01-07 21:36:16 +00:00
David Anderson
5efe6d0a42
Merge pull request #1151 from alliedmodders/update-sp
Update SourcePawn.
2019-12-28 12:24:19 -08:00
David Anderson
e34fbcce0d Update SourcePawn.
This fixes:
 alliedmodders/sourcepawn#432
 alliedmodders/sourcepawn#435
 alliedmodders/sourcepawn#439
2019-12-28 12:07:36 -08:00
Kruzya
8746b2fd7f Adjust CanAdminTarget to support multiple Group Immunity IDs (#1147) 2019-12-23 10:45:36 -08:00
David Anderson
4328627326
Merge pull request #1144 from alliedmodders/update-sp
Update SourcePawn to tip-of-tree.
2019-12-15 20:48:27 -08:00
David Anderson
fc6925fa0a Update SourcePawn to tip-of-tree. 2019-12-15 20:34:44 -08:00
Asher Baker
b5de3eb588
Speculative fix for MySQL crashes (#1135)
https://crash.limetech.org/stats/dbi.mysql.ext.%25/my_real_read
https://crash.limetech.org/stats/dbi.mysql.ext.%25/net_real_write

Both of these are caused by the VIO ptr ending up as null in the middle of reading/writing to a connection - I can't find any indication of a fix for this made to MySQL, so don't think it is a bug fix we're missing, but there are some musings around the internet that it could be caused by improper thread-safety initialisation.

`my_init` (what we had here) is called internally by `mysql_library_init` but I think would have still led to an automatic `mysql_library_init` call the first time `mysql_init` was called (which we can do on a thread in case of threaded connections), which is exactly the thread-safety issue called out by the MySQL docs, so hopefully doing things properly here will help.
2019-12-15 15:01:15 +00:00
Impact
26422fd425 Fix typo in clientprefs plugin description (#1142) 2019-12-14 16:12:14 -08:00
David Anderson
cd1a296e4f
Merge pull request #1131 from alliedmodders/update-sp
Update SourcePawn.
2019-11-24 20:57:06 -08:00
David Anderson
3ddf9f8a0d Update SourcePawn. 2019-11-24 20:41:59 -08:00
komashchenko
82df6087af Update CScore and MVP CSGO gamedata (#1127) 2019-11-19 21:31:54 -08:00
PerfectLaugh
bef8562de5 Fix CSGO Update crash (11/19/2019) (#1125)
* Fix CSGO Update crash (11/19/2019)

We know what happened when Valve do something big.
Not tested on Linux

* Fix RoundRespawn on Windows

* Fix TerminateRound on Linux x86

* Comment out Linux x64 part of TerminateRound

Better leave blank here.
2019-11-18 22:18:28 -05:00
Nicholas Hastings
1000d419fc Throw configuration error on unsupported compilers (#1029) 2019-11-15 16:40:39 -08:00
Einyux
2a9deb6a64 Add missing const to origin parameters (#1079) 2019-11-13 00:33:00 -08:00
42
351e406f85 Fix ArrayStack.Pop documentation (#1099) 2019-11-13 00:26:36 -08:00
David Anderson
d6e518838f
Merge pull request #1053 from nosoop/remote-ext-filename-check
Check short name for remote extensions
2019-11-12 11:45:02 +09:00
hydrogen-mvm
9e39f18230 Fix OpenFile files.inc example (#1120)
"rb" = binary file for *reading* (not writing, that would be "wb").
2019-11-11 16:24:12 -08:00
BotoX
3dd1e5a318 Validate GetEntityHandle in FindEntityByNetClass (#1089) 2019-11-05 22:25:18 -08:00
Bara
23e1c0b71e Add slot define for healthshot/shield and tablet (#1114) 2019-11-04 12:36:17 +00:00
JoinedSenses
a1436cd205 Add windows supported SDKs to powershell checkout-deps (#1116)
.sh version has this bit:

```
if [ $ismac -eq 0 ]; then
  # Add these SDKs for Windows or Linux
  sdks+=( orangebox blade episode1 bms )

  # Add more SDKs for Windows only
  if [ $iswin -eq 1 ]; then
    sdks+=( darkm swarm bgt eye contagion )
  fi
fi
```

Added these to the SDK list.
2019-11-04 12:34:56 +00:00
David Anderson
0d320b7922
Merge pull request #1115 from alliedmodders/update-sp
Update SourcePawn.
2019-11-02 12:32:35 -07:00
David Anderson
b2a0d0e4da Update SourcePawn.
Bug: alliedmodders/sourcepawn#400
Bug: alliedmodders/sourcepawn#401
Bug: alliedmodders/sourcepawn#402
2019-11-02 12:16:04 -07:00
Bara
273f058da9 Add classic knife to CSWeaponID (#1111) 2019-10-31 13:50:29 -07:00
Headline
c6f751bb67
Return DBDriver instead of Handle in DBI (#1109) 2019-10-31 01:53:50 -07:00
Headline
00b7ac5a39
Add bounds check for userid reset on disconnect (#1108) 2019-10-30 17:17:53 -07:00
David Anderson
c0686dc4f9
Merge pull request #1106 from alliedmodders/update-sp
Update SourcePawn to master.
2019-10-28 22:51:30 -07:00
David Anderson
7ab3a3cfd9 Update SourcePawn to master.
This turns on the new expression parser by default.
2019-10-28 21:19:22 -07:00
nosoop
06d327e76c Reverted addition of TF_CUSTOM_GENERIC_BOMB 2019-10-16 17:10:57 -07:00
nosoop
2e28b036f8 Add new condition and custom damage type from Scream Fortress XI 2019-10-16 00:13:41 -07:00
nosoop
5293815bf6 Check other filename sources on remote extensions 2019-07-29 04:00:18 -07:00
1186 changed files with 130695 additions and 37737 deletions

8
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every week
interval: "weekly"

98
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,98 @@
name: Continuous Integration
on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
pull_request:
branches:
- master
- '[0-9]+.[0-9]+-dev'
jobs:
test:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-latest, windows-latest]
include:
- os: windows-latest
os_short: win
compiler_cc: msvc
- os: ubuntu-latest
os_short: linux
compiler_cc: clang
compiler_cxx: clang++
- os: ubuntu-20.04
os_short: linux
compiler_cc: clang-8
compiler_cxx: clang++-8
fail-fast: false
runs-on: ${{ matrix.os }}
name: ${{ matrix.os_short }}-${{ matrix.compiler_cc }}
env:
SDKS: '["episode1","css","tf2","l4d2","csgo"]'
ARCH: x86,x86_64
DEPENDENCIES_FOLDER: dependencies
DEPENDENCIES_ROOT: ${{ github.workspace }}/dependencies
MYSQL_VERSION: '5.5'
MMSOURCE_VERSION: '1.12'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
path: sourcemod
- name: Cache dependencies
uses: actions/cache@v4
env:
cache-name: hl2sdk-mysql-mmsource
with:
path: ${{ env.DEPENDENCIES_ROOT }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-${{ join(fromJSON(env.SDKS), '') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-
${{ runner.os }}-build-${{ env.cache-name }}-mysql${{ env.MYSQL_VERSION }}-
# Setup Python for AMBuild
- uses: actions/setup-python@v5
name: Setup Python 3.8
with:
python-version: 3.8
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
- name: Install dependencies
shell: bash
run: |
mkdir -p ${{ env.DEPENDENCIES_FOLDER }}
cd ${{ env.DEPENDENCIES_FOLDER }}
# Satisfy checkout-deps requirement for a "sourcemod" folder.
mkdir -p sourcemod
../sourcemod/tools/checkout-deps.sh -s ${{ join(fromJSON(env.SDKS)) }}
- name: Install Linux dependencies
if: startsWith(runner.os, 'Linux')
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \
libc6-dev libc6-dev-i386 linux-libc-dev \
linux-libc-dev:i386 lib32z1-dev ${{ matrix.compiler_cc }}
- name: Select clang compiler
if: startsWith(runner.os, 'Linux')
run: |
echo "CC=${{ matrix.compiler_cc }}" >> $GITHUB_ENV
echo "CXX=${{ matrix.compiler_cxx }}" >> $GITHUB_ENV
${{ matrix.compiler_cc }} --version
${{ matrix.compiler_cxx }} --version
- name: Build
working-directory: sourcemod
run: |
mkdir build
cd build
python ../configure.py --enable-optimize --sdks=${{ join(fromJSON(env.SDKS)) }} --targets=${{ env.ARCH }} --mms-path=${{ env.DEPENDENCIES_ROOT }}/mmsource-${{ env.MMSOURCE_VERSION }} --hl2sdk-root=${{ env.DEPENDENCIES_ROOT }} --mysql-path=${{ env.DEPENDENCIES_ROOT }}/mysql-${{ env.MYSQL_VERSION }} --mysql64-path=${{ env.DEPENDENCIES_ROOT }}/mysql-${{ env.MYSQL_VERSION }}-x86_64
ambuild

104
.github/workflows/mocktest.yml vendored Normal file
View File

@ -0,0 +1,104 @@
name: hl2sdk-mock tests
on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
pull_request:
branches:
- master
- '[0-9]+.[0-9]+-dev'
jobs:
mock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
name: Clone sourcemod
with:
submodules: recursive
path: sourcemod
- uses: actions/checkout@v4
name: Clone metamod-source
with:
repository: alliedmodders/metamod-source
submodules: recursive
path: metamod-source
- uses: actions/checkout@v4
name: Clone hl2sdk-mock
with:
repository: alliedmodders/hl2sdk-mock
submodules: recursive
path: hl2sdk-mock
- uses: actions/setup-python@v5
name: Setup Python 3.10
with:
python-version: "3.10"
- name: Install AMBuild
run: |
python -m pip install --upgrade pip setuptools wheel
pip install git+https://github.com/alliedmodders/ambuild
- name: Build MetaMod:Source
working-directory: metamod-source
run: |
python configure.py --enable-optimize --sdks=mock --targets=x86_64
ambuild objdir
- name: Build SourceMod
working-directory: sourcemod
run: |
python configure.py --no-mysql --enable-optimize --sdks=mock --targets=x86_64
ambuild objdir
- name: Build hl2sdk-mock
working-directory: hl2sdk-mock
run: |
python configure.py --enable-optimize --targets=x86_64
ambuild objdir
- name: Setup gamedir
working-directory: hl2sdk-mock
shell: bash
run: |
mkdir ../gamedir
./build_gamedir.sh ../gamedir ../metamod-source/objdir/package
./build_gamedir.sh ../gamedir ../sourcemod/objdir/package
- name: Compile testsuite
working-directory: hl2sdk-mock
shell: bash
run: |
mkdir ../gamedir/addons/sourcemod/plugins/optional
for f in ../sourcemod/plugins/testsuite/mock/*.sp; do
echo "Compiling $(basename $f)"
../gamedir/addons/sourcemod/scripting/spcomp64 -i ../gamedir/addons/sourcemod/scripting/include -o "../gamedir/addons/sourcemod/plugins/optional/$(basename $f .sp).smx" -E "$f"
done
- name: Test
working-directory: hl2sdk-mock
shell: bash
run: |
for f in ../gamedir/addons/sourcemod/plugins/optional/*.smx; do
echo "==================================="
echo "Running $(basename $f)..."
echo "==================================="
timeout 60 ./objdir/dist/x86_64/srcds -game_dir ../gamedir +map de_thunder -command "sm plugins load optional/$(basename $f)" -run -run-ticks 20 |
{
failed=0
while IFS= read -r line; do
echo "$line"
if [[ "$line" == *"FAIL"* ]]; then
failed=1
fi
done
if [ "$failed" = "1" ]; then
echo "$(basename $f) failed."
exit 1
fi
}
done

79
.github/workflows/scripting.yml vendored Normal file
View File

@ -0,0 +1,79 @@
name: SourcePawn scripting
on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
paths:
- 'plugins/include/*'
- 'sourcepawn/**'
workflow_dispatch:
schedule:
- cron: '53 05 01 */3 *' # Artifacts expire every 3 months
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
os_short: linux
- os: windows-latest
os_short: win
- os: macos-latest
os_short: mac
fail-fast: false
runs-on: ${{ matrix.os }}
env:
ARCH: x86,x86_64
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
# Setup Python for AMBuild
- uses: actions/setup-python@v5
name: Setup Python 3.8
with:
python-version: 3.8
- name: Install AMBuild
run: |
python -m pip install --upgrade pip setuptools wheel
pip install git+https://github.com/alliedmodders/ambuild
- name: Build universal x64/arm64 on macOS
if: startsWith(runner.os, 'macOS')
run: echo "ARCH=x86_64,arm64" >> $GITHUB_ENV
- name: Install Linux dependencies
if: startsWith(runner.os, 'Linux')
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \
libc6-dev libc6-dev-i386 linux-libc-dev \
linux-libc-dev:i386 lib32z1-dev ${{ matrix.compiler_cc }}
- name: Select clang compiler
if: startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS')
run: |
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
clang --version
clang++ --version
- name: Build
shell: bash
run: |
mkdir build
cd build
python ../configure.py --enable-optimize --scripting-only --targets=${{ env.ARCH }}
ambuild
echo "SM_VERSION=$(cat ../product.version)" >> $GITHUB_ENV
- name: Archive tooling
uses: actions/upload-artifact@v4
with:
name: sourcemod-tooling-${{ env.SM_VERSION }}-${{ matrix.os_short }}
path: build/package

42
.github/workflows/translations.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Update translation project
on:
push:
branches:
- master
paths:
- 'translations/**'
workflow_dispatch:
jobs:
update_translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v5
name: Setup Python 3.10
with:
python-version: "3.10"
- name: Install Python dependencies
working-directory: tools/language_check
run: |
python -m pip install --upgrade -r requirements.txt
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PEM }}
- name: Update translation project
working-directory: tools/language_check
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
ORGANIZATION: alliedmodders
PROJECT_NUMBER: 1
run: |
python ./compare_translation_phrases.py

View File

@ -0,0 +1,29 @@
name: Sanity check translation phrases
on:
push:
branches:
- master
paths:
- 'translations/**'
pull_request:
branches:
- master
paths:
- 'translations/**'
workflow_dispatch:
jobs:
check_translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
name: Setup Python 3.10
with:
python-version: "3.10"
- name: Check translation phrases syntax
working-directory: tools/language_check
run: |
python ./sanity_check.py

7
.gitignore vendored
View File

@ -28,7 +28,6 @@ LIB-Debug/
[Tt]humbs.db
# AMBuild build directories
build/
obj-*/
*~
*.rej
@ -36,3 +35,9 @@ obj-*/
*.smx
*.swp
*.gdb_history
objdir
# Not standardized, but common name for build directories
build*/
.vs/*
.vscode/*

8
.gitmodules vendored
View File

@ -1,6 +1,14 @@
[submodule "public/amtl"]
path = public/amtl
url = https://github.com/alliedmodders/amtl
shallow = true
[submodule "sourcepawn"]
path = sourcepawn
url = https://github.com/alliedmodders/sourcepawn
shallow = true
[submodule "hl2sdk-manifests"]
path = hl2sdk-manifests
url = https://github.com/alliedmodders/hl2sdk-manifests.git
[submodule "public/safetyhook"]
path = public/safetyhook
url = https://github.com/alliedmodders/safetyhook

View File

@ -1,118 +1,86 @@
git:
depth: 3
sudo: false
language: cpp
os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-3.9
- llvm-toolchain-trusty-4.0
- llvm-toolchain-trusty-5.0
packages:
- lib32stdc++6
- lib32z1-dev
- libc6-dev-i386
- linux-libc-dev
- g++-multilib
# - clang-3.6
# - clang-3.8
# - clang-4.0
# - clang-5.0
# - g++-6
# - g++-6-multilib
- clang-3.9
- g++-4.8-multilib
- g++-4.8
- g++-4.9-multilib
- g++-4.9
- g++-5-multilib
- g++-5
- g++-7-multilib
- g++-7
cache:
directories:
- ../mysql-5.0
env:
- MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9"
- MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8"
- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9"
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
dist: xenial
matrix:
jobs:
fast_finish: true
include:
- os: linux
sudo: false
dist: trusty
language: cpp
addons:
apt:
packages: ['clang-3.6', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev', 'g++-multilib']
cache:
directories: ['../mysql-5.0']
env: ['MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6"']
sources:
- ubuntu-toolchain-r-test
packages: ['clang-3.8', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev:i386', 'g++-4.9-multilib', 'python3-pip']
env:
- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
- SDKS=episode1,css,tf2,l4d2,csgo
- MODE=optimize
- ARCH=x86,x86_64
- os: linux
sudo: false
dist: trusty
language: cpp
addons:
apt:
packages: ['clang-3.8', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev', 'g++-multilib']
cache:
directories: ['../mysql-5.0']
env: ['MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"']
sources:
- ubuntu-toolchain-r-test
packages: ['clang-3.4', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev:i386', 'g++-4.9-multilib', 'python3-pip']
env:
- MATRIX_EVAL="CC=clang && CXX=clang++"
- SDKS=episode1,css,tf2,l4d2,csgo
- MODE=optimize
- ARCH=x86,x86_64
- os: osx
osx_image: xcode7.2
language: cpp
env:
- MATRIX_EVAL="CC=clang && CXX=clang++"
- SDKS=episode1,css,tf2,l4d2,csgo
- MODE=optimize
- ARCH=x86_64,x86
# # This is a faster test for the latest g++.
# - os: linux
# dist: bionic
# sudo: false
# language: cpp
# addons:
# apt:
# packages: ['lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev:i386', 'g++-multilib', 'g++']
# cache:
# directories: ['../mysql-5.0']
# env:
# - MATRIX_EVAL="CC=gcc && CXX=g++"
# - SDKS=csgo
# # GCC currently fails in opt builds trying to inline stuff in sqlite3.c.
# - MODE=debug
# This is a faster test for the latest clang.
- os: linux
sudo: false
dist: bionic
language: cpp
addons:
apt:
sources: ['llvm-toolchain-trusty-4.0']
packages: ['clang-4.0', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev', 'g++-multilib']
cache:
directories: ['../mysql-5.0']
env: ['MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0"']
- os: linux
sudo: false
language: cpp
addons:
apt:
sources: ['llvm-toolchain-trusty-5.0']
packages: ['clang-5.0', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev', 'g++-multilib']
cache:
directories: ['../mysql-5.0']
env: ['MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"']
- os: linux
sudo: false
language: cpp
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-6', 'g++-6-multilib', 'lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev', 'g++-multilib']
cache:
directories: ['../mysql-5.0']
env: ['MATRIX_EVAL="CC=gcc-6 && CXX=g++-6"']
allow_failures:
- env: MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7"
- env: MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9"
- env: MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8"
- env: MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9"
- env: MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
- env: MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
packages: ['lib32stdc++6', 'lib32z1-dev', 'libc6-dev-i386', 'linux-libc-dev:i386', 'g++-multilib', 'clang']
env:
- MATRIX_EVAL="CC=clang && CXX=clang++"
- SDKS=csgo
- MODE=optimize
- ARCH=x86
before_script:
- CHECKOUT_DIR=$PWD && cd .. && $CHECKOUT_DIR/tools/checkout-deps.sh && cd $CHECKOUT_DIR
- CHECKOUT_DIR=$PWD && cd .. && $CHECKOUT_DIR/tools/checkout-deps.sh -s ${SDKS} && cd $CHECKOUT_DIR
script:
- mkdir build && cd build
- PATH="~/.local/bin:$PATH"
- eval "${MATRIX_EVAL}"
- python ../configure.py --enable-optimize --sdks=episode1,css,tf2,l4d2,csgo,dota
- eval "${CC} --version"
- eval "${CXX} --version"
- python3 ../configure.py --enable-${MODE} --sdks=${SDKS} --targets=${ARCH}
- ambuild

View File

@ -1,63 +1,7 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import collections
import os, sys
class SDK(object):
def __init__(self, sdk, ext, aDef, name, platform, dir):
self.folder = 'hl2sdk-' + dir
self.envvar = sdk
self.ext = ext
self.code = aDef
self.define = name
self.platform = platform
self.name = dir
self.path = None # Actual path
self.platformSpec = platform
# By default, nothing supports x64.
if type(platform) is list:
self.platformSpec = {p: ['x86'] for p in platform}
else:
self.platformSpec = platform
def shouldBuild(self, target, archs):
if target.platform not in self.platformSpec:
return False
if not len([i for i in self.platformSpec[target.platform] if i in archs]):
return False
return True
WinOnly = ['windows']
WinLinux = ['windows', 'linux']
WinLinuxMac = ['windows', 'linux', 'mac']
CSGO = {
'windows': ['x86'],
'linux': ['x86', 'x64'],
'mac': ['x64']
}
PossibleSDKs = {
'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'),
'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'),
'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'),
'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'),
'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'),
'tf2': SDK('HL2SDKTF2', '2.tf2', '11', 'TF2', WinLinuxMac, 'tf2'),
'l4d': SDK('HL2SDKL4D', '2.l4d', '12', 'LEFT4DEAD', WinLinuxMac, 'l4d'),
'nucleardawn': SDK('HL2SDKND', '2.nd', '13', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'),
'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '15', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'),
'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'),
'swarm': SDK('HL2SDK-SWARM', '2.swarm', '16', 'ALIENSWARM', WinOnly, 'swarm'),
'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'),
'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', CSGO, 'csgo'),
'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'),
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'),
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'),
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'),
'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'),
}
import subprocess
def ResolveEnvPath(env, folder):
if env in os.environ:
@ -80,41 +24,60 @@ def ResolveEnvPath(env, folder):
def Normalize(path):
return os.path.abspath(os.path.normpath(path))
def SetArchFlags(compiler, arch, platform):
def SetArchFlags(compiler):
if compiler.behavior == 'gcc':
if arch == 'x86':
compiler.cflags += ['-m32']
compiler.linkflags += ['-m32']
if platform == 'mac':
compiler.linkflags += ['-arch', 'i386']
elif arch == 'x64':
compiler.cflags += ['-m64', '-fPIC']
compiler.linkflags += ['-m64']
if platform == 'mac':
compiler.linkflags += ['-arch', 'x86_64']
if compiler.target.arch == 'x86_64':
compiler.cflags += ['-fPIC']
elif compiler.like('msvc'):
if builder.target.arch == 'x86':
compiler.linkflags += ['/MACHINE:X86']
elif builder.target.arch == 'x64':
compiler.linkflags += ['/MACHINE:X64']
if compiler.target.arch == 'x86_64':
compiler.defines += ['WIN64']
def AppendArchSuffix(binary, name, arch):
if arch == 'x64':
binary.localFolder = name + '.x64'
SdkHelpers = builder.Eval('hl2sdk-manifests/SdkHelpers.ambuild', {
'Project': 'sourcemod'
})
class SMConfig(object):
def __init__(self):
self.sdk_manifests = []
self.sdks = {}
self.sdk_targets = []
self.binaries = []
self.spvm = []
self.extensions = []
self.generated_headers = None
self.mms_root = None
self.mysql_root = {}
self.spcomp = None
self.spcomp_bins = None
self.spcomp_bins = []
self.smx_files = {}
self.versionlib = None
self.archs = builder.target.arch.replace('x86_64', 'x64').split(',')
self.all_targets = []
self.target_archs = set()
self.enable_asan = getattr(builder.options, 'enable_asan', False)
self.asan_libs = {}
self.libsafetyhook = {}
if builder.options.targets:
target_archs = builder.options.targets.split(',')
else:
target_archs = ['x86']
if builder.backend != 'amb2':
target_archs.append('x86_64')
for arch in target_archs:
try:
cxx = builder.DetectCxx(target_arch = arch)
self.target_archs.add(cxx.target.arch)
except Exception as e:
# Error if archs were manually overridden.
if builder.options.targets:
raise
print('Skipping target {}: {}'.format(arch, e))
continue
self.all_targets.append(cxx)
if not self.all_targets:
raise Exception('No suitable C/C++ compiler was found.')
def use_auto_versioning(self):
if builder.backend != 'amb2':
@ -134,41 +97,38 @@ class SMConfig(object):
import re
with open(os.path.join(builder.sourcePath, 'product.version'), 'r') as fp:
productContents = fp.read()
m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents)
m = re.match(r'(\d+)\.(\d+)\.(\d+).*', productContents)
if m == None:
self.productVersion = '1.0.0'
else:
major, minor, release = m.groups()
self.productVersion = '{0}.{1}.{2}'.format(major, minor, release)
def findSdkPath(self, sdk_name):
dir_name = 'hl2sdk-{}'.format(sdk_name)
if builder.options.hl2sdk_root:
sdk_path = os.path.join(builder.options.hl2sdk_root, dir_name)
if os.path.exists(sdk_path):
return sdk_path
return ResolveEnvPath('HL2SDK{}'.format(sdk_name.upper()), dir_name)
def shouldIncludeSdk(self, sdk):
return not sdk.get('source2', False)
def detectSDKs(self):
sdk_list = builder.options.sdks.split(',')
use_all = sdk_list[0] == 'all'
use_present = sdk_list[0] == 'present'
sdk_list = [s for s in builder.options.sdks.split(',') if s]
SdkHelpers.sdk_filter = self.shouldIncludeSdk
SdkHelpers.find_sdk_path = self.findSdkPath
SdkHelpers.findSdks(builder, self.all_targets, sdk_list)
for sdk_name in PossibleSDKs:
sdk = PossibleSDKs[sdk_name]
if sdk.shouldBuild(builder.target, self.archs):
if builder.options.hl2sdk_root:
sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder)
else:
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
if sdk_path is None or not os.path.isdir(sdk_path):
if use_all or sdk_name in sdk_list:
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
continue
if use_all or use_present or sdk_name in sdk_list:
sdk.path = Normalize(sdk_path)
self.sdks[sdk_name] = sdk
if len(self.sdks) < 1 and len(sdk_list):
raise Exception('No SDKs were found that build on {0}-{1}, nothing to do.'.format(
builder.target.platform, builder.target.arch))
self.sdks = SdkHelpers.sdks
self.sdk_manifests = SdkHelpers.sdk_manifests
self.sdk_targets = SdkHelpers.sdk_targets
if builder.options.mms_path:
self.mms_root = builder.options.mms_path
else:
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source')
if not self.mms_root:
@ -179,40 +139,56 @@ class SMConfig(object):
self.mms_root = Normalize(self.mms_root)
if builder.options.hasMySql:
if builder.options.mysql_path:
self.mysql_root['x86'] = builder.options.mysql_path
else:
for i in range(10):
self.mysql_root['x86'] = ResolveEnvPath('MYSQL55', 'mysql-5.' + str(i))
if self.mysql_root['x86']:
break
if not self.mysql_root['x86'] or not os.path.isdir(self.mysql_root['x86']):
raise Exception('Could not find a path to MySQL!')
self.mysql_root['x86'] = Normalize(self.mysql_root['x86'])
# For now, ignore 64-bit MySQL on Windows
if 'x64' in self.archs and builder.target.platform != 'windows':
if builder.options.mysql64_path:
self.mysql_root['x64'] = builder.options.mysql64_path
if 'x86' in self.target_archs:
if builder.options.mysql_path:
self.mysql_root['x86'] = builder.options.mysql_path
else:
for i in range(10):
self.mysql_root['x64'] = ResolveEnvPath('MYSQL55_64', 'mysql-5.' + str(i) + '-x86_64')
if self.mysql_root['x64']:
self.mysql_root['x86'] = ResolveEnvPath('MYSQL55', 'mysql-5.' + str(i))
if self.mysql_root['x86']:
break
if not self.mysql_root['x64'] or not os.path.isdir(self.mysql_root['x64']):
if not self.mysql_root['x86'] or not os.path.isdir(self.mysql_root['x86']):
raise Exception('Could not find a path to MySQL. Configure with --no-mysql to disable it.')
self.mysql_root['x86'] = Normalize(self.mysql_root['x86'])
if 'x86_64' in self.target_archs:
if builder.options.mysql64_path:
self.mysql_root['x86_64'] = builder.options.mysql64_path
else:
for i in range(10):
self.mysql_root['x86_64'] = ResolveEnvPath('MYSQL55_64', 'mysql-5.' + str(i) + '-x86_64')
if self.mysql_root['x86_64']:
break
if not self.mysql_root['x86_64'] or not os.path.isdir(self.mysql_root['x86_64']):
raise Exception('Could not find a path to 64-bit MySQL!')
self.mysql_root['x64'] = Normalize(self.mysql_root['x64'])
self.mysql_root['x86_64'] = Normalize(self.mysql_root['x86_64'])
def configure(self):
builder.AddConfigureFile('pushbuild.txt')
if not set(self.archs).issubset(['x86', 'x64']):
raise Exception('Unknown target architecture: {0}'.format(builder.target.arch))
allowed_archs = ['x86_64']
if builder.host.platform == 'mac':
if getattr(builder.options, 'scripting_only', False):
allowed_archs += ['arm64']
else:
allowed_archs += ['x86']
cxx = builder.DetectCxx()
if cxx.like('msvc') and len(self.archs) > 1:
raise Exception('Building multiple archs with MSVC is not currently supported')
if not set(self.target_archs).issubset(allowed_archs):
raise Exception('Unknown target architecture: {0}'.format(self.target_archs))
for cxx in self.all_targets:
self.configure_cxx(cxx)
def configure_cxx(self, cxx):
if cxx.family == 'msvc':
if cxx.version < 1914 and builder.options.generator != 'vs':
raise Exception(f'Only MSVC 2017 15.7 and later are supported, full C++17 support is required. ({str(cxx.version)} < 1914)')
elif cxx.family == 'gcc':
if cxx.version < 'gcc-9':
raise Exception('Only GCC versions 9 or later are supported, full C++17 support is required.')
elif cxx.family == 'clang':
if cxx.version < 'clang-5':
raise Exception('Only clang versions 5 or later are supported, full C++17 support is required.')
if cxx.like('gcc'):
self.configure_gcc(cxx)
@ -228,11 +204,11 @@ class SMConfig(object):
cxx.defines += ['DEBUG', '_DEBUG']
# Platform-specifics
if builder.target.platform == 'linux':
if cxx.target.platform == 'linux':
self.configure_linux(cxx)
elif builder.target.platform == 'mac':
elif cxx.target.platform == 'mac':
self.configure_mac(cxx)
elif builder.target.platform == 'windows':
elif cxx.target.platform == 'windows':
self.configure_windows(cxx)
# Finish up.
@ -267,25 +243,28 @@ class SMConfig(object):
'-Wno-unused',
'-Wno-switch',
'-Wno-array-bounds',
'-msse',
'-fvisibility=hidden',
]
if cxx.target.arch in ['x86', 'x86_64']:
cxx.cflags += ['-msse']
cxx.cxxflags += ['-std=c++17']
cxx.cxxflags += [
'-std=c++11',
'-fno-exceptions',
'-fno-threadsafe-statics',
'-Wno-non-virtual-dtor',
'-Wno-overloaded-virtual',
'-Wno-register',
'-fvisibility-inlines-hidden',
]
have_gcc = cxx.family == 'gcc'
have_clang = cxx.family == 'clang'
if cxx.version >= 'clang-3.9':
if cxx.version >= 'clang-3.9' or cxx.version == 'clang-3.4' or cxx.version > 'apple-clang-6.0':
cxx.cxxflags += ['-Wno-expansion-to-defined']
if cxx.version == 'clang-3.9' or cxx.version == 'apple-clang-8.0':
cxx.cflags += ['-Wno-varargs']
if cxx.version >= 'clang-3.6' or cxx.version >= 'apple-clang-7.0':
if cxx.version >= 'clang-3.4' or cxx.version >= 'apple-clang-7.0':
cxx.cxxflags += ['-Wno-inconsistent-missing-override']
if cxx.version >= 'clang-2.9' or cxx.version >= 'apple-clang-3.0':
cxx.cxxflags += ['-Wno-null-dereference']
@ -305,14 +284,35 @@ class SMConfig(object):
cxx.cxxflags += ['-Wno-deprecated']
cxx.cflags += ['-Wno-sometimes-uninitialized']
if self.enable_asan:
if not have_clang:
raise Exception('--enable-asan only supported when using Clang')
self.configure_asan(cxx)
# Work around SDK warnings.
if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0':
cxx.cflags += [
'-Wno-implicit-int-float-conversion',
'-Wno-tautological-overlap-compare',
]
if have_gcc:
cxx.cflags += ['-mfpmath=sse']
cxx.cflags += ['-Wno-maybe-uninitialized']
if builder.options.opt == '1':
cxx.cflags += ['-O3']
if self.enable_asan:
cxx.cflags += ['-O1']
else:
cxx.cflags += ['-O3']
# Don't omit the frame pointer.
cxx.cflags += ['-fno-omit-frame-pointer']
def configure_msvc(self, cxx):
if self.enable_asan:
raise Exception('--enable-asan only supported when using Clang')
if builder.options.debug == '1':
cxx.cflags += ['/MTd']
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
@ -331,6 +331,7 @@ class SMConfig(object):
'/EHsc',
'/GR-',
'/TP',
'/std:c++17',
]
cxx.linkflags += [
'kernel32.lib',
@ -358,29 +359,63 @@ class SMConfig(object):
# Don't omit the frame pointer.
cxx.cflags += ['/Oy-']
def configure_asan(self, cxx):
if cxx.target.platform != 'linux':
raise Exception('--enable-asan only supported on Linux')
cxx.cflags += ['-fsanitize=address']
cxx.linkflags += ['-fsanitize=address']
if cxx.target.arch == 'x86':
libclang_rt = 'libclang_rt.asan-i386.so'
else:
libclang_rt = 'libclang_rt.asan-x86_64.so'
try:
argv = cxx.cxx_argv + ['--print-file-name', libclang_rt]
output = subprocess.check_output(argv)
output = output.decode('utf-8')
output = output.strip()
except:
raise Exception('Could not find {}'.format(libclang_rt))
print('ASAN library for {}: {}'.format(cxx.target.arch, output))
print('You will need to LD_PRELOAD this into srcds.')
self.asan_libs[cxx.target.arch] = os.path.dirname(output)
def configure_linux(self, cxx):
cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
cxx.defines += ['LINUX', '_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
cxx.linkflags += ['-lm']
if cxx.family == 'gcc':
cxx.linkflags += ['-static-libgcc']
elif cxx.family == 'clang':
cxx.linkflags += ['-lgcc_eh']
cxx.linkflags += ['-static-libstdc++']
def configure_mac(self, cxx):
cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL']
cxx.cflags += ['-mmacosx-version-min=10.5']
cxx.cflags += ['-mmacosx-version-min=10.15']
cxx.linkflags += [
'-mmacosx-version-min=10.5',
'-lstdc++',
'-stdlib=libstdc++',
'-mmacosx-version-min=10.15',
'-stdlib=libc++',
'-lc++',
]
cxx.cxxflags += ['-stdlib=libstdc++']
cxx.cxxflags += ['-stdlib=libc++']
def configure_windows(self, cxx):
cxx.defines += ['WIN32', '_WINDOWS']
def AddVersioning(self, binary, arch):
if builder.target.platform == 'windows':
def add_libamtl(self):
# Add libamtl.
self.libamtl = {}
for cxx in self.all_targets:
def get_configure_fn(cxx):
return lambda builder, name: self.StaticLibrary(builder, cxx, name)
extra_vars = {'Configure': get_configure_fn(cxx)}
libamtl = builder.Build('public/amtl/amtl/AMBuilder', extra_vars)
self.libamtl[cxx.target.arch] = libamtl.binary
def AddVersioning(self, binary):
if binary.compiler.target.platform == 'windows':
binary.sources += ['version.rc']
binary.compiler.rcdefines += [
'BINARY_NAME="{0}"'.format(binary.outputFile),
@ -388,31 +423,39 @@ class SMConfig(object):
]
if self.use_auto_versioning():
binary.compiler.rcdefines += ['SM_GENERATED_BUILD']
elif builder.target.platform == 'mac':
elif binary.compiler.target.platform == 'mac':
if binary.type == 'library':
binary.compiler.postlink += [
'-compatibility_version', '1.0.0',
'-current_version', self.productVersion
]
if self.use_auto_versioning():
binary.compiler.linkflags += [self.versionlib[arch]]
binary.compiler.postlink += [self.versionlib[binary.compiler.target.arch]]
binary.compiler.sourcedeps += SM.generated_headers
return binary
def LibraryBuilder(self, compiler, name, arch):
def LibraryBuilder(self, compiler, name):
binary = compiler.Library(name)
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
self.AddVersioning(binary)
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
self.AddCxxCompat(binary)
# Dumb clang behavior means we have to manually find libclang_rt.
if self.enable_asan:
binary.compiler.linkflags += [
'-shared-libsan',
'-Wl,-rpath={}'.format(self.asan_libs[binary.compiler.target.arch]),
]
return binary
def ProgramBuilder(self, compiler, name, arch):
def ProgramBuilder(self, compiler, name):
binary = compiler.Program(name)
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
self.AddVersioning(binary)
if '-static-libgcc' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-static-libgcc')
if self.enable_asan:
binary.compiler.linkflags.append('-static-libsan')
if '-lgcc_eh' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-lgcc_eh')
if binary.compiler.like('gcc'):
@ -421,25 +464,23 @@ class SMConfig(object):
binary.compiler.linkflags += ['/SUBSYSTEM:CONSOLE']
return binary
def StaticLibraryBuilder(self, compiler, name, arch):
binary = compiler.StaticLibrary(name)
AppendArchSuffix(binary, name, arch)
return binary;
def StaticLibraryBuilder(self, compiler, name):
return compiler.StaticLibrary(name)
def Library(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.LibraryBuilder(compiler, name, arch)
def Library(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.LibraryBuilder(compiler, name)
def Program(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.ProgramBuilder(compiler, name, arch)
def Program(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.ProgramBuilder(compiler, name)
def StaticLibrary(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.StaticLibraryBuilder(compiler, name, arch)
def StaticLibrary(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.StaticLibraryBuilder(compiler, name)
def ConfigureForExtension(self, context, compiler):
compiler.cxxincludes += [
@ -452,167 +493,82 @@ class SMConfig(object):
]
return compiler
def ExtLibrary(self, context, name, arch):
binary = self.Library(context, name, arch)
def ExtLibrary(self, context, compiler, name):
binary = self.Library(context, compiler, name)
SetArchFlags(compiler)
self.ConfigureForExtension(context, binary.compiler)
return binary
def ConfigureForHL2(self, binary, sdk, arch):
def AddCxxCompat(self, binary):
if binary.compiler.target.platform == 'linux':
binary.sources += [
os.path.join(builder.sourcePath, 'public', 'amtl', 'compat', 'stdcxx.cpp'),
]
def ConfigureForHL2(self, context, binary, sdk):
compiler = binary.compiler
SetArchFlags(compiler, arch, builder.target.platform)
SetArchFlags(compiler)
compiler.cxxincludes += [
os.path.join(self.mms_root, 'core'),
os.path.join(self.mms_root, 'core', 'sourcehook'),
]
defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs]
compiler.defines += defines
for other_sdk in self.sdk_manifests:
compiler.defines += ['SE_{}={}'.format(other_sdk['define'], other_sdk['code'])]
paths = [
['public'],
['public', 'engine'],
['public', 'mathlib'],
['public', 'vstdlib'],
['public', 'tier0'],
['public', 'tier1']
]
if sdk.name == 'episode1' or sdk.name == 'darkm':
paths.append(['public', 'dlls'])
paths.append(['game_shared'])
else:
paths.append(['public', 'game', 'server'])
paths.append(['public', 'toolframework'])
paths.append(['game', 'shared'])
paths.append(['common'])
compiler.defines += ['SOURCE_ENGINE=' + sdk.code]
if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'):
# The 2013 SDK already has these in public/tier0/basetypes.h
compiler.defines.remove('stricmp=strcasecmp')
compiler.defines.remove('_stricmp=strcasecmp')
compiler.defines.remove('_snprintf=snprintf')
compiler.defines.remove('_vsnprintf=vsnprintf')
if compiler.like('msvc'):
compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32']
if compiler.version >= 1900:
compiler.linkflags += ['legacy_stdio_definitions.lib']
else:
compiler.defines += ['COMPILER_GCC']
if arch == 'x64':
compiler.defines += ['X64BITS', 'PLATFORM_64BITS']
# For everything after Swarm, this needs to be defined for entity networking
# to work properly with sendprop value changes.
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
compiler.defines += ['NETWORK_VARS_ENABLED']
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2']:
if builder.target.platform in ['linux', 'mac']:
compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE']
if sdk.name == 'csgo' and builder.target.platform == 'linux':
compiler.linkflags += ['-lstdc++']
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
if builder.target.platform == 'linux':
if sdk.name == 'episode1':
lib_folder = os.path.join(sdk.path, 'linux_sdk')
elif sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32')
elif arch == 'x64':
lib_folder = os.path.join(sdk.path, 'lib', 'linux64')
else:
lib_folder = os.path.join(sdk.path, 'lib', 'linux')
elif builder.target.platform == 'mac':
if sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32')
elif arch == 'x64':
lib_folder = os.path.join(sdk.path, 'lib', 'osx64')
else:
lib_folder = os.path.join(sdk.path, 'lib', 'mac')
if builder.target.platform in ['linux', 'mac']:
if sdk.name in ['sdk2013', 'bms'] or arch == 'x64':
compiler.postlink += [
compiler.Dep(os.path.join(lib_folder, 'tier1.a')),
compiler.Dep(os.path.join(lib_folder, 'mathlib.a'))
]
else:
compiler.postlink += [
compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a')),
compiler.Dep(os.path.join(lib_folder, 'mathlib_i486.a'))
]
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
if arch == 'x64':
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces.a'))]
else:
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))]
dynamic_libs = []
if builder.target.platform == 'linux':
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
elif arch == 'x64' and sdk.name == 'csgo':
dynamic_libs = ['libtier0_client.so', 'libvstdlib_client.so']
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
else:
dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so']
elif builder.target.platform == 'mac':
compiler.linkflags.append('-liconv')
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
elif builder.target.platform == 'windows':
libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
if sdk.name in ['swarm', 'blade', 'insurgency', 'doi', 'csgo']:
libs.append('interfaces')
for lib in libs:
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
compiler.linkflags.append(compiler.Dep(lib_path))
for library in dynamic_libs:
source_path = os.path.join(lib_folder, library)
output_path = os.path.join(binary.localFolder, library)
def make_linker(source_path, output_path):
def link(context, binary):
cmd_node, (output,) = context.AddSymlink(source_path, output_path)
return output
return link
linker = make_linker(source_path, output_path)
compiler.linkflags[0:0] = [compiler.Dep(library, linker)]
SdkHelpers.configureCxx(context, binary, sdk)
return binary
def HL2Library(self, context, name, sdk, arch):
binary = self.Library(context, name, arch)
def AddCDetour(self, binary):
public_path = os.path.join(builder.sourcePath, 'public')
binary.sources += [ os.path.join(public_path, 'CDetour', 'detours.cpp') ]
binary.compiler.cxxincludes += [ os.path.join(public_path, 'safetyhook', 'include') ]
for task in self.libsafetyhook:
if task.target.arch == binary.compiler.target.arch:
binary.compiler.linkflags += [task.binary]
return
raise Exception('No suitable build of safetyhook was found.')
def HL2Library(self, context, compiler, name, sdk):
binary = self.Library(context, compiler, name)
self.ConfigureForExtension(context, binary.compiler)
return self.ConfigureForHL2(binary, sdk, arch)
return self.ConfigureForHL2(context, binary, sdk)
def HL2Project(self, context, name):
project = context.cxx.LibraryProject(name)
self.ConfigureForExtension(context, project.compiler)
return project
def HL2Config(self, project, context, compiler, name, sdk):
binary = project.Configure(compiler, name,
'{0} - {1} {2}'.format(self.tag, sdk['name'], compiler.target.arch))
self.AddCxxCompat(binary)
self.AddVersioning(binary)
return self.ConfigureForHL2(context, binary, sdk)
def HL2Config(self, project, name, sdk, arch):
binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name))
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
return self.ConfigureForHL2(binary, sdk, arch)
def HL2ExtConfig(self, project, context, compiler, name, sdk):
binary = project.Configure(compiler, name,
'{0} - {1} {2}'.format(self.tag, sdk['name'], compiler.target.arch))
self.AddCxxCompat(binary)
self.AddVersioning(binary)
self.ConfigureForHL2(context, binary, sdk)
self.ConfigureForExtension(context, binary.compiler)
return binary
if getattr(builder, 'target', None) is not None:
sys.stderr.write("Your output folder was configured for AMBuild 2.1, and SourceMod is now\n")
sys.stderr.write("configured to use AMBuild 2.2. Please remove your output folder and\n")
sys.stderr.write("reconfigure to continue.\n")
os._exit(1)
SM = SMConfig()
SM.detectProductVersion()
SM.detectSDKs()
if not getattr(builder.options, 'scripting_only', False):
SM.detectSDKs()
SM.configure()
SM.add_libamtl()
# This will clone the list and each cxx object as we recurse, preventing child
# scripts from messing up global state.
builder.targets = builder.CloneableList(SM.all_targets)
if SM.use_auto_versioning():
SM.generated_headers = builder.Build(
@ -624,46 +580,104 @@ if SM.use_auto_versioning():
{ 'SM': SM }
)
class SafetyHookShim(object):
def __init__(self):
self.all_targets = {}
self.libsafetyhook = {}
SafetyHook = SafetyHookShim()
SafetyHook.all_targets = SM.all_targets
builder.Build('public/safetyhook/AMBuilder', {'SafetyHook': SafetyHook })
SM.libsafetyhook = SafetyHook.libsafetyhook
class SPRoot(object):
def __init__(self):
self.generated_headers = SM.generated_headers
# SourcePawn's build scripts are always one-offs, and attach the current target
# to the builder, so we have to provide a shim to our StaticLibrary() method.
def StaticLibrary(self, builder, name):
return SM.StaticLibrary(builder, builder.cxx, name)
def Program(self, builder, name):
return SM.Program(builder, builder.cxx, name)
def Library(self, builder, name):
return SM.Library(builder, builder.cxx, name)
@property
def targets(self):
return SM.all_targets
@property
def libamtl(self):
return SM.libamtl
SP_build_parts = ['core']
if getattr(builder.options, 'scripting_only', False):
SP_build_parts = ['spcomp']
# Build SourcePawn externally.
SP = builder.Build('sourcepawn/AMBuildScript', {
'external_root': SM,
'external_root': SPRoot(),
'external_amtl': os.path.join(builder.sourcePath, 'public', 'amtl'),
'external_build': ['core'],
'external_build': SP_build_parts,
})
if len(SP.spcomp) > 1:
SM.spcomp = SP.spcomp['x86']
def IsBetterDefaultSpcomp(spcomp, other):
if other is None:
return True
if spcomp.target.arch == 'universal':
return True
if other.target.arch == 'universal':
return False
return spcomp.target.arch == 'x86'
for spcomp in SP.spcomp:
if IsBetterDefaultSpcomp(spcomp, SM.spcomp):
SM.spcomp = spcomp
SM.spcomp_bins.append(spcomp)
# If we have a universal binary, ignore all other spcomps.
if SM.spcomp.target.arch == 'universal':
SM.spcomp_bins = [SM.spcomp]
if not getattr(builder.options, 'scripting_only', False):
for cxx in SM.all_targets:
SM.spvm += [
SP.libsourcepawn[cxx.target.arch]
]
if getattr(builder.options, 'scripting_only', False):
BuildScripts = [
'tools/buildbot/PackageHelpers',
'tools/buildbot/ToolsPackageScript',
]
else:
SM.spcomp = SP.spcomp[list(SP.spcomp.keys())[0]]
SM.spcomp_bins = list(SP.spcomp.values())
for arch in SM.archs:
SM.binaries += [
SP.libsourcepawn[arch]
BuildScripts = [
'loader/AMBuilder',
'core/AMBuilder',
'core/logic/AMBuilder',
'extensions/bintools/AMBuilder',
'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/dhooks/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/pgsql/AMBuilder',
'extensions/regex/AMBuilder',
'extensions/sdkhooks/AMBuilder',
'extensions/sdktools/AMBuilder',
'extensions/sqlite/AMBuilder',
'extensions/tf2/AMBuilder',
'extensions/topmenus/AMBuilder',
'extensions/updater/AMBuilder',
]
BuildScripts = [
'loader/AMBuilder',
'core/AMBuilder',
'core/logic/AMBuilder',
'extensions/bintools/AMBuilder',
'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/regex/AMBuilder',
'extensions/sdkhooks/AMBuilder',
'extensions/sdktools/AMBuilder',
'extensions/sqlite/AMBuilder',
'extensions/tf2/AMBuilder',
'extensions/topmenus/AMBuilder',
'extensions/updater/AMBuilder',
]
if builder.backend == 'amb2':
BuildScripts += [
'plugins/AMBuilder',
'tools/buildbot/PackageScript',
]
if builder.backend == 'amb2':
BuildScripts += [
'plugins/AMBuilder',
'tools/buildbot/PackageHelpers',
'tools/buildbot/PackageScript',
]
builder.Build(BuildScripts, { 'SM': SM })

View File

@ -16,6 +16,7 @@ Development
- [SourcePawn scripting](https://wiki.alliedmods.net/Category:SourceMod_Scripting): SourcePawn examples and introduction to the language
- [SourceMod plugin API](https://sm.alliedmods.net/new-api): Online SourceMod plugin API reference generated from the include files
- [SourceMod extension development](https://wiki.alliedmods.net/Category:SourceMod_Development): C++ examples and introduction to various extension interfaces
- [Translation project](https://github.com/orgs/alliedmodders/projects/1): Help [translate SourceMod](https://wiki.alliedmods.net/Translations_(SourceMod_Scripting)) into your language
Contact
-------

17
appveyor.yml Normal file
View File

@ -0,0 +1,17 @@
version: 1.0.{build}
image: Visual Studio 2015
clone_folder: c:/projects/sourcemod
clone_depth: 1
install:
- cmd: set PATH=C:\Python38;C:\Python38\Scripts;%PATH%
- cmd: git submodule update --init --recursive
- cmd: git pull --recurse-submodules
- cmd: cd ..
- ps: sourcemod/tools/checkout-deps.ps1 -SDKs episode1,css,tf2,l4d2,csgo
- cmd: cd sourcemod
build_script:
- cmd: call "%VS140COMNTOOLS%/vsvars32.bat"
- cmd: mkdir build
- cmd: cd build
- cmd: python.exe ../configure.py --enable-optimize --no-mysql --sdks=episode1,css,tf2,l4d2,csgo
- cmd: ambuild

View File

@ -67,7 +67,7 @@ struct DatabaseInfo;
class IPlayerInfoBridge;
class ICommandArgs;
typedef ke::Lambda<bool(int client, const ICommandArgs*)> CommandFunc;
typedef ke::Function<bool(int client, const ICommandArgs*)> CommandFunc;
class CoreProvider
{
@ -86,9 +86,6 @@ public:
const char *gamesuffix;
/* Data */
ServerGlobals *serverGlobals;
void * serverFactory;
void * engineFactory;
void * matchmakingDSFactory;
SMGlobalClass * listeners;
// ConVar functions.
@ -115,6 +112,8 @@ public:
virtual void ConsolePrint(const char *fmt, ...) = 0;
virtual void ConsolePrintVa(const char *fmt, va_list ap) = 0;
virtual void FormatSourceBinaryName(const char *basename, char *buffer, size_t maxlength) = 0;
// Game engine helper functions.
virtual bool IsClientConVarQueryingSupported() = 0;
virtual int QueryClientConVar(int client, const char *cvar) = 0;

View File

@ -44,6 +44,7 @@ public:
virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file) = 0;
virtual bool EndOfFile(FileHandle_t file) = 0;
virtual bool FileExists(const char *pFileName, const char *pPathID = 0) = 0;
virtual unsigned int Size(FileHandle_t file) = 0;
virtual unsigned int Size(const char *pFileName, const char *pPathID = 0) = 0;
virtual int Read(void* pOutput, int size, FileHandle_t file) = 0;
virtual int Write(void const* pInput, int size, FileHandle_t file) = 0;
@ -56,6 +57,7 @@ public:
virtual void RenameFile(char const *pOldPath, char const *pNewPath, const char *pathID = 0) = 0;
virtual bool IsDirectory(const char *pFileName, const char *pathID = 0) = 0;
virtual void CreateDirHierarchy(const char *path, const char *pathID = 0) = 0;
virtual int GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, int nMaxLen) = 0;
};
} // namespace SourceMod

View File

@ -73,6 +73,9 @@ struct sm_logic_t
void (*FreeCellArray)(ICellArray *arr);
void * (*FromPseudoAddress)(uint32_t pseudoAddr);
uint32_t (*ToPseudoAddress)(void *addr);
void (*SetEntityLumpWritable)(bool writable);
bool (*ParseEntityLumpString)(const char *entityString, int &status, size_t &position);
const char * (*GetEntityLumpString)();
IScriptManager *scripts;
IShareSys *sharesys;
IExtensionSys *extsys;

View File

@ -53,24 +53,6 @@
* passwords to work, for security reasons.
*/
"PassInfoVar" "_password"
/**
* Specifies the sound that gets played when an item is selected from a menu.
*/
"MenuItemSound" "buttons/button14.wav"
/**
* Specifies the sound that gets played when an "Exit" button is selected
* from a menu.
*/
"MenuExitSound" "buttons/combine_button7.wav"
/**
* Specifies the sound that gets played when an "Exit Back" button is selected
* from a menu. This is the special "Back" button that is intended to roll back
* to a previous menu.
*/
"MenuExitBackSound" "buttons/combine_button7.wav"
/**
* Enables or disables whether SourceMod reads a client's cl_language cvar to set
@ -145,5 +127,15 @@
* Disable this option at your own risk.
*/
"FollowCSGOServerGuidelines" "yes"
}
/**
* Controls whether the SourcePawn runtime will generate additional metadata about
* JIT-compiled functions for performance profiling or debugging purposes.
*
* "none" - Don't generate any additional JIT metadata
* "default" - Generate basic perf metadata (on Linux) and delete it automatically on quit
* "perf" - Generate basic perf metadata (Linux only - function names)
* "jitdump" - Generate extended perf metadata (Linux only - function names, bytecode, and source information)
*/
"JITMetadata" "default"
}

Binary file not shown.

View File

@ -1,4 +1,38 @@
"Languages"
{
"en" "English"
"ar" "Arabic" // Arabic
"bg" "Bulgarian" // Bulgarian
"chi" "SChinese" // Chinese (Simplified)
"cze" "Czech" // Czech
"da" "Danish" // Danish
"de" "German" // German
"el" "Greek" // Greek
"en" "English" // English
"es" "Spanish" // Spanish
"fi" "Finnish" // Finnish
"fr" "French" // French
"he" "Hebrew" // Hebrew
"hu" "Hungarian" // Hungarian
"it" "Italian" // Italian
"jp" "Japanese" // Japanese
"ko" "Korean" // Korean
"ko" "KoreanA" // Korean (https://bugs.alliedmods.net/show_bug.cgi?id=4667)
"las" "LatAm" // Latin American Spanish
"lt" "Lithuanian" // Lithuanian
"lv" "Latvian" // Latvian
"nl" "Dutch" // Dutch
"no" "Norwegian" // Norwegian
"pl" "Polish" // Polish
"pt" "Brazilian" // Brazilian Portuguese
"pt_p" "Portuguese" // Portuguese
"ro" "Romanian" // Romanian
"ru" "Russian" // Russian
"sk" "Slovak" // Slovak
"sv" "Swedish" // Swedish
"th" "Thai" // Thai
"tr" "Turkish" // Turkish
"ua" "Ukrainian" // Ukrainian
"vi" "Vietnamese" // Vietnamese
"zho" "TChinese" // Chinese (Traditional)
}

View File

@ -0,0 +1,41 @@
CREATE TABLE IF NOT EXISTS sm_cookies
(
id serial,
name varchar(30) NOT NULL UNIQUE,
description varchar(255),
access INTEGER,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS sm_cookie_cache
(
player varchar(65) NOT NULL,
cookie_id int NOT NULL,
value varchar(100),
timestamp int NOT NULL,
PRIMARY KEY (player, cookie_id)
);
CREATE LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION add_or_update_cookie(in_player VARCHAR(65), in_cookie INT, in_value VARCHAR(100), in_time INT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the it.
UPDATE sm_cookie_cache SET value = in_value, timestamp = in_time WHERE player = in_player AND cookie_id = in_cookie;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert.
-- if someone else inserts the same key concurrently, we could get a unique-key failure.
BEGIN
INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES (in_player, in_cookie, in_value, in_time);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- do nothing... loop again, and we'll update.
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;

View File

@ -0,0 +1,65 @@
CREATE TABLE sm_admins (
id serial,
authtype varchar(6) NOT NULL,
CHECK (authtype in ('steam', 'name', 'ip')),
identity varchar(65) NOT NULL,
password varchar(65),
flags varchar(30) NOT NULL,
name varchar(65) NOT NULL,
immunity int NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE sm_groups (
id serial,
flags varchar(30) NOT NULL,
name varchar(120) NOT NULL,
immunity_level int NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE sm_group_immunity (
group_id int NOT NULL,
other_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
FOREIGN KEY (other_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
PRIMARY KEY (group_id, other_id)
);
CREATE TABLE sm_group_overrides (
group_id int NOT NULL,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
type varchar(10) NOT NULL,
CHECK (type in ('command', 'group')),
name varchar(32) NOT NULL,
access varchar(5) NOT NULL,
CHECK (access in ('allow', 'deny')),
PRIMARY KEY (group_id, type, name)
);
CREATE TABLE sm_overrides (
type varchar(10) NOT NULL,
CHECK (type in ('command', 'group')),
name varchar(32) NOT NULL,
flags varchar(30) NOT NULL,
PRIMARY KEY (type,name)
);
CREATE TABLE sm_admins_groups (
admin_id int NOT NULL,
group_id int NOT NULL,
FOREIGN KEY (admin_id) REFERENCES sm_admins(id) ON DELETE CASCADE,
FOREIGN KEY (group_id) REFERENCES sm_groups(id) ON DELETE CASCADE,
inherit_order int NOT NULL,
PRIMARY KEY (admin_id, group_id)
);
-- side note, this is pgsql module, sm_config will not exist if the above stuff exists... and it's being left to the admin
-- to figure out if it exists.
CREATE TABLE sm_config (
cfg_key varchar(32) NOT NULL,
cfg_value varchar(255) NOT NULL,
PRIMARY KEY (cfg_key)
);
INSERT INTO sm_config (cfg_key, cfg_value) VALUES ('admin_version', '1.0.0.1409');

View File

@ -1,42 +1,49 @@
# vim: set ts=2 sw=2 tw=99 noet:
import sys
try:
from ambuild2 import run, util
from ambuild2 import run, util
except:
try:
import ambuild
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
except:
sys.stderr.write('AMBuild must be installed to build this project.\n')
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
sys.exit(1)
try:
import ambuild
sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n')
sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n')
except:
sys.stderr.write('AMBuild must be installed to build this project.\n')
sys.stderr.write('http://www.alliedmods.net/ambuild\n')
sys.exit(1)
def make_objdir_name(p):
return 'obj-' + util.Platform() + '-' + p.target_arch
# Hack to show a decent upgrade message, which wasn't done until 2.2.
ambuild_version = getattr(run, 'CURRENT_API', '2.1')
if ambuild_version.startswith('2.1'):
sys.stderr.write("AMBuild 2.2 or higher is required; please update\n")
sys.exit(1)
parser = run.BuildParser(sourcePath=sys.path[0], api='2.1')
parser.default_arch = 'x86'
parser.default_build_folder = make_objdir_name
parser.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
help='Root search folder for HL2SDKs')
parser.options.add_option('--mysql-path', type=str, dest='mysql_path', default=None,
help='Path to MySQL 5')
parser.options.add_option('--mysql64-path', type=str, dest='mysql64_path', default=None,
help='Path to 64-bit MySQL 5')
parser.options.add_option('--mms-path', type=str, dest='mms_path', default=None,
parser = run.BuildParser(sourcePath=sys.path[0], api='2.2')
parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
help='Root search folder for HL2SDKs')
parser.options.add_argument('--mysql-path', type=str, dest='mysql_path', default=None,
help='Path to MySQL 5')
parser.options.add_argument('--mysql64-path', type=str, dest='mysql64_path', default=None,
help='Path to 64-bit MySQL 5')
parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None,
help='Path to Metamod:Source')
parser.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
help='Enable debugging symbols')
parser.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt',
parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt',
help='Enable optimization')
parser.options.add_option('--no-mysql', action='store_false', default=True, dest='hasMySql',
parser.options.add_argument('--no-mysql', action='store_false', default=True, dest='hasMySql',
help='Disable building MySQL extension')
parser.options.add_option('-s', '--sdks', default='all', dest='sdks',
help='Build against specified SDKs; valid args are "all", "present", or '
'comma-delimited list of engine names (default: %default)')
parser.options.add_option('--breakpad-dump', action='store_true', dest='breakpad_dump',
default=False, help='Dump and upload breakpad symbols')
parser.options.add_option('--disable-auto-versioning', action='store_true', dest='disable_auto_versioning',
parser.options.add_argument('-s', '--sdks', default='present', dest='sdks',
help='Build against specified SDKs; valid args are "none", "all", "present",'
' or comma-delimited list of engine names')
parser.options.add_argument('--breakpad-dump', action='store_true', dest='breakpad_dump',
default=False, help='Dump and upload breakpad symbols')
parser.options.add_argument('--disable-auto-versioning', action='store_true', dest='disable_auto_versioning',
default=False, help='Disable the auto versioning script')
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
help="Override the target architecture (use commas to separate multiple targets).")
parser.options.add_argument('--scripting-only', action='store_true', dest='scripting_only', default=False,
help="Only build and package the files required for scripting in SourcePawn.")
parser.options.add_argument('--enable-asan', action='store_true', dest='enable_asan',
default=False, help='Enable ASAN (clang only)')
parser.Configure()

View File

@ -1,7 +1,8 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os
project = SM.HL2Project(builder, 'sourcemod')
project = builder.LibraryProject('sourcemod')
project.sources += [
'MenuStyle_Valve.cpp',
'logic_bridge.cpp',
@ -37,76 +38,103 @@ project.sources += [
'MenuStyle_Radio.cpp',
'sm_autonatives.cpp',
'ConsoleDetours.cpp',
'vprof_tool.cpp',
'smn_commandline.cpp',
'GameHooks.cpp',
]
for sdk_name in SM.sdks:
sdk = SM.sdks[sdk_name]
for arch in SM.archs:
if not arch in sdk.platformSpec[builder.target.platform]:
continue
# SDK name to shipping gamedir
pb_gamedir_map = {
'csgo': 'csgo',
'blade': 'berimbau',
'mcv': 'vietnam',
}
binary_name = 'sourcemod.' + sdk.ext
# SDK name to source code gamedir
pb_gamesrcdir_map = {
'csgo': 'cstrike15',
'blade': 'berimbau',
'mcv': 'vietnam',
}
binary = SM.HL2Config(project, binary_name, sdk, arch)
compiler = binary.compiler
for sdk_target in SM.sdk_targets:
sdk = sdk_target.sdk
cxx = sdk_target.cxx
binary_name = 'sourcemod.' + sdk['extension']
needs_protobuf = sdk['name'] in ['csgo', 'blade', 'mcv']
binary = SM.HL2Config(project, builder, cxx, binary_name, sdk)
SM.ConfigureForExtension(builder, binary.compiler)
compiler = binary.compiler
compiler.cxxincludes += [
builder.sourcePath
]
if needs_protobuf:
compiler.cxxincludes += [
builder.sourcePath
os.path.join(sdk['path'], 'common', 'protobuf-2.5.0', 'src'),
os.path.join(sdk['path'], 'public', 'engine', 'protobuf'),
os.path.join(sdk['path'], 'public', 'game', 'shared', pb_gamedir_map[sdk['name']], 'protobuf')
]
if compiler.like('msvc'):
compiler.defines += ['_ALLOW_KEYWORD_MACROS']
if cxx.target.platform == 'linux':
compiler.postlink += ['-lpthread', '-lrt']
if needs_protobuf:
if compiler.target.platform == 'linux':
if compiler.target.arch == 'x86':
lib_path = os.path.join(sdk['path'], 'lib', 'linux32', 'release', 'libprotobuf.a')
elif compiler.target.arch == 'x86_64':
lib_path = os.path.join(sdk['path'], 'lib', 'linux64', 'release', 'libprotobuf.a')
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a']
elif compiler.target.platform == 'mac':
if compiler.target.arch == 'x86':
lib_path = os.path.join(sdk['path'], 'lib', 'osx32', 'release', 'libprotobuf.a')
elif compiler.target.arch == 'x86_64':
lib_path = os.path.join(sdk['path'], 'lib', 'osx64', 'release', 'libprotobuf.a')
elif compiler.target.platform == 'windows':
msvc_ver = compiler.version
vs_year = ''
platform = ''
if compiler.target.arch == 'x86':
platform = 'win32'
elif compiler.target.arch == 'x86_64':
platform = 'win64'
if 1900 <= msvc_ver < 2000:
vs_year = '2015'
else:
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"')
if 'DEBUG' in compiler.defines:
lib_path = os.path.join(sdk['path'], 'lib', platform, 'debug', 'vs' + vs_year, 'libprotobuf.lib')
else:
lib_path = os.path.join(sdk['path'], 'lib', platform, 'release', 'vs' + vs_year, 'libprotobuf.lib')
compiler.linkflags.insert(0, lib_path)
if needs_protobuf:
binary.sources += ['smn_protobuf.cpp']
else:
binary.sources += ['smn_bitbuffer.cpp']
if sdk['name'] != 'blade':
binary.sources += [
'vprof_tool.cpp',
]
if sdk.name == 'csgo':
compiler.cxxincludes += [
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
os.path.join(sdk.path, 'public', 'engine', 'protobuf'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf')
]
if compiler.like('msvc'):
compiler.defines += ['_ALLOW_KEYWORD_MACROS']
if builder.target.platform == 'linux':
compiler.postlink += ['-lpthread', '-lrt']
if sdk.name == 'csgo':
if builder.target.platform == 'linux':
if arch == 'x86':
lib_path = os.path.join(sdk.path, 'lib', 'linux32', 'release', 'libprotobuf.a')
elif arch == 'x64':
lib_path = os.path.join(sdk.path, 'lib', 'linux64', 'release', 'libprotobuf.a')
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a']
elif builder.target.platform == 'mac':
if arch == 'x86':
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
elif arch == 'x64':
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a')
elif builder.target.platform == 'windows':
msvc_ver = compiler.version
vs_year = ''
if msvc_ver == 1800:
vs_year = '2013'
elif 1900 <= msvc_ver < 2000:
vs_year = '2015'
else:
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"')
if 'DEBUG' in compiler.defines:
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'debug', 'vs' + vs_year, 'libprotobuf.lib')
else:
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'release', 'vs' + vs_year, 'libprotobuf.lib')
compiler.linkflags.insert(0, binary.Dep(lib_path))
if sdk.name == 'csgo':
binary.sources += ['smn_protobuf.cpp']
else:
binary.sources += ['smn_bitbuffer.cpp']
if sdk.name == 'csgo':
if needs_protobuf:
binary.sources += [
os.path.join(sdk['path'], 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
os.path.join(sdk['path'], 'public', 'game', 'shared', pb_gamedir_map[sdk['name']], 'protobuf', pb_gamesrcdir_map[sdk['name']] + '_usermessages.pb.cc'),
os.path.join(sdk['path'], 'public', 'game', 'shared', pb_gamedir_map[sdk['name']], 'protobuf', pb_gamesrcdir_map[sdk['name']] + '_usermessage_helpers.cpp'),
]
if sdk['name'] == 'mcv':
binary.sources += [
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessage_helpers.cpp'),
os.path.join(sdk['path'], 'public', 'game', 'shared', pb_gamedir_map[sdk['name']], 'protobuf', pb_gamesrcdir_map[sdk['name']] + '_gcmessages.pb.cc'),
os.path.join(sdk['path'], 'public', 'engine', 'protobuf', 'engine_gcmessages.pb.cc'),
]
SM.binaries += builder.Add(project)

View File

@ -29,6 +29,8 @@
* Version: $Id$
*/
#include <memory>
#include <ITextParsers.h>
#include "ChatTriggers.h"
#include "sm_stringutil.h"
@ -64,7 +66,7 @@ ChatTriggers::~ChatTriggers()
void ChatTriggers::SetChatTrigger(ChatTriggerType type, const char *value)
{
ke::AutoPtr<char[]> filtered(new char[strlen(value) + 1]);
std::unique_ptr<char[]> filtered(new char[strlen(value) + 1]);
const char *src = value;
char *dest = filtered.get();
@ -135,26 +137,26 @@ void ChatTriggers::OnSourceModGameInitialized()
};
if (ConCommand *say = FindCommand("say")) {
hooks_.append(sCoreProviderImpl.AddCommandHook(say, pre_hook));
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say, post_hook));
hooks_.push_back(sCoreProviderImpl.AddCommandHook(say, pre_hook));
hooks_.push_back(sCoreProviderImpl.AddPostCommandHook(say, post_hook));
}
if (ConCommand *say_team = FindCommand("say_team")) {
hooks_.append(sCoreProviderImpl.AddCommandHook(say_team, pre_hook));
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say_team, post_hook));
hooks_.push_back(sCoreProviderImpl.AddCommandHook(say_team, pre_hook));
hooks_.push_back(sCoreProviderImpl.AddPostCommandHook(say_team, post_hook));
}
#if SOURCE_ENGINE == SE_EPISODEONE
m_bIsINS = (strcmp(g_SourceMod.GetGameFolderName(), "insurgency") == 0);
if (m_bIsINS) {
if (ConCommand *say2 = FindCommand("say2")) {
hooks_.append(sCoreProviderImpl.AddCommandHook(say2, pre_hook));
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say2, post_hook));
hooks_.push_back(sCoreProviderImpl.AddCommandHook(say2, pre_hook));
hooks_.push_back(sCoreProviderImpl.AddPostCommandHook(say2, post_hook));
}
}
#elif SOURCE_ENGINE == SE_NUCLEARDAWN
if (ConCommand *say_squad = FindCommand("say_squad")) {
hooks_.append(sCoreProviderImpl.AddCommandHook(say_squad, pre_hook));
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say_squad, post_hook));
hooks_.push_back(sCoreProviderImpl.AddCommandHook(say_squad, pre_hook));
hooks_.push_back(sCoreProviderImpl.AddPostCommandHook(say_squad, post_hook));
}
#endif
}
@ -275,10 +277,10 @@ bool ChatTriggers::OnSayCommand_Pre(int client, const ICommandArgs *command)
bool is_silent = false;
// Prefer the silent trigger in case of clashes.
if (strchr(m_PrivTrigger.chars(), m_ArgSBackup[0])) {
if (strchr(m_PrivTrigger.c_str(), m_ArgSBackup[0])) {
is_trigger = true;
is_silent = true;
} else if (strchr(m_PubTrigger.chars(), m_ArgSBackup[0])) {
} else if (strchr(m_PubTrigger.c_str(), m_ArgSBackup[0])) {
is_trigger = true;
}
@ -361,7 +363,7 @@ bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args)
if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
{
/* Check if we had an "sm_" prefix */
if (strncmp(cmd_buf, "sm_", 3) == 0)
if (strncasecmp(cmd_buf, "sm_", 3) == 0)
{
return false;
}
@ -385,15 +387,7 @@ bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args)
/* See if we need to do extra string manipulation */
if (prepended)
{
size_t len;
/* Check if we need to prepend sm_ */
if (prepended)
{
len = ke::SafeSprintf(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
} else {
len = ke::SafeStrcpy(m_ToExecute, sizeof(m_ToExecute), args);
}
ke::SafeSprintf(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
} else {
ke::SafeStrcpy(m_ToExecute, sizeof(m_ToExecute), args);
}
@ -467,3 +461,23 @@ bool ChatTriggers::WasFloodedMessage()
{
return m_bWasFloodedMessage;
}
const char *ChatTriggers::GetPublicChatTrigger()
{
if (!m_PubTrigger.length())
{
return NULL;
}
return m_PubTrigger.c_str();
}
const char *ChatTriggers::GetPrivateChatTrigger()
{
if (!m_PrivTrigger.length())
{
return NULL;
}
return m_PrivTrigger.c_str();
}

View File

@ -63,6 +63,8 @@ public:
unsigned int SetReplyTo(unsigned int reply);
bool IsChatTrigger();
bool WasFloodedMessage();
const char *GetPublicChatTrigger();
const char *GetPrivateChatTrigger();
private:
enum ChatTriggerType {
ChatTrigger_Public,
@ -73,9 +75,9 @@ private:
bool ClientIsFlooding(int client);
cell_t CallOnClientSayCommand(int client);
private:
ke::Vector<ke::RefPtr<CommandHook>> hooks_;
ke::AString m_PubTrigger;
ke::AString m_PrivTrigger;
std::vector<ke::RefPtr<CommandHook>> hooks_;
std::string m_PubTrigger;
std::string m_PrivTrigger;
bool m_bWillProcessInPost;
bool m_bIsChatTrigger;
bool m_bWasFloodedMessage;

View File

@ -30,21 +30,22 @@
*/
#include "ConCmdManager.h"
#include "sm_stringutil.h"
#include "PlayerManager.h"
#include "HalfLife2.h"
#include "ChatTriggers.h"
#include "logic_bridge.h"
#include "sourcemod.h"
#include "provider.h"
#include "command_args.h"
#include <bridge/include/IScriptManager.h>
#include "ChatTriggers.h"
#include "HalfLife2.h"
#include "PlayerManager.h"
#include "command_args.h"
#include "logic_bridge.h"
#include "provider.h"
#include "sm_stringutil.h"
#include "sourcemod.h"
using namespace ke;
ConCmdManager g_ConCmds;
typedef ke::LinkedList<CmdHook *> PluginHookList;
typedef std::list<CmdHook *> PluginHookList;
void RegisterInPlugin(CmdHook *hook);
ConCmdManager::ConCmdManager()
@ -120,8 +121,13 @@ void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
if (hook->admin)
hook->admin->group->hooks.remove(hook);
if (hook->info->hooks.empty())
if (hook->info->hooks.empty()) {
RemoveConCmd(hook->info, hook->info->pCmd->GetName(), true);
}
else { // update plugin reference to next hook in line
auto next = *hook->info->hooks.begin();
next->info->pPlugin = next->plugin;
}
iter = pList->erase(iter);
delete hook;
@ -147,30 +153,13 @@ ConCmdInfo *ConCmdManager::FindInTrie(const char *name)
return pInfo;
}
ConCmdList::iterator ConCmdManager::FindInList(const char *cmd)
{
List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
while (iter != m_CmdList.end())
{
if (strcasecmp((*iter)->pCmd->GetName(), cmd) == 0)
break;
iter++;
}
return iter;
}
ResultType ConCmdManager::DispatchClientCommand(int client, const char *cmd, int args, ResultType type)
{
ConCmdInfo *pInfo;
ConCmdInfo *pInfo = FindInTrie(cmd);
if ((pInfo = FindInTrie(cmd)) == NULL)
if (pInfo == NULL)
{
ConCmdList::iterator item = FindInList(cmd);
if (item == m_CmdList.end())
return type;
pInfo = *item;
return type;
}
cell_t result = type;
@ -181,7 +170,7 @@ ResultType ConCmdManager::DispatchClientCommand(int client, const char *cmd, int
if (hook->type == CmdHook::Server || !hook->pf->IsRunnable())
continue;
if (hook->admin && !CheckAccess(client, cmd, hook->admin))
if (hook->admin && !CheckAccess(client, cmd, hook->admin.get()))
{
if (result < Pl_Handled)
result = Pl_Handled;
@ -225,17 +214,7 @@ bool ConCmdManager::InternalDispatch(int client, const ICommandArgs *args)
ConCmdInfo *pInfo = FindInTrie(cmd);
if (pInfo == NULL)
{
/* Unfortunately, we now have to do a slow lookup because Valve made client commands
* case-insensitive. We can't even use our sortedness.
*/
if (client == 0 && !engine->IsDedicatedServer())
return false;
ConCmdList::iterator item = FindInList(cmd);
if (item == m_CmdList.end())
return false;
pInfo = *item;
return false;
}
/* This is a hack to prevent say triggers from firing on messages that were
@ -271,7 +250,7 @@ bool ConCmdManager::InternalDispatch(int client, const ICommandArgs *args)
} else {
// Check admin rights if needed. realClient isn't needed since we
// should bypass admin checks if client == 0 anyway.
if (client && hook->admin && !CheckAccess(client, cmd, hook->admin))
if (client && hook->admin && !CheckAccess(client, cmd, hook->admin.get()))
{
if (result < Pl_Handled)
result = Pl_Handled;
@ -359,8 +338,8 @@ bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
}
RefPtr<CommandGroup> cmdgroup = i->value;
CmdHook *pHook = new CmdHook(CmdHook::Client, pInfo, pFunction, description);
pHook->admin = new AdminCmdInfo(cmdgroup, adminflags);
CmdHook *pHook = new CmdHook(CmdHook::Client, pInfo, pFunction, description, pPlugin);
pHook->admin = std::make_unique<AdminCmdInfo>(cmdgroup, adminflags);
/* First get the command group override, if any */
bool override = adminsys->GetCommandOverride(group,
@ -380,7 +359,7 @@ bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
pHook->admin->eflags = pHook->admin->flags;
pInfo->eflags = pHook->admin->eflags;
cmdgroup->hooks.append(pHook);
cmdgroup->hooks.push_back(pHook);
pInfo->hooks.append(pHook);
RegisterInPlugin(pHook);
return true;
@ -398,7 +377,7 @@ bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
if (!pInfo)
return false;
CmdHook *pHook = new CmdHook(CmdHook::Server, pInfo, pFunction, description);
CmdHook *pHook = new CmdHook(CmdHook::Server, pInfo, pFunction, description, pPlugin);
pInfo->hooks.append(pHook);
RegisterInPlugin(pHook);
@ -425,13 +404,13 @@ void RegisterInPlugin(CmdHook *hook)
const char *cmd = (*iter)->info->pCmd->GetName();
if (strcmp(orig, cmd) < 0)
{
pList->insertBefore(iter, hook);
pList->emplace(iter, hook);
return;
}
iter++;
}
pList->append(hook);
pList->emplace_back(hook);
}
void ConCmdManager::AddToCmdList(ConCmdInfo *info)
@ -495,7 +474,7 @@ void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, Flag
for (PluginHookList::iterator iter = group->hooks.begin(); iter != group->hooks.end(); iter++)
{
CmdHook *hook = *iter;
if (remove)
if (!remove)
hook->admin->eflags = bits;
else
hook->admin->eflags = hook->admin->flags;
@ -562,10 +541,6 @@ ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *descri
ConCmdInfo *pInfo;
if (!m_Cmds.retrieve(name, &pInfo))
{
ConCmdList::iterator item = FindInList(name);
if (item != m_CmdList.end())
return *item;
pInfo = new ConCmdInfo();
/* Find the commandopan */
ConCommand *pCmd = FindCommand(name);
@ -648,7 +623,7 @@ void ConCmdManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs
name = hook->info->pCmd->GetName();
if (hook->helptext.length())
help = hook->helptext.chars();
help = hook->helptext.c_str();
else
help = hook->info->pCmd->GetHelpText();
UTIL_ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);

View File

@ -32,6 +32,14 @@
#ifndef _INCLUDE_SOURCEMOD_CONCMDMANAGER_H_
#define _INCLUDE_SOURCEMOD_CONCMDMANAGER_H_
#include <list>
#include <memory>
#include <am-inlinelist.h>
#include <am-refcounting.h>
#include <am-utility.h>
#include <sm_stringhashmap.h>
#include "sm_globals.h"
#include "sourcemm_api.h"
#include <IForwardSys.h>
@ -41,12 +49,7 @@
#include <IAdminSystem.h>
#include "concmd_cleaner.h"
#include "GameHooks.h"
#include <am-autoptr.h>
#include <sm_stringhashmap.h>
#include <am-utility.h>
#include <am-inlinelist.h>
#include <am-linkedlist.h>
#include <am-refcounting.h>
#include <sm_namehashset.h>
using namespace SourceHook;
@ -55,7 +58,7 @@ struct ConCmdInfo;
struct CommandGroup : public ke::Refcounted<CommandGroup>
{
ke::LinkedList<CmdHook *> hooks;
std::list<CmdHook *> hooks;
};
struct AdminCmdInfo
@ -78,10 +81,11 @@ struct CmdHook : public ke::InlineListNode<CmdHook>
Client
};
CmdHook(Type type, ConCmdInfo *cmd, IPluginFunction *fun, const char *description)
CmdHook(Type type, ConCmdInfo *cmd, IPluginFunction *fun, const char *description, IPlugin *plugin)
: type(type),
info(cmd),
pf(fun),
plugin(plugin),
helptext(description)
{
}
@ -89,8 +93,9 @@ struct CmdHook : public ke::InlineListNode<CmdHook>
Type type;
ConCmdInfo *info;
IPluginFunction *pf; /* function hook */
ke::AString helptext; /* help text */
ke::AutoPtr<AdminCmdInfo> admin; /* admin requirements, if any */
IPlugin *plugin; /* owning plugin */
std::string helptext; /* help text */
std::unique_ptr<AdminCmdInfo> admin; /* admin requirements, if any */
};
typedef ke::InlineList<CmdHook> CmdHookList;
@ -104,12 +109,32 @@ struct ConCmdInfo
pCmd = nullptr;
eflags = 0;
}
bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */
ConCommand *pCmd; /**< Pointer to the command itself */
CmdHookList hooks; /**< Hook list */
FlagBits eflags; /**< Effective admin flags */
ke::RefPtr<CommandHook> sh_hook; /**< SourceHook hook, if any. */
IPlugin *pPlugin; /**< Owning plugin handle. */
struct ConCmdPolicy
{
static inline bool matches(const char *name, ConCmdInfo *info)
{
const char *conCmdChars = info->pCmd->GetName();
std::string concmdName = ke::Lowercase(conCmdChars);
std::string input = ke::Lowercase(name);
return concmdName == input;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
std::string lower = ke::Lowercase(key.c_str());
return detail::CharsAndLength(lower.c_str()).hash();
}
};
};
typedef List<ConCmdInfo *> ConCmdList;
@ -153,11 +178,6 @@ private:
void AddToCmdList(ConCmdInfo *info);
void RemoveConCmd(ConCmdInfo *info, const char *cmd, bool untrack);
bool CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin);
// Case insensitive
ConCmdList::iterator FindInList(const char *name);
// Case sensitive
ConCmdInfo *FindInTrie(const char *name);
public:
inline const List<ConCmdInfo *> & GetCommandList()
@ -167,7 +187,7 @@ public:
private:
typedef StringHashMap<ke::RefPtr<CommandGroup> > GroupMap;
StringHashMap<ConCmdInfo *> m_Cmds; /* command lookup */
NameHashSet<ConCmdInfo *, ConCmdInfo::ConCmdPolicy> m_Cmds; /* command lookup */
GroupMap m_CmdGrps; /* command group map */
ConCmdList m_CmdList; /* command list */
};

View File

@ -41,7 +41,11 @@ ConVarManager g_ConVarManager;
const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
typedef List<const ConVar *> ConVarList;
NameHashSet<ConVarInfo *> convar_cache;
NameHashSet<ConVarInfo *, ConVarInfo::ConVarPolicy> convar_cache;
enum {
eQueryCvarValueStatus_Cancelled = -1,
};
class ConVarReentrancyGuard
{
@ -206,18 +210,27 @@ void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *na
void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
{
ConVarList *pConVarList;
List<ConVarQuery>::iterator iter;
/* If plugin has a convar list, free its memory */
if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
{
delete pConVarList;
}
/* Clear any references to this plugin as the convar creator */
for (List<ConVarInfo *>::iterator iter = m_ConVars.begin(); iter != m_ConVars.end(); ++iter)
{
ConVarInfo *pInfo = (*iter);
if (pInfo->pPlugin == plugin)
{
pInfo->pPlugin = nullptr;
}
}
const IPluginRuntime * pRuntime = plugin->GetRuntime();
/* Remove convar queries for this plugin that haven't returned results yet */
for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();)
for (List<ConVarQuery>::iterator iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();)
{
ConVarQuery &query = (*iter);
if (query.pCallback->GetParentRuntime() == pRuntime)
@ -238,6 +251,19 @@ void ConVarManager::OnClientDisconnected(int client)
ConVarQuery &query = (*iter);
if (query.client == client)
{
IPluginFunction *pCallback = query.pCallback;
if (pCallback)
{
cell_t ret;
pCallback->PushCell(query.cookie);
pCallback->PushCell(client);
pCallback->PushCell(eQueryCvarValueStatus_Cancelled);
pCallback->PushString("");
pCallback->PushString("");
pCallback->PushCell(query.value);
pCallback->Execute(&ret);
}
iter = m_ConVarQueries.erase(iter);
continue;
}
@ -330,6 +356,8 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
ConVarInfo *pInfo = NULL;
Handle_t hndl = 0;
IPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
/* Find out if the convar exists already */
pConVar = icvar->FindVar(name);
@ -337,11 +365,16 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
if (pConVar)
{
/* Add convar to plugin's list */
AddConVarToPluginList(pContext, pConVar);
AddConVarToPluginList(plugin, pConVar);
/* First find out if we already have a handle to it */
if (convar_cache_lookup(name, &pInfo))
{
/* If the convar doesn't have an owning plugin, but SM created it, adopt it */
if (pInfo->sourceMod && pInfo->pPlugin == nullptr) {
pInfo->pPlugin = plugin;
}
return pInfo->handle;
}
else
@ -382,6 +415,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->handle = hndl;
pInfo->sourceMod = true;
pInfo->pChangeForward = NULL;
pInfo->pPlugin = plugin;
/* Create a handle from the new convar */
hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
@ -398,7 +432,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->pVar = pConVar;
/* Add convar to plugin's list */
AddConVarToPluginList(pContext, pConVar);
AddConVarToPluginList(plugin, pConVar);
/* Insert struct into caches */
m_ConVars.push_back(pInfo);
@ -552,15 +586,13 @@ QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char
return cookie;
}
void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
void ConVarManager::AddConVarToPluginList(IPlugin *plugin, const ConVar *pConVar)
{
ConVarList *pConVarList;
ConVarList::iterator iter;
bool inserted = false;
const char *orig = pConVar->GetName();
IPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
/* Check plugin for an existing convar list */
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
{
@ -679,7 +711,7 @@ void ConVarManager::OnClientQueryFinished(QueryCvarCookie_t cookie,
}
#endif
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar, IPlugin **ppPlugin)
{
ConVarInfo *pInfo;
HandleError error;
@ -694,5 +726,10 @@ HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
*pVar = pInfo->pVar;
}
if (ppPlugin)
{
*ppPlugin = pInfo->pPlugin;
}
return error;
}

View File

@ -62,16 +62,27 @@ struct ConVarInfo
bool sourceMod; /**< Determines whether or not convar was created by a SourceMod plugin */
IChangeableForward *pChangeForward; /**< Forward associated with convar */
ConVar *pVar; /**< The actual convar */
IPlugin *pPlugin; /**< Originally owning plugin */
List<IConVarChangeListener *> changeListeners;
static inline bool matches(const char *name, const ConVarInfo *info)
struct ConVarPolicy
{
return strcmp(name, info->pVar->GetName()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
static inline bool matches(const char *name, ConVarInfo *info)
{
const char *conVarChars = info->pVar->GetName();
std::string convarName = ke::Lowercase(conVarChars);
std::string input = ke::Lowercase(name);
return convarName == input;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
std::string lower = ke::Lowercase(key.c_str());
return detail::CharsAndLength(lower.c_str()).hash();
}
};
};
/**
@ -144,7 +155,7 @@ public:
bool IsQueryingSupported();
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar);
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar, IPlugin **ppPlugin = nullptr);
// Called via game hooks.
void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
@ -161,7 +172,7 @@ private:
/**
* Adds a convar to a plugin's list.
*/
static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar);
static void AddConVarToPluginList(IPlugin *plugin, const ConVar *pConVar);
private:
HandleType_t m_ConVarType;
List<ConVarInfo *> m_ConVars;

View File

@ -307,7 +307,7 @@ bool ConsoleDetours::AddListener(IPluginFunction *fun, const char *command)
}
else
{
ke::AutoPtr<char[]> str(UTIL_ToLowerCase(command));
std::unique_ptr<char[]> str(UTIL_ToLowerCase(command));
IChangeableForward *forward;
if (!m_Listeners.retrieve(str.get(), &forward))
{
@ -329,7 +329,7 @@ bool ConsoleDetours::RemoveListener(IPluginFunction *fun, const char *command)
}
else
{
ke::AutoPtr<char[]> str(UTIL_ToLowerCase(command));
std::unique_ptr<char[]> str(UTIL_ToLowerCase(command));
IChangeableForward *forward;
if (!m_Listeners.retrieve(str.get(), &forward))
return false;

View File

@ -290,18 +290,18 @@ ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value,
pBase = pBase->m_pGlobalClassNext;
}
ke::AString vstr(value);
m_KeyValues.replace(option, ke::Move(vstr));
std::string vstr(value);
m_KeyValues.replace(option, std::move(vstr));
return result;
}
const char *CoreConfig::GetCoreConfigValue(const char *key)
{
StringHashMap<ke::AString>::Result r = m_KeyValues.find(key);
StringHashMap<std::string>::Result r = m_KeyValues.find(key);
if (!r.found())
return NULL;
return r->value.chars();
return r->value.c_str();
}
bool SM_AreConfigsExecuted()

View File

@ -68,7 +68,7 @@ private:
*/
ConfigResult SetConfigOption(const char *option, const char *value, ConfigSource, char *Error, size_t maxlength);
private:
StringHashMap<ke::AString> m_KeyValues;
StringHashMap<std::string> m_KeyValues;
};
extern bool SM_AreConfigsExecuted();

View File

@ -484,7 +484,7 @@ bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
pForward->PushCell(BAD_HANDLE);
}
pForward->PushString(pHook->name.chars());
pForward->PushString(pHook->name.c_str());
pForward->PushCell(bDontBroadcast);
pForward->Execute(NULL);
@ -505,7 +505,7 @@ bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
{
assert(pHook->pPostHook == NULL);
assert(pHook->pPreHook == NULL);
m_EventHooks.remove(pHook->name.chars());
m_EventHooks.remove(pHook->name.c_str());
delete pHook;
}
}

View File

@ -71,11 +71,11 @@ struct EventHook
IChangeableForward *pPostHook;
bool postCopy;
unsigned int refCount;
ke::AString name;
std::string name;
static inline bool matches(const char *name, const EventHook *hook)
{
return strcmp(name, hook->name.chars()) == 0;
return strcmp(name, hook->name.c_str()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{

View File

@ -91,7 +91,7 @@ void GameHooks::OnVSPReceived()
void GameHooks::Shutdown()
{
for (size_t i = 0; i < hooks_.length(); i++)
for (size_t i = 0; i < hooks_.size(); i++)
SH_REMOVE_HOOK_ID(hooks_[i]);
hooks_.clear();
@ -115,7 +115,7 @@ void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPla
const char *cvarName, const char *cvarValue){
int client = IndexOfEdict(pPlayer);
# if SOURCE_ENGINE == SE_CSGO
# if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue))
return;
# endif

View File

@ -63,7 +63,7 @@ class CommandHook : public ke::Refcounted<CommandHook>
{
public:
// return false to RETURN_META(MRES_IGNORED), or true to SUPERCEDE.
typedef ke::Lambda<bool(int, const ICommandArgs *)> Callback;
typedef ke::Function<bool(int, const ICommandArgs *)> Callback;
public:
CommandHook(ConCommand *cmd, const Callback &callback, bool post);
@ -114,11 +114,11 @@ private:
void SetCommandClient(int client);
private:
class HookList : public ke::Vector<int>
class HookList : public std::vector<int>
{
public:
HookList &operator += (int hook_id) {
this->append(hook_id);
this->push_back(hook_id);
return *this;
}
};

View File

@ -45,9 +45,12 @@
#if SOURCE_ENGINE == SE_CSGO
#include <cstrike15_usermessages.pb.h>
#elif SOURCE_ENGINE == SE_BLADE
#include <berimbau_usermessages.pb.h>
#elif SOURCE_ENGINE == SE_MCV
#include <vietnam_usermessages.pb.h>
#endif
typedef ICommandLine *(*FakeGetCommandLine)();
#define TIER0_NAME FORMAT_SOURCE_BIN_NAME("tier0")
@ -59,6 +62,7 @@ ConVar *sv_lan = NULL;
static void *g_EntList = NULL;
static void **g_pEntInfoList = NULL;
static int entInfoOffset = -1;
static int utlVecOffsetOffset = -1;
static CEntInfo *EntInfoArray()
{
@ -143,6 +147,7 @@ void CHalfLife2::OnSourceModAllInitialized_Post()
m_CSGOBadList.add("m_nActiveCoinRank");
m_CSGOBadList.add("m_nMusicID");
#endif
g_pGameConf->GetOffset("CSendPropExtra_UtlVector::m_Offset", &utlVecOffsetOffset);
}
ConfigResult CHalfLife2::OnSourceModConfigChanged(const char *key, const char *value,
@ -180,7 +185,9 @@ void CHalfLife2::InitLogicalEntData()
|| SOURCE_ENGINE == SE_CSS \
|| SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS \
|| SOURCE_ENGINE == SE_NUCLEARDAWN
|| SOURCE_ENGINE == SE_BLADE \
|| SOURCE_ENGINE == SE_NUCLEARDAWN \
|| SOURCE_ENGINE == SE_PVKII
if (g_SMAPI->GetServerFactory(false)("VSERVERTOOLS003", nullptr))
{
@ -317,23 +324,41 @@ bool UTIL_FindInSendTable(SendTable *pTable,
sm_sendprop_info_t *info,
unsigned int offset)
{
const char *pname;
int props = pTable->GetNumProps();
SendProp *prop;
for (int i=0; i<props; i++)
for (int i = 0; i < props; i++)
{
prop = pTable->GetProp(i);
pname = prop->GetName();
SendProp *prop = pTable->GetProp(i);
// Skip InsideArray props (SendPropArray / SendPropArray2),
// we'll find them later by their containing array.
if (prop->IsInsideArray()) {
continue;
}
const char *pname = prop->GetName();
SendTable *pInnerTable = prop->GetDataTable();
if (pname && strcmp(name, pname) == 0)
{
// get true offset of CUtlVector
if (utlVecOffsetOffset != -1 && prop->GetOffset() == 0 && pInnerTable && pInnerTable->GetNumProps())
{
SendProp *pLengthProxy = pInnerTable->GetProp(0);
const char *ipname = pLengthProxy->GetName();
if (ipname && strcmp(ipname, "lengthproxy") == 0 && pLengthProxy->GetExtraData())
{
info->prop = prop;
info->actual_offset = offset + *reinterpret_cast<size_t *>(reinterpret_cast<intptr_t>(pLengthProxy->GetExtraData()) + utlVecOffsetOffset);
return true;
}
}
info->prop = prop;
info->actual_offset = offset + info->prop->GetOffset();
return true;
}
if (prop->GetDataTable())
if (pInnerTable)
{
if (UTIL_FindInSendTable(prop->GetDataTable(),
if (UTIL_FindInSendTable(pInnerTable,
name,
info,
offset + prop->GetOffset())
@ -388,6 +413,17 @@ ServerClass *CHalfLife2::FindServerClass(const char *classname)
return pInfo->sc;
}
ServerClass *CHalfLife2::FindEntityServerClass(CBaseEntity *pEntity)
{
IServerNetworkable* pNetwork = ((IServerUnknown *)pEntity)->GetNetworkable();
if (pNetwork == nullptr)
{
return nullptr;
}
return pNetwork->GetServerClass();
}
DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
{
DataTableInfo *pInfo = NULL;
@ -420,20 +456,25 @@ bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_
return false;
}
if (!pInfo->lookup.retrieve(offset, info))
{
sm_sendprop_info_t temp_info;
DataTableInfo::SendPropInfo temp;
if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
if (!pInfo->lookup.retrieve(offset, &temp))
{
bool found = UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp.info, 0);
temp.name = offset;
pInfo->lookup.insert(offset, temp);
if (found)
{
return false;
*info = temp.info;
}
pInfo->lookup.insert(offset, temp_info);
*info = temp_info;
return found;
}
return true;
*info = temp.info;
return info->prop != nullptr;
}
SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
@ -467,15 +508,25 @@ bool CHalfLife2::FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatab
m_Maps.add(i, pMap, new DataMapCache());
DataMapCache *cache = i->value;
DataMapCacheInfo temp;
if (!cache->retrieve(offset, pDataTable))
if (!cache->retrieve(offset, &temp))
{
if (!UTIL_FindDataMapInfo(pMap, offset, pDataTable))
return false;
cache->insert(offset, *pDataTable);
bool found = UTIL_FindDataMapInfo(pMap, offset, &temp.info);
temp.name = offset;
cache->insert(offset, temp);
if (found)
{
*pDataTable = temp.info;
}
return found;
}
return true;
*pDataTable = temp.info;
return pDataTable->prop != nullptr;
}
void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
@ -516,7 +567,7 @@ bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
char buffer[253];
ke::SafeSprintf(buffer, sizeof(buffer), "%s\1\n", msg);
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
CCSUsrMsg_SayText *pMsg;
if ((pMsg = (CCSUsrMsg_SayText *)g_UserMsgs.StartProtobufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@ -543,7 +594,7 @@ bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
}
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
CCSUsrMsg_TextMsg *pMsg;
if ((pMsg = (CCSUsrMsg_TextMsg *)g_UserMsgs.StartProtobufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@ -580,7 +631,7 @@ bool CHalfLife2::HintTextMsg(int client, const char *msg)
{
cell_t players[] = {client};
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
CCSUsrMsg_HintText *pMsg;
if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@ -610,7 +661,7 @@ bool CHalfLife2::HintTextMsg(int client, const char *msg)
bool CHalfLife2::HintTextMsg(cell_t *players, int count, const char *msg)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
CCSUsrMsg_HintText *pMsg;
if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
{
@ -645,7 +696,7 @@ bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, boo
int count = 0;
cell_t players[] = {client};
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
CCSUsrMsg_VGUIMenu *pMsg;
if ((pMsg = (CCSUsrMsg_VGUIMenu *)g_UserMsgs.StartProtobufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
{
@ -670,7 +721,7 @@ bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, boo
SubKey = data->GetFirstSubKey();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
pMsg->set_name(name);
pMsg->set_show(show);
@ -1059,8 +1110,26 @@ int CHalfLife2::ReferenceToIndex(cell_t entRef)
return hndl.GetEntryIndex();
}
else
{
CEntInfo *pInfo = LookupEntity(entRef);
if (!pInfo)
{
return INVALID_EHANDLE_INDEX;
}
IServerUnknown *pUnk = static_cast<IServerUnknown *>(pInfo->m_pEntity);
if (!pUnk)
{
return INVALID_EHANDLE_INDEX;
}
CBaseEntity *pEntity = pUnk->GetBaseEntity();
if (!pEntity)
{
return INVALID_EHANDLE_INDEX;
}
return entRef;
return entRef;
}
}
cell_t CHalfLife2::EntityToBCompatRef(CBaseEntity *pEntity)
@ -1212,6 +1281,45 @@ bool IsWindowsReservedDeviceName(const char *pMapname)
}
#endif
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
// This frees memory allocated by the game using the game's CRT on Windows,
// avoiding a crash due to heap corruption (issue #910).
template< class T, class I >
class CUtlMemoryGlobalMalloc : public CUtlMemory< T, I >
{
typedef CUtlMemory< T, I > BaseClass;
public:
using BaseClass::BaseClass;
void Purge()
{
if (!IsExternallyAllocated())
{
if (m_pMemory)
{
UTLMEMORY_TRACK_FREE();
g_pMemAlloc->Free((void*)m_pMemory);
m_pMemory = 0;
}
m_nAllocationCount = 0;
}
BaseClass::Purge();
}
};
void CHalfLife2::FreeUtlVectorUtlString(CUtlVector<CUtlString, CUtlMemoryGlobalMalloc<CUtlString>> &vec)
{
CUtlMemoryGlobalMalloc<unsigned char> *pMemory;
FOR_EACH_VEC(vec, i)
{
pMemory = (CUtlMemoryGlobalMalloc<unsigned char> *) &vec[i].m_Storage.m_Memory;
pMemory->Purge();
vec[i].m_Storage.SetLength(0);
}
}
#endif
SMFindMapResult CHalfLife2::FindMap(const char *pMapName, char *pFoundMap, size_t nMapNameMax)
{
/* We need to ensure user input does not contain reserved device names on windows */
@ -1245,8 +1353,13 @@ SMFindMapResult CHalfLife2::FindMap(const char *pMapName, char *pFoundMap, size_
static size_t helperCmdLen = strlen(pHelperCmd->GetName());
#ifdef PLATFORM_WINDOWS
CUtlVector<CUtlString, CUtlMemoryGlobalMalloc<CUtlString>> results;
pHelperCmd->AutoCompleteSuggest(pMapName, *(CUtlVector<CUtlString, CUtlMemory<CUtlString>>*)&results);
#else
CUtlVector<CUtlString> results;
pHelperCmd->AutoCompleteSuggest(pMapName, results);
#endif
if (results.Count() == 0)
return SMFindMapResult::NotFound;
@ -1258,16 +1371,22 @@ SMFindMapResult CHalfLife2::FindMap(const char *pMapName, char *pFoundMap, size_
bool bExactMatch = Q_strcmp(pMapName, &results[0][helperCmdLen + 1]) == 0;
if (bExactMatch)
{
#ifdef PLATFORM_WINDOWS
FreeUtlVectorUtlString(results);
#endif
return SMFindMapResult::Found;
}
else
{
ke::SafeStrcpy(pFoundMap, nMapNameMax, &results[0][helperCmdLen + 1]);
#ifdef PLATFORM_WINDOWS
FreeUtlVectorUtlString(results);
#endif
return SMFindMapResult::FuzzyMatch;
}
#elif SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_HL2DM \
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_PVKII
static IVEngineServer *engine23 = (IVEngineServer *)(g_SMAPI->GetEngineFactory()("VEngineServer023", nullptr));
if (engine23)
{
@ -1313,11 +1432,39 @@ bool CHalfLife2::IsMapValid(const char *map)
if (!map || !map[0])
return false;
return FindMap(map) != SMFindMapResult::NotFound;
return FindMap(map) == SMFindMapResult::Found;
}
// TODO: Add ep1 support for this. (No IServerTools available there)
#if SOURCE_ENGINE >= SE_ORANGEBOX
#if SOURCE_ENGINE < SE_ORANGEBOX
class VKeyValuesSS_Helper {};
static bool VKeyValuesSS(CBaseEntity* pThisPtr, const char *pszKey, const char *pszValue, int offset)
{
void** this_ptr = *reinterpret_cast<void***>(&pThisPtr);
void** vtable = *reinterpret_cast<void***>(pThisPtr);
void* vfunc = vtable[offset];
union
{
bool (VKeyValuesSS_Helper::* mfpnew)(const char *, const char *);
#ifndef PLATFORM_POSIX
void* addr;
} u;
u.addr = vfunc;
#else
struct
{
void* addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = vfunc;
u.s.adjustor = 0;
#endif
return (bool)(reinterpret_cast<VKeyValuesSS_Helper*>(this_ptr)->*u.mfpnew)(pszKey, pszValue);
}
#endif
string_t CHalfLife2::AllocPooledString(const char *pszValue)
{
// This is admittedly a giant hack, but it's a relatively safe method for
@ -1327,28 +1474,58 @@ string_t CHalfLife2::AllocPooledString(const char *pszValue)
// current targetname string_t, set it to our string to insert via SetKeyValue,
// read back the new targetname value, restore the old value, and return the new one.
#if SOURCE_ENGINE < SE_ORANGEBOX
CBaseEntity* pEntity = nullptr;
for (int i = 0; i < gpGlobals->maxEntities; ++i)
{
pEntity = ReferenceToEntity(i);
if (pEntity)
{
break;
}
}
if (!pEntity)
{
logger->LogError("Failed to locate a valid entity for AllocPooledString.");
return NULL_STRING;
}
#else
CBaseEntity *pEntity = ((IServerUnknown *) servertools->FirstEntity())->GetBaseEntity();
#endif
auto *pDataMap = GetDataMap(pEntity);
assert(pDataMap);
static int offset = -1;
if (offset == -1)
static int iNameOffset = -1;
if (iNameOffset == -1)
{
sm_datatable_info_t info;
bool found = FindDataMapInfo(pDataMap, "m_iName", &info);
assert(found);
offset = info.actual_offset;
iNameOffset = info.actual_offset;
}
string_t *pProp = (string_t *) ((intp) pEntity + offset);
string_t* pProp = (string_t*)((intp)pEntity + iNameOffset);
string_t backup = *pProp;
#if SOURCE_ENGINE < SE_ORANGEBOX
static int iFuncOffset;
if (!g_pGameConf->GetOffset("DispatchKeyValue", &iFuncOffset) || !iFuncOffset)
{
logger->LogError("Failed to locate DispatchKeyValue in core gamedata. AllocPooledString unsupported.");
return NULL_STRING;
}
VKeyValuesSS(pEntity, "targetname", pszValue, iFuncOffset);
#else
servertools->SetKeyValue(pEntity, "targetname", pszValue);
#endif
string_t newString = *pProp;
*pProp = backup;
return newString;
}
#endif
bool CHalfLife2::GetServerSteam3Id(char *pszOut, size_t len) const
{
@ -1396,7 +1573,9 @@ uint64_t CHalfLife2::GetServerSteamId64() const
|| SOURCE_ENGINE == SE_DOI \
|| SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_ALIENSWARM \
|| SOURCE_ENGINE == SE_TF2
|| SOURCE_ENGINE == SE_TF2 \
|| SOURCE_ENGINE == SE_PVKII \
|| SOURCE_ENGINE == SE_MCV
const CSteamID *sid = engine->GetGameServerSteamID();
if (sid)
{

View File

@ -74,7 +74,7 @@ using namespace SourceMod;
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_INSURGENCY || SOURCE_ENGINE == SE_DOI
#define SOURCE_BIN_PREFIX "lib"
#define SOURCE_BIN_SUFFIX "_srv"
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
#elif SOURCE_ENGINE >= SE_LEFT4DEAD || SOURCE_ENGINE == SE_PVKII
#define SOURCE_BIN_PREFIX "lib"
#define SOURCE_BIN_SUFFIX ""
#else
@ -89,16 +89,24 @@ using namespace SourceMod;
struct DataTableInfo
{
struct SendPropPolicy
struct SendPropInfo
{
static inline bool matches(const char *name, const sm_sendprop_info_t &info)
static inline bool matches(const char *name, const SendPropInfo &info)
{
return strcmp(name, info.prop->GetName()) == 0;
return strcmp(name, info.name.c_str()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
SendPropInfo()
: name(), info{nullptr, 0}
{
}
std::string name;
sm_sendprop_info_t info;
};
static inline bool matches(const char *name, const DataTableInfo *info)
@ -116,22 +124,30 @@ struct DataTableInfo
}
ServerClass *sc;
NameHashSet<sm_sendprop_info_t, SendPropPolicy> lookup;
NameHashSet<SendPropInfo> lookup;
};
struct DataMapCachePolicy
struct DataMapCacheInfo
{
static inline bool matches(const char *name, const sm_datatable_info_t &info)
static inline bool matches(const char *name, const DataMapCacheInfo &info)
{
return strcmp(name, info.prop->fieldName) == 0;
return strcmp(name, info.name.c_str()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
DataMapCacheInfo()
: name(), info{nullptr, 0}
{
}
std::string name;
sm_datatable_info_t info;
};
typedef NameHashSet<sm_datatable_info_t, DataMapCachePolicy> DataMapCache;
typedef NameHashSet<DataMapCacheInfo> DataMapCache;
struct DelayedFakeCliCmd
{
@ -179,6 +195,12 @@ enum class SMFindMapResult : cell_t {
PossiblyAvailable
};
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS
template< class T, class I = int >
class CUtlMemoryGlobalMalloc;
class CUtlString;
#endif
class CHalfLife2 :
public SMGlobalClass,
public IGameHelpers
@ -199,6 +221,7 @@ public: //IGameHelpers
bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info);
datamap_t *GetDataMap(CBaseEntity *pEntity);
ServerClass *FindServerClass(const char *classname);
ServerClass *FindEntityServerClass(CBaseEntity *pEntity);
typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable);
void SetEdictStateChanged(edict_t *pEdict, unsigned short offset);
@ -229,10 +252,11 @@ public: //IGameHelpers
bool IsMapValid(const char *map);
SMFindMapResult FindMap(char *pMapName, size_t nMapNameMax);
SMFindMapResult FindMap(const char *pMapName, char *pFoundMap = NULL, size_t nMapNameMax = 0);
bool GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax);
#if SOURCE_ENGINE >= SE_ORANGEBOX
string_t AllocPooledString(const char *pszValue);
#if SOURCE_ENGINE >= SE_LEFT4DEAD && defined PLATFORM_WINDOWS && SOURCE_ENGINE != SE_MOCK
void FreeUtlVectorUtlString(CUtlVector<CUtlString, CUtlMemoryGlobalMalloc<CUtlString>> &vec);
#endif
bool GetMapDisplayName(const char *pMapName, char *pDisplayname, size_t nMapNameMax);
string_t AllocPooledString(const char *pszValue);
bool GetServerSteam3Id(char *pszOut, size_t len) const override;
uint64_t GetServerSteamId64() const override;
public:
@ -271,7 +295,7 @@ public:
return !m_bFollowCSGOServerGuidelines || !m_CSGOBadList.has(pszPropName);
}
private:
ke::HashSet<ke::AString, detail::StringHashMapPolicy> m_CSGOBadList;
ke::HashSet<std::string, detail::StringHashMapPolicy> m_CSGOBadList;
bool m_bFollowCSGOServerGuidelines = true;
#endif
};

View File

@ -38,6 +38,7 @@
#include "sourcemm_api.h"
#include "PlayerManager.h"
#include "MenuStyle_Valve.h"
#include <IGameConfigs.h>
#include "sourcemm_api.h"
#include "logic_bridge.h"
@ -68,6 +69,29 @@ void MenuManager::OnSourceModAllInitialized()
m_StyleType = handlesys->CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
}
void MenuManager::OnSourceModAllInitialized_Post()
{
const char* pTmp;
pTmp = g_pGameConf->GetKeyValue("MenuItemSound");
if (nullptr != pTmp)
{
m_SelectSound = pTmp;
}
pTmp = g_pGameConf->GetKeyValue("MenuExitSound");
if (nullptr != pTmp)
{
m_ExitSound = pTmp;
}
pTmp = g_pGameConf->GetKeyValue("MenuExitBackSound");
if (nullptr != pTmp)
{
m_ExitBackSound = pTmp;
}
}
void MenuManager::OnSourceModAllShutdown()
{
handlesys->RemoveType(m_MenuType, g_pCoreIdent);
@ -308,7 +332,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
{
ItemDrawInfo &dr = drawItems[foundItems].draw;
/* Is the item valid? */
if (menu->GetItemInfo(i, &dr) != NULL)
if (menu->GetItemInfo(i, &dr, client) != NULL)
{
/* Ask the user to change the style, if necessary */
mh->OnMenuDrawItem(menu, client, i, dr.style);
@ -398,7 +422,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
}
while (++lastItem < totalItems)
{
if (menu->GetItemInfo(lastItem, &dr) != NULL)
if (menu->GetItemInfo(lastItem, &dr, client) != NULL)
{
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style))
@ -420,7 +444,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
lastItem--;
while (lastItem != 0)
{
if (menu->GetItemInfo(lastItem, &dr) != NULL)
if (menu->GetItemInfo(lastItem, &dr, client) != NULL)
{
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style))
@ -699,30 +723,9 @@ bool MenuManager::MenuSoundsEnabled()
return (sm_menu_sounds.GetInt() != 0);
}
ConfigResult MenuManager::OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
char *error,
size_t maxlength)
std::string *MenuManager::GetMenuSound(ItemSelection sel)
{
if (strcmp(key, "MenuItemSound") == 0)
{
m_SelectSound.assign(value);
return ConfigResult_Accept;
} else if (strcmp(key, "MenuExitBackSound") == 0) {
m_ExitBackSound.assign(value);
return ConfigResult_Accept;
} else if (strcmp(key, "MenuExitSound") == 0) {
m_ExitSound.assign(value);
return ConfigResult_Accept;
}
return ConfigResult_Ignore;
}
const char *MenuManager::GetMenuSound(ItemSelection sel)
{
const char *sound = NULL;
std::string *sound = nullptr;
switch (sel)
{
@ -732,7 +735,7 @@ const char *MenuManager::GetMenuSound(ItemSelection sel)
{
if (m_SelectSound.size() > 0)
{
sound = m_SelectSound.c_str();
sound = &m_SelectSound;
}
break;
}
@ -740,7 +743,7 @@ const char *MenuManager::GetMenuSound(ItemSelection sel)
{
if (m_ExitBackSound.size() > 0)
{
sound = m_ExitBackSound.c_str();
sound = &m_ExitBackSound;
}
break;
}
@ -748,7 +751,7 @@ const char *MenuManager::GetMenuSound(ItemSelection sel)
{
if (m_ExitSound.size() > 0)
{
sound = m_ExitSound.c_str();
sound = &m_ExitSound;
}
break;
}

View File

@ -36,8 +36,8 @@
#include <sh_vector.h>
#include <sh_stack.h>
#include <sh_list.h>
#include <sh_string.h>
#include "sm_globals.h"
#include <string>
using namespace SourceMod;
using namespace SourceHook;
@ -55,12 +55,8 @@ public:
MenuManager();
public: //SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModAllInitialized_Post();
void OnSourceModAllShutdown();
ConfigResult OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
char *error,
size_t maxlength);
void OnSourceModLevelChange(const char *mapName);
public: //IMenuManager
virtual const char *GetInterfaceName()
@ -99,7 +95,7 @@ public:
HandleError ReadStyleHandle(Handle_t handle, IMenuStyle **style);
public:
bool MenuSoundsEnabled();
const char *GetMenuSound(ItemSelection sel);
std::string *GetMenuSound(ItemSelection sel);
protected:
Handle_t CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner);
Handle_t CreateStyleHandle(IMenuStyle *style);
@ -109,9 +105,9 @@ private:
CVector<IMenuStyle *> m_Styles;
HandleType_t m_StyleType;
HandleType_t m_MenuType;
String m_SelectSound;
String m_ExitBackSound;
String m_ExitSound;
std::string m_SelectSound = "";
std::string m_ExitBackSound = "";
std::string m_ExitSound = "";
};
extern MenuManager g_Menus;

View File

@ -8,7 +8,7 @@
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@ -36,7 +36,7 @@
#include "MenuManager.h"
#include "CellRecipientFilter.h"
#if defined MENU_DEBUG
#include "Logger.h"
#include <bridge/include/ILogger.h>
#endif
#include "logic_bridge.h"
#include "AutoHandleRooter.h"
@ -59,7 +59,7 @@ Handle_t BaseMenuStyle::GetHandle()
void BaseMenuStyle::AddClientToWatch(int client)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
logger->LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
#endif
m_WatchList.push_back(client);
}
@ -67,7 +67,7 @@ void BaseMenuStyle::AddClientToWatch(int client)
void BaseMenuStyle::RemoveClientFromWatch(int client)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
logger->LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
#endif
m_WatchList.remove(client);
}
@ -75,7 +75,7 @@ void BaseMenuStyle::RemoveClientFromWatch(int client)
void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore/* =false */)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
logger->LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
#endif
CBaseMenuPlayer *player = GetMenuPlayer(client);
menu_states_t &states = player->states;
@ -99,7 +99,7 @@ void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool
/* Fire callbacks */
mh->OnMenuCancel(menu, client, reason);
/* Only fire end if there's a valid menu */
if (menu)
{
@ -115,7 +115,7 @@ void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool
void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
logger->LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
#endif
int maxClients = g_Players.GetMaxClients();
for (int i=1; i<=maxClients; i++)
@ -135,7 +135,7 @@ void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
logger->LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
#endif
if (client < 1 || client > g_Players.MaxClients())
{
@ -191,7 +191,7 @@ MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
void BaseMenuStyle::OnClientDisconnected(int client)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
logger->LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
#endif
CBaseMenuPlayer *player = GetMenuPlayer(client);
if (!player->bInMenu)
@ -214,7 +214,7 @@ void BaseMenuStyle::ProcessWatchList()
}
#if defined MENU_DEBUG
g_Logger.LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)",
logger->LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)",
m_WatchList.m_Size,
m_WatchList.m_FirstLink,
m_WatchList.m_FreeNodes,
@ -232,7 +232,7 @@ void BaseMenuStyle::ProcessWatchList()
#if defined MENU_DEBUG
if (total)
{
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
logger->LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
}
#endif
@ -244,7 +244,7 @@ void BaseMenuStyle::ProcessWatchList()
client = do_lookup[i];
player = GetMenuPlayer(client);
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
logger->LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
client,
player->bInMenu,
player->menuHoldTime,
@ -254,7 +254,7 @@ void BaseMenuStyle::ProcessWatchList()
if (!player->bInMenu || !player->menuHoldTime)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
logger->LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
#endif
m_WatchList.remove(client);
continue;
@ -269,7 +269,7 @@ void BaseMenuStyle::ProcessWatchList()
void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
logger->LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
#endif
CBaseMenuPlayer *player = GetMenuPlayer(client);
@ -302,7 +302,7 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
ItemSelection type = states.slots[key_press].type;
/* Check if we should play a sound about the type */
if (g_Menus.MenuSoundsEnabled() &&
if (g_Menus.MenuSoundsEnabled() &&
(!menu || (menu->GetMenuOptionFlags() & MENUFLAG_NO_SOUND) != MENUFLAG_NO_SOUND))
{
CellRecipientFilter filter;
@ -311,9 +311,9 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
clients[0] = client;
filter.Initialize(clients, 1);
const char *sound = g_Menus.GetMenuSound(type);
std::string *sound = g_Menus.GetMenuSound(type);
if (sound != NULL)
if (nullptr != sound && !sound->empty())
{
edict_t *pEdict = PEntityOfEntIndex(client);
if (pEdict)
@ -323,23 +323,23 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
if (pCollideable)
{
const Vector & pos = pCollideable->GetCollisionOrigin();
enginesound->EmitSound(filter,
client,
CHAN_AUTO,
enginesound->EmitSound(filter,
client,
CHAN_AUTO,
#if SOURCE_ENGINE >= SE_PORTAL2
sound,
-1,
sound->c_str(),
-1,
#endif
sound,
VOL_NORM,
ATTN_NORM,
sound->c_str(),
VOL_NORM,
ATTN_NORM,
#if SOURCE_ENGINE >= SE_PORTAL2
0,
0,
#endif
0,
PITCH_NORM,
0,
PITCH_NORM,
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS \
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_TF2
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_PVKII
0,
#endif
&pos);
@ -412,7 +412,7 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
logger->LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
client,
menu,
mh,
@ -475,7 +475,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
unsigned int time)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
logger->LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
client,
menu,
mh,
@ -487,7 +487,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
logger->LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
#endif
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
@ -498,7 +498,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
if (player->bAutoIgnore)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
logger->LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
#endif
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
@ -517,7 +517,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
if (player->bInMenu)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
logger->LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
#endif
_CancelClientMenu(client, MenuCancel_Interrupted, true);
}
@ -532,7 +532,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
if (!display)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
logger->LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
#endif
player->bAutoIgnore = false;
player->bInMenu = false;
@ -562,7 +562,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
player->bAutoIgnore = false;
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
logger->LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
#endif
return true;
@ -571,7 +571,7 @@ bool BaseMenuStyle::DoClientMenu(int client,
bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
logger->LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
#endif
CBaseMenuPlayer *player = GetMenuPlayer(client);
menu_states_t &states = player->states;
@ -581,7 +581,7 @@ bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
if (!display)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
logger->LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
#endif
if (player->menuHoldTime)
{
@ -598,15 +598,15 @@ bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
player->bAutoIgnore = false;
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
logger->LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
#endif
return true;
}
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
m_pStyle(pStyle), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false),
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false),
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
m_pStyle(pStyle), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false),
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false),
m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
{
}
@ -628,49 +628,49 @@ Handle_t CBaseMenu::GetHandle()
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
{
if (m_Pagination == (unsigned)MENU_NO_PAGINATION
&& m_items.length() >= m_pStyle->GetMaxPageItems())
&& m_items.size() >= m_pStyle->GetMaxPageItems())
{
return false;
}
CItem item;
CItem item(m_items.size());
item.info = info;
if (draw.display)
item.display = new ke::AString(draw.display);
item.display = std::make_unique<std::string>(draw.display);
item.style = draw.style;
m_items.append(ke::Move(item));
m_items.push_back(std::move(item));
return true;
}
bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
{
if (m_Pagination == (unsigned)MENU_NO_PAGINATION &&
m_items.length() >= m_pStyle->GetMaxPageItems())
m_items.size() >= m_pStyle->GetMaxPageItems())
{
return false;
}
if (position >= m_items.length())
if (position >= m_items.size())
return false;
CItem item;
CItem item(position);
item.info = info;
if (draw.display)
item.display = new ke::AString(draw.display);
item.display = std::make_unique<std::string>(draw.display);
item.style = draw.style;
m_items.insert(position, ke::Move(item));
m_items.emplace(m_items.begin() + position, std::move(item));
return true;
}
bool CBaseMenu::RemoveItem(unsigned int position)
{
if (position >= m_items.length())
if (position >= m_items.size())
return false;
m_items.remove(position);
m_items.erase(m_items.begin() + position);
return true;
}
@ -679,23 +679,140 @@ void CBaseMenu::RemoveAllItems()
m_items.clear();
}
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */, int client/* =0 */)
{
if (position >= m_items.length())
if (position >= m_items.size())
return NULL;
if (client > 0 && position < m_RandomMaps[client].size())
{
position = m_RandomMaps[client][position];
}
if (draw)
{
draw->display = m_items[position].display->chars();
draw->display = m_items[position].display->c_str();
draw->style = m_items[position].style;
}
return m_items[position].info.chars();
return m_items[position].info.c_str();
}
void CBaseMenu::ShufflePerClient(int start, int stop)
{
// limit map len to 255 items since it's using uint8
int length = MIN(GetItemCount(), 255);
if (stop >= 0)
length = MIN(length, stop);
for (int i = 1; i <= SM_MAXPLAYERS; i++)
{
// populate per-client map ...
m_RandomMaps[i].resize(length);
for (int j = 0; j < length; j++)
m_RandomMaps[i][j] = j;
// ... and random shuffle it
for (int j = length - 1; j > start; j--)
{
int x = rand() % (j - start + 1) + start;
uint8_t tmp = m_RandomMaps[i][x];
m_RandomMaps[i][x] = m_RandomMaps[i][j];
m_RandomMaps[i][j] = tmp;
}
}
}
void CBaseMenu::SetClientMapping(int client, int *array, int length)
{
length = MIN(length, 255);
m_RandomMaps[client].resize(length);
for (int i = 0; i < length; i++)
{
m_RandomMaps[client][i] = array[i];
}
}
bool CBaseMenu::IsPerClientShuffled()
{
for (int i = 1; i <= SM_MAXPLAYERS; i++)
{
if(m_RandomMaps[i].size() > 0)
return true;
}
return false;
}
unsigned int CBaseMenu::GetRealItemIndex(int client, unsigned int position)
{
if (client > 0 && position < m_RandomMaps[client].size())
{
position = m_RandomMaps[client][position];
return m_items[position].index;
}
return position;
}
void CBaseMenu::ShufflePerClient(int start, int stop)
{
// limit map len to 255 items since it's using uint8
int length = MIN(GetItemCount(), 255);
if (stop >= 0)
length = MIN(length, stop);
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
// populate per-client map ...
m_RandomMaps[i].resize(length);
for (int j = 0; j < length; j++)
m_RandomMaps[i][j] = j;
// ... and random shuffle it
for (int j = length - 1; j > start; j--)
{
int x = rand() % (j - start + 1) + start;
uint8_t tmp = m_RandomMaps[i][x];
m_RandomMaps[i][x] = m_RandomMaps[i][j];
m_RandomMaps[i][j] = tmp;
}
}
}
void CBaseMenu::SetClientMapping(int client, int *array, int length)
{
length = MIN(length, 255);
m_RandomMaps[client].resize(length);
for (int i = 0; i < length; i++)
{
m_RandomMaps[client][i] = array[i];
}
}
bool CBaseMenu::IsPerClientShuffled()
{
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
if(m_RandomMaps[i].length() > 0)
return true;
}
return false;
}
unsigned int CBaseMenu::GetRealItemIndex(int client, unsigned int position)
{
if (client > 0 && position < m_RandomMaps[client].length())
{
position = m_RandomMaps[client][position];
return m_items[position].index;
}
return position;
}
unsigned int CBaseMenu::GetItemCount()
{
return m_items.length();
return m_items.size();
}
bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
@ -733,7 +850,7 @@ void CBaseMenu::SetDefaultTitle(const char *message)
const char *CBaseMenu::GetDefaultTitle()
{
return m_Title.chars();
return m_Title.c_str();
}
void CBaseMenu::Cancel()
@ -744,7 +861,7 @@ void CBaseMenu::Cancel()
}
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
logger->LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
this,
m_bShouldDelete);
#endif
@ -768,7 +885,7 @@ void CBaseMenu::Destroy(bool releaseHandle)
}
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
logger->LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
this,
releaseHandle,
m_bCancelling,
@ -825,5 +942,5 @@ IMenuHandler *CBaseMenu::GetHandler()
unsigned int CBaseMenu::GetBaseMemUsage()
{
return m_Title.length() + (m_items.length() * sizeof(CItem));
return m_Title.size() + (m_items.size() * sizeof(CItem));
}

View File

@ -8,7 +8,7 @@
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@ -32,9 +32,11 @@
#ifndef _INCLUDE_MENUSTYLE_BASE_H
#define _INCLUDE_MENUSTYLE_BASE_H
#include <memory>
#include <utility>
#include <IMenuManager.h>
#include <IPlayerHelpers.h>
#include <am-autoptr.h>
#include <am-string.h>
#include <am-vector.h>
#include "sm_fastlink.h"
@ -44,30 +46,34 @@ using namespace SourceMod;
class CItem
{
public:
CItem()
CItem(unsigned int index)
{
this->index = index;
style = 0;
access = 0;
}
CItem(CItem &&other)
: info(ke::Move(other.info)),
display(ke::Move(other.display))
: info(std::move(other.info)),
display(std::move(other.display))
{
index = other.index;
style = other.style;
access = other.access;
}
CItem & operator =(CItem &&other)
{
info = ke::Move(other.info);
display = ke::Move(other.display);
index = other.index;
info = std::move(other.info);
display = std::move(other.display);
style = other.style;
access = other.access;
return *this;
}
public:
ke::AString info;
ke::AutoPtr<ke::AString> display;
unsigned int index;
std::string info;
std::unique_ptr<std::string> display;
unsigned int style;
unsigned int access;
@ -92,7 +98,7 @@ public:
class CBaseMenu;
class BaseMenuStyle :
class BaseMenuStyle :
public IMenuStyle,
public IClientListener
{
@ -107,11 +113,11 @@ public: //IClientListener
public: //what derived must implement
virtual CBaseMenuPlayer *GetMenuPlayer(int client) =0;
virtual void SendDisplay(int client, IMenuPanel *display) =0;
public: //what derived may implement
virtual bool DoClientMenu(int client,
CBaseMenu *menu,
public: //what derived may implement
virtual bool DoClientMenu(int client,
CBaseMenu *menu,
unsigned int first_item,
IMenuHandler *mh,
IMenuHandler *mh,
unsigned int time);
virtual bool DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time);
virtual void AddClientToWatch(int client);
@ -138,7 +144,7 @@ public:
virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw);
virtual bool RemoveItem(unsigned int position);
virtual void RemoveAllItems();
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL);
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL, int client=0);
virtual unsigned int GetItemCount();
virtual bool SetPagination(unsigned int itemsPerPage);
virtual unsigned int GetPagination();
@ -152,14 +158,18 @@ public:
virtual unsigned int GetMenuOptionFlags();
virtual void SetMenuOptionFlags(unsigned int flags);
virtual IMenuHandler *GetHandler();
virtual void ShufflePerClient(int start, int stop);
virtual void SetClientMapping(int client, int *array, int length);
virtual bool IsPerClientShuffled();
virtual unsigned int GetRealItemIndex(int client, unsigned int position);
unsigned int GetBaseMemUsage();
private:
void InternalDelete();
protected:
ke::AString m_Title;
std::string m_Title;
IMenuStyle *m_pStyle;
unsigned int m_Pagination;
ke::Vector<CItem> m_items;
std::vector<CItem> m_items;
bool m_bShouldDelete;
bool m_bCancelling;
IdentityToken_t *m_pOwner;
@ -168,6 +178,7 @@ protected:
Handle_t m_hHandle;
IMenuHandler *m_pHandler;
unsigned int m_nFlags;
std::vector<uint8_t> m_RandomMaps[SM_MAXPLAYERS+1];
};
#endif //_INCLUDE_MENUSTYLE_BASE_H

View File

@ -35,7 +35,7 @@
#include <IGameConfigs.h>
#include "PlayerManager.h"
#if defined MENU_DEBUG
#include "Logger.h"
#include <bridge/include/ILogger.h>
#endif
#include "logic_bridge.h"
@ -45,6 +45,10 @@
#if SOURCE_ENGINE == SE_CSGO
#include <game/shared/csgo/protobuf/cstrike15_usermessages.pb.h>
#elif SOURCE_ENGINE == SE_BLADE
#include <game/shared/berimbau/protobuf/berimbau_usermessages.pb.h>
#elif SOURCE_ENGINE == SE_MCV
#include <game/shared/vietnam/protobuf/vietnam_usermessages.pb.h>
#endif
extern const char *g_RadioNumTable[];
@ -59,7 +63,7 @@ unsigned int g_RadioMenuTimeout = 0;
#define MAX_MENUSLOT_KEYS 10
static unsigned int s_RadioMaxPageItems = MAX_MENUSLOT_KEYS;
static bool s_RadioClosesOnInvalidSlot = false;
CRadioStyle::CRadioStyle()
{
@ -122,6 +126,12 @@ void CRadioStyle::OnSourceModLevelChange(const char *mapName)
}
}
const char *closes = g_pGameConf->GetKeyValue("RadioMenuClosesOnInvalidSlot");
if (closes != nullptr && strcmp(closes, "yes") == 0)
{
s_RadioClosesOnInvalidSlot = true;
}
g_Menus.SetDefaultStyle(this);
g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
@ -174,7 +184,7 @@ void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFil
{
int count = pFilter->GetRecipientCount();
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
int c = ((CCSUsrMsg_ShowMenu &)msg).display_time();
#else
bf_read br(bf->GetBasePointer(), 3);
@ -197,7 +207,7 @@ void CRadioStyle::OnUserMessageSent(int msg_id)
{
int client = g_last_clients[i];
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
logger->LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
client,
m_players[client].bInExternMenu);
#endif
@ -462,7 +472,16 @@ void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
sizeof(display_pkt),
text);
}
display_keys = keys;
// Some games have implemented CHudMenu::SelectMenuItem to close the menu
// even if an invalid slot has been selected, which causes us a problem as
// we'll never get any notification from the client and we'll keep the menu
// alive on our end indefinitely. For these games, pretend that every slot
// is valid for selection so we're guaranteed to get a menuselect command.
// We don't want to do this for every game as the common SelectMenuItem
// implementation ignores invalid selections and keeps the menu open, which
// is a much nicer user experience.
display_keys = s_RadioClosesOnInvalidSlot ? 0x7ff : keys;
}
void CRadioMenuPlayer::Radio_Refresh()
@ -483,9 +502,13 @@ void CRadioMenuPlayer::Radio_Refresh()
time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
// TODO: find what happens past 240 on CS:GO
CCSUsrMsg_ShowMenu *msg = (CCSUsrMsg_ShowMenu *)g_UserMsgs.StartProtobufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
if (!msg)
{
return;
}
msg->set_bits_valid_slots(display_keys);
msg->set_display_time(time);
msg->set_menu_string(ptr);
@ -576,7 +599,7 @@ bool CRadioMenu::DisplayAtItem(int client,
IMenuHandler *alt_handler)
{
#if defined MENU_DEBUG
g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
logger->LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
this,
client,
time);

View File

@ -40,6 +40,7 @@
#include "UserMessages.h"
#include "sm_fastlink.h"
#include <sh_stack.h>
#include <sh_string.h>
#include <compat_wrappers.h>
#include "logic/common_logic.h"
#include "AutoHandleRooter.h"

View File

@ -514,15 +514,16 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite
/* Check by our item count, NOT the vote array size */
if (item < m_Items)
{
m_ClientVotes[client] = item;
m_Votes[item]++;
unsigned int index = menu->GetRealItemIndex(client, item);
m_ClientVotes[client] = index;
m_Votes[index]++;
m_NumVotes++;
if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool())
{
static char buffer[1024];
ItemDrawInfo dr;
menu->GetItemInfo(item, &dr);
menu->GetItemInfo(item, &dr, client);
if (sm_vote_console.GetBool())
{

View File

@ -63,11 +63,7 @@ bool g_forcedChange = false;
void NextMapManager::OnSourceModAllInitialized_Post()
{
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#else
SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#endif
ConCommand *pCmd = FindCommand("changelevel");
if (pCmd != NULL)
@ -79,11 +75,7 @@ void NextMapManager::OnSourceModAllInitialized_Post()
void NextMapManager::OnSourceModShutdown()
{
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#else
SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#endif
if (changeLevelCmd != NULL)
{
@ -117,6 +109,8 @@ bool NextMapManager::SetNextMap(const char *map)
return true;
}
static char g_nextMap[PLATFORM_MAX_PATH];
#if SOURCE_ENGINE != SE_DARKMESSIAH
void NextMapManager::HookChangeLevel(const char *map, const char *unknown)
#else
@ -130,8 +124,16 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const
}
const char *newmap = sm_nextmap.GetString();
if (newmap[0] != '\0') {
ke::SafeStrcpy(g_nextMap, sizeof(g_nextMap), newmap);
newmap = g_nextMap;
if (newmap[0] == 0 || !g_HL2.IsMapValid(newmap))
// Clear the value so that if the map load fails later we don't get stuck in a loop.
// This might cause us to go off-cycle for a map, but nextmap will get us back on track.
sm_nextmap.SetValue("");
}
if (newmap[0] == '\0' || !g_HL2.IsMapValid(newmap))
{
RETURN_META(MRES_IGNORED);
}
@ -150,6 +152,15 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const
void NextMapManager::OnSourceModLevelChange( const char *mapName )
{
// If we were controlling the map change, reset sm_nextmap to be the name of the map we successfully changed to.
// This maintains an old API contract on the plugin side. We use the real map name even if it was different from
// the expected map name as if the expected map failed to load we let the game take over instead, but the nextmap
// plugin compares the sm_nextmap value to the current map to decide if it should advance the mapcycle.
if (g_nextMap[0] != '\0') {
sm_nextmap.SetValue(mapName);
g_nextMap[0] = '\0';
}
/* Skip the first 'mapchange' when the server starts up */
if (m_tempChangeInfo.startTime != 0)
{

View File

@ -96,7 +96,7 @@ public:
private:
MapChangeData m_tempChangeInfo;
char lastMap[32];
char lastMap[PLATFORM_MAX_PATH];
};
extern NextMapManager g_NextMap;

View File

@ -30,6 +30,7 @@
*/
#include "PlayerManager.h"
#include "sourcemod.h"
#include "IAdminSystem.h"
#include "ConCmdManager.h"
#include "MenuStyle_Valve.h"
@ -58,6 +59,8 @@ bool g_OnMapStarted = false;
IForward *PreAdminCheck = NULL;
IForward *PostAdminCheck = NULL;
IForward *PostAdminFilter = NULL;
IForward *ServerEnterHibernation = NULL;
IForward *ServerExitHibernation = NULL;
const unsigned int *g_NumPlayersToAuth = NULL;
int lifestate_offset = -1;
@ -92,6 +95,12 @@ SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &)
#else
SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
#endif
SH_DECL_HOOK2_void(IVEngineServer, ClientPrintf, SH_NOATTRIB, 0, edict_t *, const char *);
static void PrintfBuffer_FrameAction(void *data)
{
g_Players.OnPrintfFrameAction(static_cast<unsigned int>(reinterpret_cast<uintptr_t>(data)));
}
ConCommand *maxplayersCmd = NULL;
@ -172,6 +181,7 @@ void PlayerManager::OnSourceModAllInitialized()
#elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
SH_ADD_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true);
#endif
SH_ADD_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false);
sharesys->AddInterface(NULL, this);
@ -188,12 +198,15 @@ void PlayerManager::OnSourceModAllInitialized()
m_clcommandkv_post = forwardsys->CreateForward("OnClientCommandKeyValues_Post", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
m_clinfochanged = forwardsys->CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2);
m_clauth = forwardsys->CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String);
m_cllang = forwardsys->CreateForward("OnClientLanguageChanged", ET_Ignore, 2, NULL, Param_Cell, Param_Cell);
m_onActivate = forwardsys->CreateForward("OnServerLoad", ET_Ignore, 0, NULL);
m_onActivate2 = forwardsys->CreateForward("OnMapStart", ET_Ignore, 0, NULL);
PreAdminCheck = forwardsys->CreateForward("OnClientPreAdminCheck", ET_Event, 1, p1);
PostAdminCheck = forwardsys->CreateForward("OnClientPostAdminCheck", ET_Ignore, 1, p1);
PostAdminFilter = forwardsys->CreateForward("OnClientPostAdminFilter", ET_Ignore, 1, p1);
ServerEnterHibernation = forwardsys->CreateForward("OnServerEnterHibernation", ET_Ignore, 0, NULL);
ServerExitHibernation = forwardsys->CreateForward("OnServerExitHibernation", ET_Ignore, 0, NULL);
m_bIsListenServer = !engine->IsDedicatedServer();
m_ListenClient = 0;
@ -204,6 +217,9 @@ void PlayerManager::OnSourceModAllInitialized()
SH_ADD_HOOK(ConCommand, Dispatch, pCmd, SH_STATIC(CmdMaxplayersCallback), true);
maxplayersCmd = pCmd;
}
gameevents->AddListener(this, "player_connect", true);
gameevents->AddListener(this, "player_disconnect", true);
}
void PlayerManager::OnSourceModShutdown()
@ -225,6 +241,7 @@ void PlayerManager::OnSourceModShutdown()
#elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
SH_REMOVE_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true);
#endif
SH_REMOVE_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false);
/* Release forwards */
forwardsys->ReleaseForward(m_clconnect);
@ -237,12 +254,15 @@ void PlayerManager::OnSourceModShutdown()
forwardsys->ReleaseForward(m_clcommandkv_post);
forwardsys->ReleaseForward(m_clinfochanged);
forwardsys->ReleaseForward(m_clauth);
forwardsys->ReleaseForward(m_cllang);
forwardsys->ReleaseForward(m_onActivate);
forwardsys->ReleaseForward(m_onActivate2);
forwardsys->ReleaseForward(PreAdminCheck);
forwardsys->ReleaseForward(PostAdminCheck);
forwardsys->ReleaseForward(PostAdminFilter);
forwardsys->ReleaseForward(ServerEnterHibernation);
forwardsys->ReleaseForward(ServerExitHibernation);
delete [] m_Players;
@ -250,6 +270,8 @@ void PlayerManager::OnSourceModShutdown()
{
SH_REMOVE_HOOK(ConCommand, Dispatch, maxplayersCmd, SH_STATIC(CmdMaxplayersCallback), true);
}
gameevents->RemoveListener(this);
}
ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
@ -396,7 +418,7 @@ void PlayerManager::RunAuthChecks()
pPlayer = &m_Players[m_AuthQueue[i]];
pPlayer->UpdateAuthIds();
authstr = pPlayer->m_AuthID.chars();
authstr = pPlayer->m_AuthID.c_str();
if (!pPlayer->IsAuthStringValidated())
{
@ -503,7 +525,7 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
/* Get the client's language */
if (m_QueryLang)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
pPlayer->m_LangId = translator->GetServerLanguage();
#else
const char *name;
@ -511,10 +533,13 @@ bool PlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, const
{
unsigned int langid;
pPlayer->m_LangId = (translator->GetLanguageByName(name, &langid)) ? langid : translator->GetServerLanguage();
OnClientLanguageChanged(client, pPlayer->m_LangId);
} else {
pPlayer->m_LangId = translator->GetServerLanguage();
}
#endif
pPlayer->m_OriginalLangId = pPlayer->m_LangId;
}
List<IClientListener *>::iterator iter;
@ -632,7 +657,7 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
int userId = engine->GetPlayerUserId(pEntity);
#if (SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_LEFT4DEAD2)
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_PVKII)
static ConVar *tv_name = icvar->FindVar("tv_name");
#endif
#if SOURCE_ENGINE == SE_TF2
@ -655,10 +680,13 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
&& ((!m_bIsReplayActive && newCount == 1)
|| (m_bIsReplayActive && newCount == 2))
&& (m_SourceTVUserId == userId
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_MCV
// It seems likely that MCV will change this at some point, but it's GOTV at the moment.
|| strcmp(playername, "GOTV") == 0
#elif SOURCE_ENGINE == SE_BLADE
|| strcmp(playername, "BBTV") == 0
#elif (SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_LEFT4DEAD2)
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_PVKII)
|| (tv_name && strcmp(playername, tv_name->GetString()) == 0) || (tv_name && tv_name->GetString()[0] == 0 && strcmp(playername, "unnamed") == 0)
#else
|| strcmp(playername, "SourceTV") == 0
@ -700,19 +728,19 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
for (iter=m_hooks.begin(); iter!=m_hooks.end(); iter++)
{
pListener = (*iter);
pListener->OnClientAuthorized(client, steamId ? steamId : pPlayer->m_AuthID.chars());
pListener->OnClientAuthorized(client, steamId ? steamId : pPlayer->m_AuthID.c_str());
}
/* Finally, tell plugins */
if (m_clauth->GetFunctionCount())
{
m_clauth->PushCell(client);
/* For legacy reasons, people are expecting the Steam2 id here if using Steam auth */
m_clauth->PushString(steamId ? steamId : pPlayer->m_AuthID.chars());
m_clauth->PushString(steamId ? steamId : pPlayer->m_AuthID.c_str());
m_clauth->Execute(NULL);
}
pPlayer->Authorize_Post();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
else if(m_QueryLang)
{
// Not a bot
@ -761,6 +789,11 @@ void PlayerManager::OnSourceModLevelEnd()
void PlayerManager::OnServerHibernationUpdate(bool bHibernating)
{
cell_t res;
if (bHibernating)
ServerEnterHibernation->Execute(&res);
else
ServerExitHibernation->Execute(&res);
/* If bots were added at map start, but not fully inited before hibernation, there will
* be no OnClientDisconnect for them, despite them getting booted right before this.
*/
@ -772,7 +805,7 @@ void PlayerManager::OnServerHibernationUpdate(bool bHibernating)
CPlayer *pPlayer = &m_Players[i];
if (pPlayer->IsConnected() && pPlayer->IsFakeClient())
{
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE >= SE_CSGO || SOURCE_ENGINE == SE_NUCLEARDAWN
#if SOURCE_ENGINE < SE_LEFT4DEAD || SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_NUCLEARDAWN || SOURCE_ENGINE == SE_MCV
// These games have the bug fixed where hltv/replay was getting kicked on hibernation
if (pPlayer->IsSourceTV() || pPlayer->IsReplay())
continue;
@ -846,6 +879,88 @@ void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity)
}
}
void PlayerManager::OnClientPrintf(edict_t *pEdict, const char *szMsg)
{
int client = IndexOfEdict(pEdict);
CPlayer &player = m_Players[client];
if (!player.IsConnected())
RETURN_META(MRES_IGNORED);
INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client));
if (pNetChan == NULL)
RETURN_META(MRES_IGNORED);
size_t nMsgLen = strlen(szMsg);
#if SOURCE_ENGINE == SE_EPISODEONE || SOURCE_ENGINE == SE_DARKMESSIAH
static const int nNumBitsWritten = 0;
#else
int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
#endif
// if the msg is bigger than allowed then just let it fail
if (nMsgLen + 1 >= SVC_Print_BufferSize) // +1 for NETMSG_TYPE_BITS
RETURN_META(MRES_IGNORED);
// enqueue msgs if we'd overflow the SVC_Print buffer (+7 as ceil)
if (!player.m_PrintfBuffer.empty() || (nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + nMsgLen >= SVC_Print_BufferSize)
{
// Don't send any more messages for this player until the buffer is empty.
// Queue up a gameframe hook to empty the buffer (if we haven't already)
if (player.m_PrintfBuffer.empty())
g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial());
player.m_PrintfBuffer.push_back(szMsg);
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}
void PlayerManager::OnPrintfFrameAction(unsigned int serial)
{
int client = GetClientFromSerial(serial);
CPlayer &player = m_Players[client];
if (!player.IsConnected())
{
player.ClearNetchannelQueue();
return;
}
INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client));
if (pNetChan == NULL)
{
player.ClearNetchannelQueue();
return;
}
while (!player.m_PrintfBuffer.empty())
{
#if SOURCE_ENGINE == SE_EPISODEONE || SOURCE_ENGINE == SE_DARKMESSIAH
static const int nNumBitsWritten = 0;
#else
int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
#endif
std::string &string = player.m_PrintfBuffer.front();
// stop if we'd overflow the SVC_Print buffer (+7 as ceil)
if ((nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + string.length() >= SVC_Print_BufferSize)
break;
SH_CALL(engine, &IVEngineServer::ClientPrintf)(player.m_pEdict, string.c_str());
player.m_PrintfBuffer.pop_front();
}
if (!player.m_PrintfBuffer.empty())
{
// continue processing it on the next gameframe as buffer is not empty
g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial());
}
}
void ClientConsolePrint(edict_t *e, const char *fmt, ...)
{
char buffer[512];
@ -876,8 +991,6 @@ void ClientConsolePrint(edict_t *e, const char *fmt, ...)
void ListExtensionsToClient(CPlayer *player, const CCommand &args)
{
char buffer[256];
unsigned int id = 0;
unsigned int start = 0;
AutoExtensionList extensions(extsys);
if (!extensions->size())
@ -886,11 +999,6 @@ void ListExtensionsToClient(CPlayer *player, const CCommand &args)
return;
}
if (args.ArgC() > 2)
{
start = atoi(args.Arg(2));
}
size_t i = 0;
for (; i < extensions->size(); i++)
{
@ -902,17 +1010,6 @@ void ListExtensionsToClient(CPlayer *player, const CCommand &args)
continue;
}
id++;
if (id < start)
{
continue;
}
if (id - start > 10)
{
break;
}
IExtensionInterface *api = ext->GetAPI();
const char *name = api->GetExtensionName();
@ -937,31 +1034,14 @@ void ListExtensionsToClient(CPlayer *player, const CCommand &args)
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, ": %s", description);
}
ClientConsolePrint(player->GetEdict(), "%s", buffer);
}
for (; i < extensions->size(); i++)
{
char error[255];
if (extensions->at(i)->IsRunning(error, sizeof(error)))
{
break;
}
}
if (i < extensions->size())
{
ClientConsolePrint(player->GetEdict(), "To see more, type \"sm exts %d\"", id);
}
}
void ListPluginsToClient(CPlayer *player, const CCommand &args)
{
char buffer[256];
unsigned int id = 0;
edict_t *e = player->GetEdict();
unsigned int start = 0;
AutoPluginList plugins(scripts);
if (!plugins->size())
@ -970,11 +1050,6 @@ void ListPluginsToClient(CPlayer *player, const CCommand &args)
return;
}
if (args.ArgC() > 2)
{
start = atoi(args.Arg(2));
}
SourceHook::List<SMPlugin *> m_FailList;
size_t i = 0;
@ -987,18 +1062,6 @@ void ListPluginsToClient(CPlayer *player, const CCommand &args)
continue;
}
/* Count valid plugins */
id++;
if (id < start)
{
continue;
}
if (id - start > 10)
{
break;
}
size_t len;
const sm_plugininfo_t *info = pl->GetPublicInfo();
len = ke::SafeSprintf(buffer, sizeof(buffer), " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
@ -1016,21 +1079,6 @@ void ListPluginsToClient(CPlayer *player, const CCommand &args)
}
ClientConsolePrint(e, "%s", buffer);
}
/* See if we can get more plugins */
for (; i < plugins->size(); i++)
{
if (plugins->at(i)->GetStatus() == Plugin_Running)
{
break;
}
}
/* Do we actually have more plugins? */
if (i < plugins->size())
{
ClientConsolePrint(e, "To see more, type \"sm plugins %d\"", id);
}
}
#if SOURCE_ENGINE >= SE_ORANGEBOX
@ -1065,14 +1113,18 @@ void PlayerManager::OnClientCommand(edict_t *pEntity)
}
else if (args.ArgC() > 1 && strcmp(args.Arg(1), "credits") == 0)
{
ClientConsolePrint(pEntity,
"SourceMod would not be possible without:");
ClientConsolePrint(pEntity,
ClientConsolePrint(pEntity,
"SourceMod would not be possible without:");
ClientConsolePrint(pEntity,
" David \"BAILOPAN\" Anderson, Matt \"pRED\" Woodrow");
ClientConsolePrint(pEntity,
ClientConsolePrint(pEntity,
" Scott \"DS\" Ehlert, Fyren");
ClientConsolePrint(pEntity,
ClientConsolePrint(pEntity,
" Nicholas \"psychonic\" Hastings, Asher \"asherkin\" Baker");
ClientConsolePrint(pEntity,
" Ruben \"Dr!fter\" Gonzalez, Josh \"KyleS\" Allard");
ClientConsolePrint(pEntity,
" Michael \"Headline\" Flaherty, Jannik \"Peace-Maker\" Hartung");
ClientConsolePrint(pEntity,
" Borja \"faluco\" Ferrer, Pavol \"PM OnoTo\" Marko");
ClientConsolePrint(pEntity,
@ -1087,7 +1139,7 @@ void PlayerManager::OnClientCommand(edict_t *pEntity)
ClientConsolePrint(pEntity,
"To see credits, type \"sm credits\"");
ClientConsolePrint(pEntity,
"Visit http://www.sourcemod.net/");
"Visit https://www.sourcemod.net/");
RETURN_META(MRES_SUPERCEDE);
}
@ -1112,7 +1164,7 @@ void PlayerManager::OnClientCommand(edict_t *pEntity)
if (g_ConsoleDetours.IsEnabled())
{
cell_t res2 = g_ConsoleDetours.InternalDispatch(client, &cargs);
if (res2 >= Pl_Stop)
if (res2 >= Pl_Handled)
{
RETURN_META(MRES_SUPERCEDE);
}
@ -1242,65 +1294,69 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
m_clinfochanged->PushCell(client);
m_clinfochanged->Execute(&res, NULL);
if (pPlayer->IsFakeClient())
{
return;
}
IPlayerInfo *info = pPlayer->GetPlayerInfo();
const char *new_name = info ? info->GetName() : engine->GetClientConVarValue(client, "name");
const char *old_name = pPlayer->m_Name.c_str();
#if SOURCE_ENGINE >= SE_LEFT4DEAD
const char *networkid_force;
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
{
unsigned int accountId = pPlayer->GetSteamAccountID();
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
pPlayer->Kick("NetworkID spoofing detected.");
RETURN_META(MRES_IGNORED);
}
#endif
if (strcmp(old_name, new_name) != 0)
{
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
if (!pPlayer->IsFakeClient())
{
if (!CheckSetAdminName(client, pPlayer, id))
AdminId id = adminsys->FindAdminByIdentity("name", new_name);
if (id != INVALID_ADMIN_ID && pPlayer->GetAdminId() != id)
{
char kickMsg[128];
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
pPlayer->Kick(kickMsg);
RETURN_META(MRES_IGNORED);
if (!CheckSetAdminName(client, pPlayer, id))
{
char kickMsg[128];
logicore.CoreTranslate(kickMsg, sizeof(kickMsg), "%T", 2, NULL, "Name Reserved", &client);
pPlayer->Kick(kickMsg);
RETURN_META(MRES_IGNORED);
}
}
} else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
if (id == pPlayer->GetAdminId())
{
/* This player is changing their name; force them to drop admin privileges! */
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
else if ((id = adminsys->FindAdminByIdentity("name", old_name)) != INVALID_ADMIN_ID) {
if (id == pPlayer->GetAdminId())
{
/* This player is changing their name; force them to drop admin privileges! */
pPlayer->SetAdminId(INVALID_ADMIN_ID, false);
}
}
}
pPlayer->SetName(new_name);
}
if (m_PassInfoVar.size() > 0)
if (!pPlayer->IsFakeClient())
{
/* Try for a password change */
const char *old_pass = pPlayer->m_LastPassword.c_str();
const char *new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
if (strcmp(old_pass, new_pass) != 0)
if (m_PassInfoVar.size() > 0)
{
pPlayer->m_LastPassword.assign(new_pass);
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
/* Try for a password change */
const char* old_pass = pPlayer->m_LastPassword.c_str();
const char* new_pass = engine->GetClientConVarValue(client, m_PassInfoVar.c_str());
if (strcmp(old_pass, new_pass) != 0)
{
/* If there is already an admin id assigned, this will just bail out. */
pPlayer->DoBasicAdminChecks();
pPlayer->m_LastPassword.assign(new_pass);
if (pPlayer->IsInGame() && pPlayer->IsAuthorized())
{
/* If there is already an admin id assigned, this will just bail out. */
pPlayer->DoBasicAdminChecks();
}
}
}
#if SOURCE_ENGINE >= SE_LEFT4DEAD
const char* networkid_force;
if ((networkid_force = engine->GetClientConVarValue(client, "networkid_force")) && networkid_force[0] != '\0')
{
unsigned int accountId = pPlayer->GetSteamAccountID();
logger->LogMessage("\"%s<%d><STEAM_1:%d:%d><>\" has bad networkid (id \"%s\") (ip \"%s\")",
new_name, pPlayer->GetUserId(), accountId & 1, accountId >> 1, networkid_force, pPlayer->GetIPAddress());
pPlayer->Kick("NetworkID spoofing detected.");
RETURN_META(MRES_IGNORED);
}
#endif
}
/* Notify Extensions */
List<IClientListener *>::iterator iter;
IClientListener *pListener = NULL;
@ -1314,6 +1370,13 @@ void PlayerManager::OnClientSettingsChanged(edict_t *pEntity)
}
}
void PlayerManager::OnClientLanguageChanged(int client, unsigned int language)
{
m_cllang->PushCell(client);
m_cllang->PushCell(language);
m_cllang->Execute(NULL);
}
int PlayerManager::GetMaxClients()
{
return m_maxClients;
@ -1333,6 +1396,11 @@ int PlayerManager::GetNumPlayers()
return m_PlayerCount;
}
int PlayerManager::GetNumClients()
{
return m_ClientCount;
}
int PlayerManager::GetClientOfUserId(int userid)
{
if (userid < 0 || userid > USHRT_MAX)
@ -1474,7 +1542,10 @@ void PlayerManager::InvalidatePlayer(CPlayer *pPlayer)
}
}
m_UserIdLookUp[engine->GetPlayerUserId(pPlayer->m_pEdict)] = 0;
auto userid = engine->GetPlayerUserId(pPlayer->m_pEdict);
if (userid != -1)
m_UserIdLookUp[userid] = 0;
pPlayer->Disconnect();
}
@ -1910,7 +1981,7 @@ void CmdMaxplayersCallback()
g_Players.MaxPlayersChanged();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
{
for (int i = 1; i <= m_maxClients; i++)
@ -1918,7 +1989,10 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
if (m_Players[i].m_LanguageCookie == cookie)
{
unsigned int langid;
m_Players[i].m_LangId = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
unsigned int new_langid = (translator->GetLanguageByName(cvarValue, &langid)) ? langid : translator->GetServerLanguage();
m_Players[i].m_LangId = new_langid;
m_Players[i].m_OriginalLangId = new_langid;
OnClientLanguageChanged(i, new_langid);
return true;
}
@ -1928,6 +2002,27 @@ bool PlayerManager::HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQue
}
#endif
/* IGameEventListener2::FireGameEvent */
void PlayerManager::FireGameEvent(IGameEvent *pEvent)
{
const char *name = pEvent->GetName();
if (strcmp(name, "player_connect") == 0)
{
const int client = pEvent->GetInt("index") + 1;
const int userid = pEvent->GetInt("userid");
m_ClientCount++;
}
else if (strcmp(name, "player_disconnect") == 0)
{
const int userid = pEvent->GetInt("userid");
const int client = m_UserIdLookUp[userid];
m_ClientCount--;
}
}
/*******************
*** PLAYER CODE ***
@ -1946,6 +2041,7 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
m_pEdict = pEntity;
m_iIndex = IndexOfEdict(pEntity);
m_LangId = translator->GetServerLanguage();
m_OriginalLangId = m_LangId;
m_Serial.bits.index = m_iIndex;
m_Serial.bits.serial = g_PlayerSerialCount++;
@ -1966,7 +2062,9 @@ void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
|| SOURCE_ENGINE == SE_HL2DM \
|| SOURCE_ENGINE == SE_BMS \
|| SOURCE_ENGINE == SE_INSURGENCY \
|| SOURCE_ENGINE == SE_DOI
|| SOURCE_ENGINE == SE_DOI \
|| SOURCE_ENGINE == SE_BLADE \
|| SOURCE_ENGINE == SE_PVKII
m_pIClient = engine->GetIServer()->GetClient(m_iIndex - 1);
#else
#if SOURCE_ENGINE == SE_SDK2013
@ -2142,9 +2240,16 @@ void CPlayer::Disconnect()
m_bIsSourceTV = false;
m_bIsReplay = false;
m_Serial.value = -1;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
m_LanguageCookie = InvalidQueryCvarCookie;
#endif
ClearNetchannelQueue();
}
void CPlayer::ClearNetchannelQueue(void)
{
while (!m_PrintfBuffer.empty())
m_PrintfBuffer.pop_front();
}
void CPlayer::SetName(const char *name)
@ -2177,11 +2282,6 @@ void CPlayer::SetName(const char *name)
const char *CPlayer::GetName()
{
if (m_Info && m_pEdict->GetUnknown())
{
return m_Info->GetName();
}
return m_Name.c_str();
}
@ -2197,7 +2297,7 @@ const char *CPlayer::GetAuthString(bool validated)
return NULL;
}
return m_AuthID.chars();
return m_AuthID.c_str();
}
const CSteamID &CPlayer::GetSteamId(bool validated)
@ -2218,7 +2318,7 @@ const char *CPlayer::GetSteam2Id(bool validated)
return NULL;
}
return m_Steam2Id.chars();
return m_Steam2Id.c_str();
}
const char *CPlayer::GetSteam3Id(bool validated)
@ -2228,14 +2328,14 @@ const char *CPlayer::GetSteam3Id(bool validated)
return NULL;
}
return m_Steam3Id.chars();
return m_Steam3Id.c_str();
}
unsigned int CPlayer::GetSteamAccountID(bool validated)
{
if (!IsFakeClient() && (!validated || IsAuthStringValidated()))
{
const CSteamID &id = GetSteamId();
const CSteamID &id = GetSteamId(validated);
if (id.IsValid())
return id.GetAccountID();
}
@ -2362,7 +2462,7 @@ void CPlayer::Kick(const char *str)
}
else
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
pClient->Disconnect(str);
#else
pClient->Disconnect("%s", str);
@ -2495,7 +2595,7 @@ void CPlayer::DoBasicAdminChecks()
}
/* Check steam id */
if ((id = adminsys->FindAdminByIdentity("steam", m_AuthID.chars())) != INVALID_ADMIN_ID)
if ((id = adminsys->FindAdminByIdentity("steam", m_AuthID.c_str())) != INVALID_ADMIN_ID)
{
if (g_Players.CheckSetAdmin(client, this, id))
{
@ -2509,9 +2609,18 @@ unsigned int CPlayer::GetLanguageId()
return m_LangId;
}
unsigned int CPlayer::GetOriginalLanguageId()
{
return m_OriginalLangId;
}
void CPlayer::SetLanguageId(unsigned int id)
{
m_LangId = id;
if(m_LangId != id)
{
m_LangId = id;
g_Players.OnClientLanguageChanged(m_iIndex, id);
}
}
int CPlayer::GetUserId()

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 :
* vim: set ts=4 sts=8 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved.
@ -43,6 +43,7 @@
#include <sh_list.h>
#include <sh_vector.h>
#include <am-string.h>
#include <am-deque.h>
#include "ConVarManager.h"
#include <steam/steamclientpublic.h>
@ -95,6 +96,7 @@ public:
bool IsInKickQueue();
IPlayerInfo *GetPlayerInfo();
unsigned int GetLanguageId();
unsigned int GetOriginalLanguageId();
void SetLanguageId(unsigned int id);
int GetUserId();
bool RunAdminCacheChecks();
@ -123,6 +125,7 @@ private:
bool IsAuthStringValidated();
bool SetEngineString();
bool SetCSteamID();
void ClearNetchannelQueue(void);
private:
bool m_IsConnected = false;
bool m_IsInGame = false;
@ -131,9 +134,9 @@ private:
String m_Name;
String m_Ip;
String m_IpNoPort;
ke::AString m_AuthID;
ke::AString m_Steam2Id;
ke::AString m_Steam3Id;
std::string m_AuthID;
std::string m_Steam2Id;
std::string m_Steam3Id;
AdminId m_Admin = INVALID_ADMIN_ID;
bool m_TempAdmin = false;
edict_t *m_pEdict = nullptr;
@ -143,20 +146,23 @@ private:
bool m_bAdminCheckSignalled = false;
int m_iIndex;
unsigned int m_LangId = SOURCEMOD_LANGUAGE_ENGLISH;
unsigned int m_OriginalLangId = SOURCEMOD_LANGUAGE_ENGLISH;
int m_UserId = -1;
bool m_bFakeClient = false;
bool m_bIsSourceTV = false;
bool m_bIsReplay = false;
serial_t m_Serial;
CSteamID m_SteamId;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie;
#endif
std::deque<std::string> m_PrintfBuffer;
};
class PlayerManager :
public SMGlobalClass,
public IPlayerManager
public IPlayerManager,
public IGameEventListener2
{
friend class CPlayer;
public:
@ -189,7 +195,10 @@ public:
#endif
void OnClientSettingsChanged(edict_t *pEntity);
//void OnClientSettingsChanged_Pre(edict_t *pEntity);
void OnClientLanguageChanged(int client, unsigned int language);
void OnServerHibernationUpdate(bool bHibernating);
void OnClientPrintf(edict_t *pEdict, const char *szMsg);
void OnPrintfFrameAction(unsigned int serial);
public: //IPlayerManager
void AddClientListener(IClientListener *listener);
void RemoveClientListener(IClientListener *listener);
@ -197,6 +206,7 @@ public: //IPlayerManager
IGamePlayer *GetGamePlayer(edict_t *pEdict);
int GetMaxClients();
int GetNumPlayers();
int GetNumClients();
int GetClientOfUserId(int userid);
bool IsServerActivated();
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
@ -207,6 +217,8 @@ public: //IPlayerManager
int GetClientFromSerial(unsigned int serial);
void ClearAdminId(AdminId id);
void RecheckAnyAdmins();
public: // IGameEventListener2
void FireGameEvent(IGameEvent *pEvent);
public:
inline int MaxClients()
{
@ -230,7 +242,7 @@ public:
{
return m_bInCCKVHook;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
#endif
private:
@ -248,6 +260,7 @@ private:
IForward *m_clcommandkv_post;
IForward *m_clinfochanged;
IForward *m_clauth;
IForward *m_cllang;
IForward *m_onActivate;
IForward *m_onActivate2;
CPlayer *m_Players;
@ -267,6 +280,10 @@ private:
int m_SourceTVUserId;
int m_ReplayUserId;
bool m_bInCCKVHook;
int m_ClientCount;
private:
static const int NETMSG_TYPE_BITS = 5; // SVC_Print overhead for netmsg type
static const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0
};
#if SOURCE_ENGINE >= SE_ORANGEBOX

View File

@ -69,6 +69,13 @@ class DefaultMapTimer :
public IConVarChangeListener
{
public:
#if SOURCE_ENGINE == SE_BMS
static constexpr int kMapTimeScaleFactor = 60;
#else
static constexpr int kMapTimeScaleFactor = 1;
#endif
DefaultMapTimer()
{
m_bInUse = false;
@ -81,7 +88,7 @@ public:
int GetMapTimeLimit()
{
return mp_timelimit->GetInt();
return (mp_timelimit->GetInt() / kMapTimeScaleFactor);
}
void SetMapTimerStatus(bool enabled)
@ -105,7 +112,7 @@ public:
return;
}
extra_time /= 60;
extra_time /= (60 / kMapTimeScaleFactor);
mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time);
}
@ -484,4 +491,3 @@ bool TimerSystem::GetMapTimeLeft(float *time_left)
return true;
}

View File

@ -816,9 +816,6 @@ public:
inline bool GetColor(const char *pszFieldName, Color *out)
{
#if SOURCE_ENGINE != SE_CSGO
return false;
#else
GETCHECK_FIELD();
CHECK_FIELD_TYPE(MESSAGE);
CHECK_FIELD_NOT_REPEATED();
@ -832,14 +829,10 @@ public:
);
return true;
#endif
}
inline bool SetColor(const char *pszFieldName, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
return false;
#else
GETCHECK_FIELD();
CHECK_FIELD_TYPE(MESSAGE);
CHECK_FIELD_NOT_REPEATED();
@ -851,14 +844,10 @@ public:
msgRGBA->set_a(value.a());
return true;
#endif
}
inline bool GetRepeatedColor(const char *pszFieldName, int index, Color *out)
{
#if SOURCE_ENGINE != SE_CSGO
return false;
#else
GETCHECK_FIELD();
CHECK_FIELD_TYPE(MESSAGE);
CHECK_FIELD_REPEATED();
@ -873,14 +862,10 @@ public:
);
return true;
#endif
}
inline bool SetRepeatedColor(const char *pszFieldName, int index, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
return false;
#else
GETCHECK_FIELD();
CHECK_FIELD_TYPE(MESSAGE);
CHECK_FIELD_REPEATED();
@ -893,14 +878,10 @@ public:
msgRGBA->set_a(value.a());
return true;
#endif
}
inline bool AddColor(const char *pszFieldName, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
return false;
#else
GETCHECK_FIELD();
CHECK_FIELD_TYPE(MESSAGE);
CHECK_FIELD_REPEATED();
@ -912,7 +893,6 @@ public:
msgRGBA->set_a(value.a());
return true;
#endif
}
inline bool GetVector2D(const char *pszFieldName, Vector2D *out)

View File

@ -35,12 +35,16 @@
#if SOURCE_ENGINE == SE_CSGO
#include <cstrike15_usermessage_helpers.h>
#elif SOURCE_ENGINE == SE_BLADE
#include <berimbau_usermessage_helpers.h>
#elif SOURCE_ENGINE == SE_MCV
#include <vietnam_usermessage_helpers.h>
#endif
#include <amtl/am-string.h>
UserMessages g_UserMsgs;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
SH_DECL_HOOK3_void(IVEngineServer, SendUserMessage, SH_NOATTRIB, 0, IRecipientFilter &, int, const protobuf::Message &);
#else
#if SOURCE_ENGINE >= SE_LEFT4DEAD
@ -49,7 +53,7 @@ SH_DECL_HOOK3(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRec
SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
#endif
SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
#endif // ==SE_CSGO
#endif // ==SE_CSGO || ==SE_BLADE || ==SE_MCV
UserMessages::UserMessages()
#ifndef USE_PROTOBUF_USERMESSAGES
@ -93,7 +97,7 @@ void UserMessages::OnSourceModAllShutdown()
{
if (m_HookCount)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
#else
@ -111,6 +115,10 @@ int UserMessages::GetMessageIndex(const char *msg)
#if SOURCE_ENGINE == SE_CSGO
// Can split this per engine and/or game later
return g_Cstrike15UsermessageHelpers.GetIndex(msg);
#elif SOURCE_ENGINE == SE_BLADE
return g_BerimbauUsermessageHelpers.GetIndex(msg);
#elif SOURCE_ENGINE == SE_MCV
return g_VietnamUsermessageHelpers.GetIndex(msg);
#else
int msgid;
@ -148,6 +156,10 @@ bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) con
#ifdef USE_PROTOBUF_USERMESSAGES
#if SOURCE_ENGINE == SE_CSGO
const char *pszName = g_Cstrike15UsermessageHelpers.GetName(msgid);
#elif SOURCE_ENGINE == SE_BLADE
const char *pszName = g_BerimbauUsermessageHelpers.GetName(msgid);
#elif SOURCE_ENGINE == SE_MCV
const char *pszName = g_VietnamUsermessageHelpers.GetName(msgid);
#endif
if (!pszName)
return false;
@ -297,7 +309,7 @@ bool UserMessages::EndMessage()
return false;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
if (m_CurFlags & USERMSG_BLOCKHOOKS)
{
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
@ -327,7 +339,7 @@ bool UserMessages::EndMessage()
} else {
engine->MessageEnd();
}
#endif // SE_CSGO
#endif // SE_CSGO || SE_BLADE || SE_MCV
m_InExec = false;
m_CurFlags = 0;
@ -412,7 +424,7 @@ bool UserMessages::InternalHook(int msg_id, IBitBufUserMessageListener *pListene
if (!m_HookCount++)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
SH_ADD_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
SH_ADD_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
#else
@ -438,6 +450,10 @@ const protobuf::Message *UserMessages::GetMessagePrototype(int msg_type)
{
#if SOURCE_ENGINE == SE_CSGO
return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type);
#elif SOURCE_ENGINE == SE_BLADE
return g_BerimbauUsermessageHelpers.GetPrototype(msg_type);
#elif SOURCE_ENGINE == SE_MCV
return g_VietnamUsermessageHelpers.GetPrototype(msg_type);
#endif
}
#endif
@ -487,7 +503,7 @@ void UserMessages::_DecRefCounter()
{
if (--m_HookCount == 0)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
#else
@ -499,12 +515,19 @@ void UserMessages::_DecRefCounter()
}
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
{
#if SOURCE_ENGINE == SE_CSGO
OnStartMessage_Pre(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
const char *pszName = g_Cstrike15UsermessageHelpers.GetName(msg_type);
#elif SOURCE_ENGINE == SE_BLADE
const char *pszName = g_BerimbauUsermessageHelpers.GetName(msg_type);
#elif SOURCE_ENGINE == SE_MCV
const char *pszName = g_VietnamUsermessageHelpers.GetName(msg_type);
#endif
OnStartMessage_Pre(&filter, msg_type, pszName);
if (m_FakeMetaRes == MRES_SUPERCEDE)
{
int size = msg.ByteSize();
@ -517,9 +540,7 @@ void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type,
m_FakeEngineBuffer = &const_cast<protobuf::Message &>(msg);
}
#if SOURCE_ENGINE == SE_CSGO
OnStartMessage_Post(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
#endif
OnStartMessage_Post(&filter, msg_type, pszName);
OnMessageEnd_Pre();
if (m_FakeMetaRes == MRES_SUPERCEDE)
@ -551,7 +572,7 @@ void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type
RETURN_META(res)
#endif
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
@ -591,7 +612,7 @@ bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_typ
UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
@ -756,7 +777,7 @@ void UserMessages::OnMessageEnd_Pre()
if (!handled && intercepted)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
#else
bf_write *engine_bfw;
@ -768,11 +789,11 @@ void UserMessages::OnMessageEnd_Pre()
m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
ENGINE_CALL(MessageEnd)();
#endif // SE_CSGO
#endif // SE_CSGO || SE_BLADE || SE_MCV
}
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
int size = m_OrigBuffer->ByteSize();
uint8 *data = (uint8 *)stackalloc(size);
m_OrigBuffer->SerializePartialToArray(data, size);
@ -802,7 +823,7 @@ void UserMessages::OnMessageEnd_Pre()
iter++;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
delete pTempMsg;
#endif
}

View File

@ -44,7 +44,7 @@
using namespace SourceHook;
using namespace SourceMod;
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
#define USE_PROTOBUF_USERMESSAGES
#endif
@ -102,12 +102,12 @@ public: //IUserMessages
bool intercept=false);
UserMessageType GetUserMessageType() const;
public:
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
void OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg);
void OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg);
#endif
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
protobuf::Message *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name);
protobuf::Message *OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name);
#elif SOURCE_ENGINE >= SE_LEFT4DEAD

View File

@ -36,7 +36,7 @@
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK1_void(ICvar, UnregisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
SH_DECL_HOOK2_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *, bool);
#else
SH_DECL_HOOK1_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
@ -82,7 +82,7 @@ public:
#endif
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_MCV
void LinkConCommandBase(ConCommandBase *pBase, bool unknown)
#else
void LinkConCommandBase(ConCommandBase *pBase)
@ -108,20 +108,20 @@ public:
listener = listener->next;
}
while (iter != tracked_bases.end())
{
if ((*iter)->pBase == pBase)
{
pInfo = (*iter);
iter = tracked_bases.erase(iter);
pInfo->cls->OnUnlinkConCommandBase(pBase, pBase->GetName());
delete pInfo;
}
else
{
iter++;
}
}
while (iter != tracked_bases.end())
{
if ((*iter)->pBase == pBase)
{
pInfo = (*iter);
iter = tracked_bases.erase(iter);
pInfo->cls->OnUnlinkConCommandBase(pBase, pBase->GetName());
delete pInfo;
}
else
{
iter++;
}
}
}
void AddTarget(ConCommandBase *pBase, IConCommandTracker *cls)

View File

@ -1,8 +1,8 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os
for arch in SM.archs:
binary = SM.Library(builder, 'sourcemod.logic', arch)
for cxx in builder.targets:
binary = SM.Library(builder, cxx, 'sourcemod.logic')
binary.compiler.cxxincludes += [
builder.sourcePath,
os.path.join(builder.sourcePath, 'core', 'logic'),
@ -16,10 +16,10 @@ for arch in SM.archs:
'SM_DEFAULT_THREADER',
'SM_LOGIC'
]
if builder.target.platform == 'linux':
if binary.compiler.target.platform == 'linux':
binary.compiler.postlink += ['-lpthread', '-lrt']
elif builder.target.platform == 'mac':
elif binary.compiler.target.platform == 'mac':
binary.compiler.cflags += ['-Wno-deprecated-declarations']
binary.compiler.postlink += ['-framework', 'CoreServices']
@ -35,8 +35,7 @@ for arch in SM.archs:
'smn_maplists.cpp',
'ADTFactory.cpp',
'smn_adt_stack.cpp',
'thread/ThreadWorker.cpp',
'thread/BaseWorker.cpp',
'BaseWorker.cpp',
'ThreadSupport.cpp',
'smn_float.cpp',
'TextParsers.cpp',
@ -85,14 +84,12 @@ for arch in SM.archs:
'smn_halflife.cpp',
'FrameIterator.cpp',
'DatabaseConfBuilder.cpp',
'LumpManager.cpp',
'smn_entitylump.cpp',
'NativeInvoker.cpp',
]
if arch == 'x64':
if binary.compiler.target.arch == 'x86_64':
binary.sources += ['PseudoAddrManager.cpp']
if builder.target.platform == 'windows':
binary.sources += ['thread/WinThreads.cpp']
else:
binary.sources += ['thread/PosixThreads.cpp']
SM.binaries += [builder.Add(binary)]

View File

@ -1581,7 +1581,7 @@ bool AdminCache::CanAdminTarget(AdminId id, AdminId target)
{
id = grp_table[i];
num = GetGroupImmunityCount(id);
for (unsigned int j=0; j<num; i++)
for (unsigned int j=0; j<num; j++)
{
other = GetGroupImmunity(id, j);
for (unsigned int k=0; k<pUser->grp_count; k++)
@ -1843,13 +1843,13 @@ bool AdminCache::DumpCache(const char *filename)
fprintf(fp, "\n\t\t\"Overrides\"\n\t\t{\n");
if (pGroup->pCmdGrpTable != NULL)
{
for (OverrideMap::iterator iter = pGroup->pCmdTable->iter(); !iter.empty(); iter.next())
iterator_group_grp_override(fp, iter->key.chars(), iter->value);
for (OverrideMap::iterator iter = pGroup->pCmdGrpTable->iter(); !iter.empty(); iter.next())
iterator_group_grp_override(fp, iter->key.c_str(), iter->value);
}
if (pGroup->pCmdTable != NULL)
{
for (OverrideMap::iterator iter = pGroup->pCmdTable->iter(); !iter.empty(); iter.next())
iterator_group_basic_override(fp, iter->key.chars(), iter->value);
iterator_group_basic_override(fp, iter->key.c_str(), iter->value);
}
fprintf(fp, "\t\t}\n");
@ -1924,9 +1924,9 @@ bool AdminCache::DumpCache(const char *filename)
fprintf(fp, "\"Overrides\"\n{\n");
for (FlagMap::iterator iter = m_CmdGrpOverrides.iter(); !iter.empty(); iter.next())
iterator_glob_grp_override(fp, iter->key.chars(), iter->value);
iterator_glob_grp_override(fp, iter->key.c_str(), iter->value);
for (FlagMap::iterator iter = m_CmdOverrides.iter(); !iter.empty(); iter.next())
iterator_glob_basic_override(fp, iter->key.chars(), iter->value);
iterator_glob_basic_override(fp, iter->key.c_str(), iter->value);
fprintf(fp, "}\n");
fclose(fp);

View File

@ -40,7 +40,7 @@ BaseWorker::BaseWorker(IThreadWorkerCallbacks *hooks) :
BaseWorker::~BaseWorker()
{
if (m_state != Worker_Stopped || m_state != Worker_Invalid)
if (m_state != Worker_Stopped && m_state != Worker_Invalid)
Stop(true);
if (m_ThreadQueue.size())

View File

@ -43,6 +43,7 @@ class BaseWorker;
class SWThreadHandle : public IThreadHandle
{
friend class BaseWorker;
friend class CompatWorker;
public:
SWThreadHandle(IThreadCreator *parent, const ThreadParams *p, IThread *thread);
IThread *GetThread();

View File

@ -31,8 +31,10 @@
#include <stdlib.h>
#include <string.h>
#include <memory>
#include "CDataPack.h"
#include <amtl/am-autoptr.h>
CDataPack::CDataPack()
{
@ -44,33 +46,15 @@ CDataPack::~CDataPack()
Initialize();
}
static ke::Vector<ke::AutoPtr<CDataPack>> sDataPackCache;
CDataPack *CDataPack::New()
{
if (sDataPackCache.empty())
return new CDataPack();
CDataPack *pack = sDataPackCache.back().take();
sDataPackCache.pop();
pack->Initialize();
return pack;
}
void
CDataPack::Free(CDataPack *pack)
{
sDataPackCache.append(static_cast<CDataPack *>(pack));
}
void CDataPack::Initialize()
{
position = 0;
do
{
} while (this->RemoveItem());
elements.clear();
position = 0;
}
void CDataPack::ResetSize()
@ -84,7 +68,7 @@ size_t CDataPack::CreateMemory(size_t size, void **addr)
val.type = CDataPackType::Raw;
val.pData.vval = new uint8_t[size + sizeof(size)];
reinterpret_cast<size_t *>(val.pData.vval)[0] = size;
elements.insert(position, val);
elements.emplace(elements.begin() + position, val);
return position++;
}
@ -94,7 +78,8 @@ void CDataPack::PackCell(cell_t cell)
InternalPack val;
val.type = CDataPackType::Cell;
val.pData.cval = cell;
elements.insert(position++, val);
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackFunction(cell_t function)
@ -102,7 +87,8 @@ void CDataPack::PackFunction(cell_t function)
InternalPack val;
val.type = CDataPackType::Function;
val.pData.cval = function;
elements.insert(position++, val);
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackFloat(float floatval)
@ -110,16 +96,42 @@ void CDataPack::PackFloat(float floatval)
InternalPack val;
val.type = CDataPackType::Float;
val.pData.fval = floatval;
elements.insert(position++, val);
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackString(const char *string)
{
InternalPack val;
val.type = CDataPackType::String;
ke::AString *sval = new ke::AString(string);
std::string *sval = new std::string(string);
val.pData.sval = sval;
elements.insert(position++, val);
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackCellArray(cell_t const *vals, cell_t count)
{
InternalPack val;
val.type = CDataPackType::CellArray;
val.pData.aval = new cell_t [count + 1];
memcpy(&val.pData.aval[1], vals, sizeof(cell_t) * count);
val.pData.aval[0] = count;
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackFloatArray(cell_t const *vals, cell_t count)
{
InternalPack val;
val.type = CDataPackType::FloatArray;
val.pData.aval = new cell_t [count + 1];
memcpy(&val.pData.aval[1], vals, sizeof(cell_t) * count);
val.pData.aval[0] = count;
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::Reset() const
@ -134,7 +146,7 @@ size_t CDataPack::GetPosition() const
bool CDataPack::SetPosition(size_t pos) const
{
if (pos > elements.length())
if (pos > elements.size())
return false;
position = pos;
@ -167,7 +179,7 @@ float CDataPack::ReadFloat() const
bool CDataPack::IsReadable(size_t bytes) const
{
return (position < elements.length());
return (position < elements.size());
}
const char *CDataPack::ReadString(size_t *len) const
@ -180,11 +192,51 @@ const char *CDataPack::ReadString(size_t *len) const
return nullptr;
}
const ke::AString &val = *elements[position++].pData.sval;
const std::string &val = *elements[position++].pData.sval;
if (len)
*len = val.length();
*len = val.size();
return val.chars();
return val.c_str();
}
cell_t *CDataPack::ReadCellArray(cell_t *size) const
{
if (!IsReadable() || elements[position].type != CDataPackType::CellArray)
{
if(size)
*size = 0;
return nullptr;
}
cell_t *val = elements[position].pData.aval;
cell_t *ptr = &(val[1]);
++position;
if (size)
*size = val[0];
return ptr;
}
cell_t *CDataPack::ReadFloatArray(cell_t *size) const
{
if (!IsReadable() || elements[position].type != CDataPackType::FloatArray)
{
if(size)
*size = 0;
return nullptr;
}
cell_t *val = elements[position].pData.aval;
cell_t *ptr = &(val[1]);
++position;
if (size)
*size = val[0];
return ptr;
}
void *CDataPack::ReadMemory(size_t *size) const
@ -205,7 +257,7 @@ void *CDataPack::ReadMemory(size_t *size) const
bool CDataPack::RemoveItem(size_t pos)
{
if (!elements.length())
if (!elements.size())
{
return false;
}
@ -214,7 +266,8 @@ bool CDataPack::RemoveItem(size_t pos)
{
pos = position;
}
if (pos >= elements.length())
if (pos >= elements.size())
{
return false;
}
@ -228,7 +281,7 @@ bool CDataPack::RemoveItem(size_t pos)
{
case CDataPackType::Raw:
{
delete elements[pos].pData.vval;
delete [] elements[pos].pData.vval;
break;
}
@ -237,8 +290,15 @@ bool CDataPack::RemoveItem(size_t pos)
delete elements[pos].pData.sval;
break;
}
case CDataPackType::CellArray:
case CDataPackType::FloatArray:
{
delete [] elements[pos].pData.aval;
break;
}
}
elements.remove(pos);
elements.erase(elements.begin() + pos);
return true;
}

View File

@ -43,7 +43,9 @@ enum CDataPackType {
Cell,
Float,
String,
Function
Function,
CellArray,
FloatArray,
};
class CDataPack
@ -52,9 +54,6 @@ public:
CDataPack();
~CDataPack();
static CDataPack *New();
static void Free(CDataPack *pack);
public: // Originally IDataReader
/**
* @brief Resets the position in the data stream to the beginning.
@ -90,6 +89,21 @@ public: // Originally IDataReader
*/
float ReadFloat() const;
/**
* @brief Reads an array of values from the data stream.
*
* @param len The size of the array stored at this position to return.
* @return A cell array read from the current position.
*/
cell_t *ReadCellArray(cell_t *len) const;
/**
* @brief Reads an array of values from the data stream.
*
* @param len The size of the array stored at this position to return.
* @return A cell array read from the current position.
*/
cell_t *ReadFloatArray(cell_t *len) const;
/**
* @brief Returns whether or not a specified number of bytes from the current stream
* position to the end can be read.
@ -150,6 +164,21 @@ public: // Originally IDataPack
*/
void PackString(const char *string);
/**
* @brief Packs an array of cells into the data stream.
*
* @param vals Cells to write.
* @param count Number of cells.
*/
void PackCellArray(cell_t const *vals, cell_t count);
/**
* @brief Packs an array of cells into the data stream.
*
* @param vals Cells to write.
* @param count Number of cells.
*/
void PackFloatArray(cell_t const *vals, cell_t count);
/**
* @brief Creates a generic block of memory in the stream.
*
@ -172,7 +201,7 @@ public: // Originally IDataPack
public:
void Initialize();
inline size_t GetCapacity() const { return this->elements.length(); };
inline size_t GetCapacity() const { return this->elements.size(); };
inline CDataPackType GetCurrentType(void) const { return this->elements[this->position].type; };
bool RemoveItem(size_t pos = -1);
@ -181,7 +210,8 @@ private:
cell_t cval;
float fval;
uint8_t *vval;
ke::AString *sval;
std::string *sval;
cell_t *aval;
} InternalPackValue;
typedef struct {
@ -189,7 +219,7 @@ private:
CDataPackType type;
} InternalPack;
ke::Vector<InternalPack> elements;
std::vector<InternalPack> elements;
mutable size_t position;
};

View File

@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include <ICellArray.h>
#include <amtl/am-bits.h>
extern HandleType_t htCellArray;
@ -214,30 +215,34 @@ private:
{
return true;
}
size_t newAllocSize = m_AllocSize;
/* Set a base allocation size of 8 items */
if (!m_AllocSize)
if (!newAllocSize)
{
m_AllocSize = 8;
newAllocSize = 8;
}
if (!ke::IsUintPtrAddSafe(m_Size, count))
{
return false;
}
/* If it's not enough, keep doubling */
while (m_Size + count > m_AllocSize)
while (m_Size + count > newAllocSize)
{
m_AllocSize *= 2;
}
/* finally, allocate the new block */
if (m_Data)
{
cell_t *data = static_cast<cell_t*>(realloc(m_Data, sizeof(cell_t) * m_BlockSize * m_AllocSize));
if (!data) // allocation failure
if (!ke::IsUintPtrMultiplySafe(newAllocSize, 2))
{
return false;
}
m_Data = data;
} else {
m_Data = static_cast<cell_t*>(malloc(sizeof(cell_t) * m_BlockSize * m_AllocSize));
newAllocSize *= 2;
}
return (m_Data != nullptr);
/* finally, allocate the new block */
cell_t *data = static_cast<cell_t*>(realloc(m_Data, sizeof(cell_t) * m_BlockSize * newAllocSize));
/* Update state if allocation was successful */
if (data)
{
m_AllocSize = newAllocSize;
m_Data = data;
}
return (data != nullptr);
}
private:
cell_t *m_Data;

View File

@ -34,17 +34,20 @@
#include "HandleSys.h"
#include "ExtensionSys.h"
#include "PluginSys.h"
#include <chrono>
#include <amtl/am-thread.h>
#include <stdlib.h>
#include <IThreader.h>
#include <bridge/include/ILogger.h>
#include <bridge/include/CoreProvider.h>
using namespace std::chrono_literals;
#define DBPARSE_LEVEL_NONE 0
#define DBPARSE_LEVEL_MAIN 1
#define DBPARSE_LEVEL_DATABASE 2
DBManager g_DBMan;
static bool s_OneTimeThreaderErrorMsg = false;
DBManager::DBManager()
: m_Terminate(false),
@ -125,8 +128,8 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> pInfo = list->GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> pInfo = list.GetDatabaseConf(name);
if (!pInfo)
{
@ -145,12 +148,12 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
/* Try to assign a real driver pointer */
if (pInfo->info.driver[0] == '\0')
{
ke::AString defaultDriver = list->GetDefaultDriver();
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(defaultDriver.chars());
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
}
dname = defaultDriver.length() ? defaultDriver.chars() : "default";
dname = defaultDriver.length() ? defaultDriver.c_str() : "default";
pInfo->realDriver = m_pDefault;
} else {
pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
@ -206,17 +209,14 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
}
}
ConfDbInfoList *list = m_Builder.GetConfigList();
for (size_t i = 0; i < list->length(); i++)
{
ke::RefPtr<ConfDbInfo> current = list->at(i);
if (current->realDriver == pDriver)
ConfDbInfoList &list = m_Builder.GetConfigList();
for (auto conf : list) {
if (conf->realDriver == pDriver)
{
current->realDriver = NULL;
conf->realDriver = NULL;
}
}
/* Someone unloaded the default driver? Silly.. */
if (pDriver == m_pDefault)
{
@ -252,11 +252,11 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
IDBDriver *DBManager::GetDefaultDriver()
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::AString defaultDriver = list->GetDefaultDriver();
ConfDbInfoList &list = m_Builder.GetConfigList();
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(defaultDriver.chars());
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
}
return m_pDefault;
@ -319,12 +319,12 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info = list->GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info = list.GetDatabaseConf(name);
if (!info)
{
// couldn't find requested conf, return default if exists
info = list->GetDefaultConfiguration();
info = list.GetDefaultConfiguration();
if (!info)
{
return NULL;
@ -334,11 +334,10 @@ const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
return &info->info;
}
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
ke::RefPtr<ConfDbInfo> DBManager::GetDatabaseConf(const char *name)
{
ConfDbInfoList *list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info(list->GetDatabaseConf(name));
return info;
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDatabaseConf(name);
}
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
@ -377,13 +376,12 @@ void DBManager::KillWorkerThread()
if (m_Worker)
{
{
ke::AutoLock lock(&m_QueueEvent);
std::lock_guard<std::mutex> lock(m_Lock);
m_Terminate = true;
m_QueueEvent.Notify();
m_QueueEvent.notify_all();
}
m_Worker->Join();
m_Worker->join();
m_Worker = nullptr;
s_OneTimeThreaderErrorMsg = false;
m_Terminate = false;
}
}
@ -399,27 +397,17 @@ bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
if (!m_Worker)
{
m_Worker = new ke::Thread([this]() -> void {
m_Worker = ke::NewThread("SM Database Worker", [this]() -> void {
Run();
}, "SM SQL Worker");
if (!m_Worker->Succeeded())
{
if (!s_OneTimeThreaderErrorMsg)
{
logger->LogError("[SM] Unable to create db threader (error unknown)");
s_OneTimeThreaderErrorMsg = true;
}
m_Worker = nullptr;
return false;
}
});
}
/* Add to the queue */
{
ke::AutoLock lock(&m_QueueEvent);
std::lock_guard<std::mutex> lock(m_Lock);
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
queue.push(op);
m_QueueEvent.Notify();
m_QueueEvent.notify_one();
}
return true;
@ -450,7 +438,7 @@ void DBManager::Run()
void DBManager::ThreadMain()
{
ke::AutoLock lock(&m_QueueEvent);
std::unique_lock<std::mutex> lock(m_Lock);
while (true) {
// The lock has been acquired. Grab everything we can out of the
@ -458,46 +446,43 @@ void DBManager::ThreadMain()
// we process all operations we can before checking to terminate.
// There's no risk of starvation since the main thread blocks on us
// terminating.
while (true)
{
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
if (queue.empty())
break;
auto queue = &m_OpQueue.GetLikelyQueue();
if (queue->empty()) {
// If the queue is empty and we've been asked to stop, leave now.
if (m_Terminate)
return;
IDBThreadOperation *op = queue.first();
queue.pop();
// Unlock the queue when we run the query, so the main thread can
// keep pumping events. We re-acquire the lock to check for more
// items. It's okay if we terminate while unlocked; the main
// thread would be blocked and we'd need to flush the queue
// anyway, so after we've depleted the queue here, we'll just
// reach the terminate at the top of the loop.
{
ke::AutoUnlock unlock(&m_QueueEvent);
op->RunThreadPart();
ke::AutoLock lock(&m_ThinkLock);
m_ThinkQueue.push(op);
}
if (!m_Terminate)
{
ke::AutoUnlock unlock(&m_QueueEvent);
#ifdef _WIN32
Sleep(20);
#else
usleep(20000);
#endif
}
// Otherwise, wait for something to happen.
m_QueueEvent.wait(lock);
continue;
}
if (m_Terminate)
return;
IDBThreadOperation *op = queue->first();
queue->pop();
// Release the lock and wait for a signal.
m_QueueEvent.Wait();
// Unlock the queue when we run the query, so the main thread can
// keep pumping events. We re-acquire the lock to check for more
// items. It's okay if we terminate while unlocked; the main
// thread would be blocked and we'd need to flush the queue
// anyway, so after we've depleted the queue here, we'll just
// reach the terminate at the top of the loop.
lock.unlock();
op->RunThreadPart();
// Re-acquire the lock and give the data back to the main thread
// immediately. We use a separate lock to minimize game thread
// contention.
{
std::lock_guard<std::mutex> think_lock(m_ThinkLock);
m_ThinkQueue.push(op);
}
// Note that we add a 20ms delay after processing a query. This is
// questionable but the intent is to avoid starving the game thread.
if (!m_Terminate)
std::this_thread::sleep_for(20ms);
lock.lock();
}
}
@ -512,7 +497,7 @@ void DBManager::RunFrame()
/* Dump one thing per-frame so the server stays sane. */
IDBThreadOperation *op;
{
ke::AutoLock lock(&m_ThinkLock);
std::lock_guard<std::mutex> lock(m_ThinkLock);
op = m_ThinkQueue.first();
m_ThinkQueue.pop();
}
@ -591,10 +576,10 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
}
}
ke::AString DBManager::GetDefaultDriverName()
std::string DBManager::GetDefaultDriverName()
{
ConfDbInfoList *list = m_Builder.GetConfigList();
return list->GetDefaultDriver();
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDefaultDriver();
}
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)

View File

@ -38,7 +38,10 @@
#include <sh_list.h>
#include <IThreader.h>
#include <IPluginSys.h>
#include <am-thread-utils.h>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread>
#include "sm_simple_prioqueue.h"
#include <am-refcounting.h>
#include "DatabaseConfBuilder.h"
@ -68,7 +71,7 @@ public: //IDBManager
void AddDriver(IDBDriver *pDrivera);
void RemoveDriver(IDBDriver *pDriver);
const DatabaseInfo *FindDatabaseConf(const char *name);
ConfDbInfo *GetDatabaseConf(const char *name);
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name);
bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength);
unsigned int GetDriverCount();
IDBDriver *GetDriver(unsigned int index);
@ -84,7 +87,7 @@ public: //IPluginsListener
public:
IDBDriver *FindOrLoadDriver(const char *name);
IDBDriver *GetDefaultDriver();
ke::AString GetDefaultDriverName();
std::string GetDefaultDriverName();
bool AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio);
void RunFrame();
inline HandleType_t GetDatabaseType()
@ -101,9 +104,10 @@ private:
PrioQueue<IDBThreadOperation *> m_OpQueue;
Queue<IDBThreadOperation *> m_ThinkQueue;
CVector<bool> m_drSafety; /* which drivers are safe? */
ke::AutoPtr<ke::Thread> m_Worker;
ke::ConditionVariable m_QueueEvent;
ke::Mutex m_ThinkLock;
std::unique_ptr<std::thread> m_Worker;
std::condition_variable m_QueueEvent;
std::mutex m_ThinkLock;
std::mutex m_Lock;
bool m_Terminate;
DatabaseConfBuilder m_Builder;

View File

@ -36,8 +36,8 @@
#define DBPARSE_LEVEL_DATABASE 2
DatabaseConfBuilder::DatabaseConfBuilder()
: m_ParseList(nullptr),
m_InfoList(new ConfDbInfoList())
: m_ParseList(),
m_InfoList()
{
}
@ -50,7 +50,7 @@ DatabaseConfBuilder::~DatabaseConfBuilder()
{
}
ConfDbInfoList *DatabaseConfBuilder::GetConfigList()
ConfDbInfoList &DatabaseConfBuilder::GetConfigList()
{
return m_InfoList;
}
@ -59,9 +59,9 @@ void DatabaseConfBuilder::StartParse()
{
SMCError err;
SMCStates states = {0, 0};
if ((err = textparsers->ParseFile_SMC(m_Filename.chars(), this, &states)) != SMCError_Okay)
if ((err = textparsers->ParseFile_SMC(m_Filename.c_str(), this, &states)) != SMCError_Okay)
{
logger->LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename.chars());
logger->LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename.c_str());
if (err != SMCError_Custom)
{
const char *txt = textparsers->GetSMCErrorString(err);
@ -75,7 +75,7 @@ void DatabaseConfBuilder::ReadSMC_ParseStart()
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_ParseList = new ConfDbInfoList();
m_ParseList.clear();
}
SMCResult DatabaseConfBuilder::ReadSMC_NewSection(const SMCStates *states, const char *name)
@ -116,7 +116,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_KeyValue(const SMCStates *states, const c
{
if (strcmp(key, "driver_default") == 0)
{
m_ParseList->SetDefaultDriver(value);
m_ParseList.SetDefaultDriver(value);
}
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
if (strcmp(key, "driver") == 0)
@ -153,15 +153,15 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
{
m_ParseCurrent->info.driver = m_ParseCurrent->driver.chars();
m_ParseCurrent->info.database = m_ParseCurrent->database.chars();
m_ParseCurrent->info.host = m_ParseCurrent->host.chars();
m_ParseCurrent->info.user = m_ParseCurrent->user.chars();
m_ParseCurrent->info.pass = m_ParseCurrent->pass.chars();
m_ParseCurrent->info.driver = m_ParseCurrent->driver.c_str();
m_ParseCurrent->info.database = m_ParseCurrent->database.c_str();
m_ParseCurrent->info.host = m_ParseCurrent->host.c_str();
m_ParseCurrent->info.user = m_ParseCurrent->user.c_str();
m_ParseCurrent->info.pass = m_ParseCurrent->pass.c_str();
/* Save it.. */
m_ParseCurrent->AddRef();
m_ParseList->append(m_ParseCurrent);
m_ParseList.push_back(m_ParseCurrent);
m_ParseCurrent = nullptr;
/* Go up one level */
@ -176,9 +176,7 @@ SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
void DatabaseConfBuilder::ReadSMC_ParseEnd(bool halted, bool failed)
{
m_InfoList->ReleaseMembers();
delete m_InfoList;
m_InfoList.clear();
m_InfoList = m_ParseList;
m_ParseList = nullptr;
m_ParseList.clear();
}

View File

@ -38,6 +38,7 @@
#include <am-vector.h>
#include <am-string.h>
#include <am-refcounting.h>
#include <am-refcounting-threadsafe.h>
class ConfDbInfo : public ke::RefcountedThreadsafe<ConfDbInfo>
@ -46,58 +47,53 @@ public:
ConfDbInfo() : realDriver(NULL)
{
}
ke::AString name;
ke::AString driver;
ke::AString host;
ke::AString user;
ke::AString pass;
ke::AString database;
std::string name;
std::string driver;
std::string host;
std::string user;
std::string pass;
std::string database;
IDBDriver *realDriver;
DatabaseInfo info;
};
class ConfDbInfoList : public ke::Vector<ConfDbInfo *>
class ConfDbInfoList : public std::vector<ke::RefPtr<ConfDbInfo>>
{
/* Allow internal usage of ConfDbInfoList */
friend class DBManager;
friend class DatabaseConfBuilder;
private:
ke::AString& GetDefaultDriver() {
std::string& GetDefaultDriver() {
return m_DefDriver;
}
ConfDbInfo *GetDatabaseConf(const char *name) {
for (size_t i = 0; i < this->length(); i++)
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name) {
for (size_t i = 0; i < this->size(); i++)
{
ConfDbInfo *current = this->at(i);
ke::RefPtr<ConfDbInfo> current = this->at(i);
/* If we run into the default configuration, then we'll save it
* for the next call to GetDefaultConfiguration */
if (strcmp(current->name.chars(), "default") == 0)
if (strcmp(current->name.c_str(), "default") == 0)
{
m_DefaultConfig = current;
}
if (strcmp(current->name.chars(), name) == 0)
if (strcmp(current->name.c_str(), name) == 0)
{
return current;
}
}
return nullptr;
}
ConfDbInfo *GetDefaultConfiguration() {
ke::RefPtr<ConfDbInfo> GetDefaultConfiguration() {
return m_DefaultConfig;
}
void SetDefaultDriver(const char *input) {
m_DefDriver = ke::AString(input);
}
void ReleaseMembers() {
for (size_t i = 0; i < this->length(); i++) {
ConfDbInfo *current = this->at(i);
current->Release();
}
m_DefDriver = std::string(input);
}
private:
ConfDbInfo *m_DefaultConfig;
ke::AString m_DefDriver;
ke::RefPtr<ConfDbInfo> m_DefaultConfig;
std::string m_DefDriver;
};
@ -108,7 +104,7 @@ public:
~DatabaseConfBuilder();
void StartParse();
void SetPath(char* path);
ConfDbInfoList *GetConfigList();
ConfDbInfoList &GetConfigList();
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
@ -120,10 +116,10 @@ private:
unsigned int m_ParseLevel;
unsigned int m_ParseState;
ConfDbInfo *m_ParseCurrent;
ConfDbInfoList *m_ParseList;
ConfDbInfoList m_ParseList;
private:
ke::AString m_Filename;
ConfDbInfoList *m_InfoList;
std::string m_Filename;
ConfDbInfoList m_InfoList;
};
#endif //_INCLUDE_DATABASE_CONF_BUILDER_H_

View File

@ -69,13 +69,13 @@ void DebugReport::GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err,
ke::SafeVsprintf(buffer, sizeof(buffer), message, ap);
const char *plname = pluginsys->FindPluginByContext(ctx->GetContext())->GetFilename();
const char *error = g_pSourcePawn2->GetErrorString(err);
if (error)
{
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
} else {
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
if (err >= 0) {
const char *error = g_pSourcePawn2->GetErrorString(err);
if (error)
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
else
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
}
g_Logger.LogError("[SM] %s", buffer);
@ -194,22 +194,22 @@ void DebugReport::ReportError(const IErrorReport &report, IFrameIterator &iter)
g_Logger.LogError("[SM] Blaming: %s", blame);
}
ke::Vector<ke::AString> arr = GetStackTrace(&iter);
for (size_t i = 0; i < arr.length(); i++)
std::vector<std::string> arr = GetStackTrace(&iter);
for (size_t i = 0; i < arr.size(); i++)
{
g_Logger.LogError("%s", arr[i].chars());
g_Logger.LogError("%s", arr[i].c_str());
}
}
ke::Vector<ke::AString> DebugReport::GetStackTrace(IFrameIterator *iter)
std::vector<std::string> DebugReport::GetStackTrace(IFrameIterator *iter)
{
char temp[3072];
ke::Vector<ke::AString> trace;
std::vector<std::string> trace;
iter->Reset();
if (!iter->Done())
{
trace.append("[SM] Call stack trace:");
trace.push_back("[SM] Call stack trace:");
for (int index = 0; !iter->Done(); iter->Next(), index++)
{
@ -221,7 +221,7 @@ ke::Vector<ke::AString> DebugReport::GetStackTrace(IFrameIterator *iter)
if (iter->IsNativeFrame())
{
g_pSM->Format(temp, sizeof(temp), "[SM] [%d] %s", index, fn);
trace.append(temp);
trace.push_back(temp);
continue;
}
if (iter->IsScriptedFrame())
@ -237,7 +237,7 @@ ke::Vector<ke::AString> DebugReport::GetStackTrace(IFrameIterator *iter)
file,
fn);
trace.append(temp);
trace.push_back(temp);
}
}
}

View File

@ -47,10 +47,12 @@ public: // IDebugListener
void ReportError(const IErrorReport &report, IFrameIterator &iter);
void OnDebugSpew(const char *msg, ...);
public:
// If err is -1, caller assumes the automatic reporting by SourcePawn is
// good enough, and only wants the supplemental logging provided here.
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
void GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err, const char *message, va_list ap);
void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...);
ke::Vector<ke::AString> GetStackTrace(IFrameIterator *iter);
std::vector<std::string> GetStackTrace(IFrameIterator *iter);
private:
int _GetPluginIndex(IPluginContext *ctx);
};

View File

@ -30,6 +30,9 @@
*/
#include <stdlib.h>
#include <memory>
#include "ExtensionSys.h"
#include <ILibrarySys.h>
#include <ISourceMod.h>
@ -76,7 +79,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s." PLATFORM_LIB_EXT,
filename);
}
else
@ -85,7 +88,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.%s." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s.%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
@ -100,7 +103,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.2.ep2v." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s.2.ep2v." PLATFORM_LIB_EXT,
filename);
}
else if (strcmp(bridge->gamesuffix, "2.nd") == 0)
@ -108,7 +111,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.2.l4d2." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s.2.l4d2." PLATFORM_LIB_EXT,
filename);
}
@ -119,7 +122,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/auto.%s/%s." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "auto.%s/%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
@ -129,7 +132,7 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s." PLATFORM_LIB_EXT,
filename);
}
}
@ -237,6 +240,8 @@ void CLocalExtension::Unload()
m_pLib->CloseLibrary();
m_pLib = NULL;
}
m_bFullyLoaded = false;
}
bool CRemoteExtension::Reload(char *error, size_t maxlength)
@ -301,7 +306,7 @@ bool CExtension::Load(char *error, size_t maxlength)
/* Check if we're past load time */
if (!bridge->IsMapLoading())
{
m_pAPI->OnExtensionsAllLoaded();
MarkAllLoaded();
}
return true;
@ -390,11 +395,6 @@ void CExtension::AddDependency(const IfaceInfo *pInfo)
}
}
bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2)
{
return (i1.iface == i2.iface) && (i1.owner == i2.owner);
}
void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
{
IfaceInfo info;
@ -496,7 +496,7 @@ void CExtensionManager::TryAutoload()
g_pSM->BuildPath(Path_SM, path, sizeof(path), "extensions");
ke::AutoPtr<IDirectory> pDir(libsys->OpenDirectory(path));
std::unique_ptr<IDirectory> pDir(libsys->OpenDirectory(path));
if (!pDir)
return;
@ -628,9 +628,15 @@ IExtension *CExtensionManager::FindExtensionByName(const char *ext)
IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
{
if (strstr(file, "..") != NULL)
{
ke::SafeStrcpy(error, maxlength, "Cannot load extensions outside the \"extensions\" folder.");
return NULL;
}
/* Remove platform extension if it's there. Compat hack. */
const char *ext = libsys->GetFileExtension(file);
if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
if (ext && strcmp(ext, PLATFORM_LIB_EXT) == 0)
{
char path2[PLATFORM_MAX_PATH];
ke::SafeStrcpy(path2, sizeof(path2), file);
@ -1171,7 +1177,6 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
rootmenu->ConsolePrint(" -> %s", pPlugin->GetFilename());
}
}
srand(static_cast<int>(time(NULL)));
pExt->unload_code = (rand() % 877) + 123; //123 to 999
rootmenu->ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
rootmenu->ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
@ -1364,7 +1369,7 @@ bool CLocalExtension::IsSameFile(const char *file)
bool CRemoteExtension::IsSameFile(const char *file)
{
/* :TODO: this could be better, but no one uses this API anyway. */
return strcmp(file, m_Path.c_str()) == 0;
/* Check full path and name passed in from LoadExternal */
return strcmp(file, m_Path.c_str()) == 0 || strcmp(file, m_File.c_str()) == 0;
}

View File

@ -61,7 +61,7 @@ IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned
{
scripts->AddFunctionsToForward(name, fwd);
m_managed.append(fwd);
m_managed.push_back(fwd);
}
return fwd;
@ -78,7 +78,7 @@ IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType
if (fwd)
{
m_unmanaged.append(fwd);
m_unmanaged.push_back(fwd);
}
return fwd;
@ -751,9 +751,9 @@ bool CForward::AddFunction(IPluginFunction *func)
return false;
if (func->IsRunnable())
m_functions.append(func);
m_functions.push_back(func);
else
m_paused.append(func);
m_paused.push_back(func);
return true;
}
@ -780,7 +780,7 @@ const char *CForward::GetForwardName()
unsigned int CForward::GetFunctionCount()
{
return m_functions.length();
return m_functions.size();
}
ExecType CForward::GetExecType()

View File

@ -36,7 +36,7 @@ SafeFrameIterator::SafeFrameIterator(IFrameIterator *it)
while (!it->Done())
{
FrameInfo info = FrameInfo(it);
frames.append(info);
frames.push_back(info);
it->Next();
}
@ -46,7 +46,7 @@ SafeFrameIterator::SafeFrameIterator(IFrameIterator *it)
bool SafeFrameIterator::Done() const
{
return current >= frames.length();
return current >= frames.size();
}
bool SafeFrameIterator::Next()
@ -77,7 +77,7 @@ const char *SafeFrameIterator::FunctionName() const
return NULL;
}
return frames[current].FunctionName.chars();
return frames[current].FunctionName.c_str();
}
const char *SafeFrameIterator::FilePath() const
@ -87,5 +87,5 @@ const char *SafeFrameIterator::FilePath() const
return NULL;
}
return frames[current].FilePath.chars();
return frames[current].FilePath.c_str();
}

View File

@ -48,16 +48,18 @@ public:
*/
struct FrameInfo
{
ke::AString FunctionName;
ke::AString FilePath;
std::string FunctionName;
std::string FilePath;
unsigned LineNumber;
FrameInfo(IFrameIterator *it)
{
LineNumber = it->LineNumber();
FunctionName = it->FunctionName();
FilePath = it->FilePath();
if (it->FunctionName())
FunctionName = it->FunctionName();
if (it->FilePath())
FilePath = it->FilePath();
}
};
@ -73,5 +75,5 @@ public:
private:
size_t current;
ke::Vector<FrameInfo> frames;
};
std::vector<FrameInfo> frames;
};

View File

@ -30,6 +30,7 @@
#include "common_logic.h"
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include <sh_list.h>
#include <sh_string.h>
#include "GameConfigs.h"
@ -46,6 +47,7 @@
#include <am-string.h>
#include <bridge/include/ILogger.h>
#include <bridge/include/CoreProvider.h>
#include <bridge/include/IFileSystemBridge.h>
#if defined PLATFORM_POSIX
#include <dlfcn.h>
@ -85,15 +87,12 @@ static const char *g_pParseEngine = NULL;
#if defined PLATFORM_WINDOWS
#define PLATFORM_NAME "windows" PLATFORM_ARCH_SUFFIX
#define PLATFORM_SERVER_BINARY "server.dll"
#elif defined PLATFORM_LINUX
#define PLATFORM_NAME "linux" PLATFORM_ARCH_SUFFIX
#define PLATFORM_COMPAT_ALT "mac" PLATFORM_ARCH_SUFFIX /* Alternate platform name if game data is missing for primary one */
#define PLATFORM_SERVER_BINARY "server_i486.so"
#elif defined PLATFORM_APPLE
#define PLATFORM_NAME "mac" PLATFORM_ARCH_SUFFIX
#define PLATFORM_COMPAT_ALT "linux" PLATFORM_ARCH_SUFFIX
#define PLATFORM_SERVER_BINARY "server.dylib"
#endif
struct TempSigInfo
@ -106,8 +105,6 @@ struct TempSigInfo
char sig[1024];
char library[64];
} s_TempSig;
unsigned int s_ServerBinCRC;
bool s_ServerBinCRC_Ok = false;
static bool DoesGameMatch(const char *value)
{
@ -153,6 +150,23 @@ static inline bool IsPlatformCompatible(const char *platform, bool *hadPrimaryMa
return false;
}
static inline time_t GetFileModTime(const char *path)
{
char filepath[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_SM, filepath, sizeof(filepath), "gamedata/%s.txt", path);
#ifdef PLATFORM_WINDOWS
struct _stat64 s;
if (_stat64(filepath, &s) != 0)
#elif defined PLATFORM_POSIX
struct stat s;
if (stat(filepath, &s) != 0)
#endif
{
return 0;
}
return s.st_mtime;
}
CGameConfig::CGameConfig(const char *file, const char *engine)
{
strncopy(m_File, file, sizeof(m_File));
@ -160,6 +174,8 @@ CGameConfig::CGameConfig(const char *file, const char *engine)
m_CustomLevel = 0;
m_CustomHandler = NULL;
m_ModTime = GetFileModTime(file);
if (!engine)
m_pEngine = bridge->GetSourceEngineName();
else
@ -206,7 +222,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
{
bShouldBeReadingDefault = true;
m_ParseState = PSTATE_GAMEDEFS;
strncopy(m_Game, name, sizeof(m_Game));
m_Game = name;
} else {
m_IgnoreLevel++;
}
@ -222,7 +238,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
{
m_ParseState = PSTATE_GAMEDEFS_KEYS;
}
else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
else if ((strcmp(name, "#supported") == 0) && (m_Game == "#default"))
{
m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
/* Ignore this section unless we get a game. */
@ -261,23 +277,23 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
}
case PSTATE_GAMEDEFS_KEYS:
{
strncopy(m_Key, name, sizeof(m_Key));
m_Key = name;
m_ParseState = PSTATE_GAMEDEFS_KEYS_PLATFORM;
matched_platform = false;
break;
}
case PSTATE_GAMEDEFS_OFFSETS:
{
m_Prop[0] = '\0';
m_Class[0] = '\0';
strncopy(m_offset, name, sizeof(m_offset));
m_Prop.clear();
m_Class.clear();
m_offset = name;
m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
matched_platform = false;
break;
}
case PSTATE_GAMEDEFS_SIGNATURES:
{
strncopy(m_offset, name, sizeof(m_offset));
m_offset = name;
s_TempSig.Reset();
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
matched_platform = false;
@ -287,39 +303,25 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
{
char error[255];
error[0] = '\0';
if (strcmp(name, "server") != 0)
GameBinaryInfo binInfo;
if (!g_GameConfigs.TryGetGameBinaryInfo(name, &binInfo))
{
ke::SafeSprintf(error, sizeof(error), "Unrecognized library \"%s\"", name);
}
else if (!s_ServerBinCRC_Ok)
else if (!binInfo.m_crcOK)
{
FILE *fp;
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
if ((fp = fopen(path, "rb")) == NULL)
{
ke::SafeSprintf(error, sizeof(error), "Could not open binary: %s", path);
} else {
size_t size;
void *buffer;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = malloc(size);
fread(buffer, size, 1, fp);
s_ServerBinCRC = UTIL_CRC32(buffer, size);
free(buffer);
s_ServerBinCRC_Ok = true;
fclose(fp);
}
ke::SafeSprintf(error, sizeof(error), "Could not get CRC for binary: %s", name);
}
else
{
bCurrentBinCRC_Ok = binInfo.m_crcOK;
bCurrentBinCRC = binInfo.m_crc;
}
if (error[0] != '\0')
{
m_IgnoreLevel = 1;
logger->LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
logger->LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game.c_str(), m_CurFile);
logger->LogError("[SM] %s", error);
} else {
m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
@ -334,12 +336,12 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
}
case PSTATE_GAMEDEFS_ADDRESSES:
{
m_Address[0] = '\0';
m_AddressSignature[0] = '\0';
m_Address.clear();
m_AddressSignature.clear();
m_AddressReadCount = 0;
m_AddressLastIsOffset = false;
strncopy(m_Address, name, sizeof(m_Address));
m_Address = name;
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS;
break;
@ -355,7 +357,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
if (strcmp(name, "linux") != 0 && strcmp(name, "windows") != 0 && strcmp(name, "mac") != 0 &&
strcmp(name, "linux64") != 0 && strcmp(name, "windows64") != 0 && strcmp(name, "mac64") != 0)
{
logger->LogError("[SM] Error while parsing Address section for \"%s\" (%s):", m_Address, m_CurFile);
logger->LogError("[SM] Error while parsing Address section for \"%s\" (%s):", m_Address.c_str(), m_CurFile);
logger->LogError("[SM] Unrecognized platform \"%s\"", name);
}
m_IgnoreLevel = 1;
@ -392,21 +394,21 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
{
if (strcmp(key, "class") == 0)
{
strncopy(m_Class, value, sizeof(m_Class));
m_Class = value;
} else if (strcmp(key, "prop") == 0) {
strncopy(m_Prop, value, sizeof(m_Prop));
m_Prop = value;
} else if (IsPlatformCompatible(key, &matched_platform)) {
m_Offsets.replace(m_offset, atoi(value));
m_Offsets.replace(m_offset.c_str(), static_cast<int>(strtol(value, NULL, 0)));
}
} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
ke::AString vstr(value);
m_Keys.replace(key, ke::Move(vstr));
std::string vstr(value);
m_Keys.replace(key, std::move(vstr));
}
else if (m_ParseState == PSTATE_GAMEDEFS_KEYS_PLATFORM) {
if (IsPlatformCompatible(key, &matched_platform))
{
ke::AString vstr(value);
m_Keys.replace(m_Key, ke::Move(vstr));
std::string vstr(value);
m_Keys.replace(m_Key.c_str(), std::move(vstr));
}
} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
if (strcmp(key, "game") == 0)
@ -442,12 +444,12 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
}
} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
if (DoesPlatformMatch(key)
&& s_ServerBinCRC_Ok
&& bCurrentBinCRC_Ok
&& !bShouldBeReadingDefault)
{
unsigned int crc = 0;
sscanf(value, "%08X", &crc);
if (s_ServerBinCRC == crc)
if (bCurrentBinCRC == crc)
{
bShouldBeReadingDefault = true;
}
@ -457,7 +459,7 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
int limit = sizeof(m_AddressRead)/sizeof(m_AddressRead[0]);
if (m_AddressLastIsOffset)
{
logger->LogError("[SM] Error parsing Address \"%s\", 'offset' entry must be the last entry (gameconf \"%s\")", m_Address, m_CurFile);
logger->LogError("[SM] Error parsing Address \"%s\", 'offset' entry must be the last entry (gameconf \"%s\")", m_Address.c_str(), m_CurFile);
}
else if (m_AddressReadCount < limit)
{
@ -465,15 +467,15 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
{
m_AddressLastIsOffset = true;
}
m_AddressRead[m_AddressReadCount] = atoi(value);
m_AddressRead[m_AddressReadCount] = static_cast<int>(strtol(value, NULL, 0));
m_AddressReadCount++;
}
else
{
logger->LogError("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", m_Address, limit, m_CurFile);
logger->LogError("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", m_Address.c_str(), limit, m_CurFile);
}
} else if (strcmp(key, "signature") == 0) {
strncopy(m_AddressSignature, value, sizeof(m_AddressSignature));
m_AddressSignature = value;
}
} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
@ -529,25 +531,24 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
{
/* Parse the offset... */
if (m_Class[0] != '\0'
&& m_Prop[0] != '\0')
if (!m_Class.empty() && !m_Prop.empty())
{
SendProp *pProp = gamehelpers->FindInSendTable(m_Class, m_Prop);
SendProp *pProp = gamehelpers->FindInSendTable(m_Class.c_str(), m_Prop.c_str());
if (pProp)
{
int val = gamehelpers->GetSendPropOffset(pProp);
m_Offsets.replace(m_offset, val);
m_Props.replace(m_offset, pProp);
m_Offsets.replace(m_offset.c_str(), val);
m_Props.replace(m_offset.c_str(), pProp);
} else {
/* Check if it's a non-default game and no offsets exist */
if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
&& (!m_Offsets.retrieve(m_offset)))
if ((m_Game != "*" && m_Game != "#default")
&& (!m_Offsets.retrieve(m_offset.c_str())))
{
logger->LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
m_Class,
m_Prop,
m_Class.c_str(),
m_Prop.c_str(),
m_CurFile,
m_Game);
m_Game.c_str());
}
}
}
@ -585,14 +586,12 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
}
void *addrInBase = NULL;
if (strcmp(s_TempSig.library, "server") == 0)
GameBinaryInfo binInfo;
if (g_GameConfigs.TryGetGameBinaryInfo(s_TempSig.library, &binInfo))
{
addrInBase = bridge->serverFactory;
} else if (strcmp(s_TempSig.library, "engine") == 0) {
addrInBase = bridge->engineFactory;
} else if (strcmp(s_TempSig.library, "matchmaking_ds") == 0) {
addrInBase = bridge->matchmakingDSFactory;
addrInBase = binInfo.m_pAddr;
}
void *final_addr = NULL;
if (addrInBase == NULL)
{
@ -655,7 +654,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
}
}
m_Sigs.replace(m_offset, final_addr);
m_Sigs.replace(m_offset.c_str(), final_addr);
}
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
@ -671,10 +670,10 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
{
m_ParseState = PSTATE_GAMEDEFS_ADDRESSES;
if (m_Address[0] != '\0' && m_AddressSignature[0] != '\0')
if (!m_Address.empty() && !m_AddressSignature.empty())
{
AddressConf addrConf(m_AddressSignature, sizeof(m_AddressSignature), m_AddressReadCount, m_AddressRead, m_AddressLastIsOffset);
m_Addresses.replace(m_Address, addrConf);
AddressConf addrConf(std::move(m_AddressSignature), m_AddressReadCount, m_AddressRead, m_AddressLastIsOffset);
m_Addresses.replace(m_Address.c_str(), addrConf);
}
break;
@ -792,7 +791,10 @@ public:
(!had_game && matched_engine) ||
(matched_engine && matched_game))
{
fileList->push_back(cur_file);
if (fileList->find(cur_file) == fileList->end())
{
fileList->push_back(cur_file);
}
}
state = MSTATE_MAIN;
}
@ -1001,10 +1003,10 @@ bool CGameConfig::GetOffset(const char *key, int *value)
const char *CGameConfig::GetKeyValue(const char *key)
{
StringHashMap<ke::AString>::Result r = m_Keys.find(key);
StringHashMap<std::string>::Result r = m_Keys.find(key);
if (!r.found())
return NULL;
return r->value.chars();
return r->value.c_str();
}
//memory addresses below 0x10000 are automatically considered invalid for dereferencing
@ -1022,7 +1024,7 @@ bool CGameConfig::GetAddress(const char *key, void **retaddr)
AddressConf &addrConf = r->value;
void *addr;
if (!GetMemSig(addrConf.signatureName, &addr))
if (!GetMemSig(addrConf.signatureName.c_str(), &addr))
{
*retaddr = NULL;
return false;
@ -1055,11 +1057,11 @@ static inline unsigned minOf(unsigned a, unsigned b)
return a <= b ? a : b;
}
CGameConfig::AddressConf::AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read, bool lastIsOffset)
CGameConfig::AddressConf::AddressConf(std::string&& sigName, unsigned readCount, int *read, bool lastIsOffset)
{
unsigned readLimit = minOf(readCount, sizeof(this->read) / sizeof(this->read[0]));
strncopy(signatureName, sigName, sizeof(signatureName) / sizeof(signatureName[0]));
this->signatureName = std::move(sigName);
this->readCount = readLimit;
memcpy(&this->read[0], read, sizeof(this->read[0])*readLimit);
@ -1081,6 +1083,42 @@ bool CGameConfig::GetMemSig(const char *key, void **addr)
return m_Sigs.retrieve(key, addr);
}
void GameBinPathManager::Init()
{
char search_path[PLATFORM_MAX_PATH * 8];
bridge->filesystem->GetSearchPath("GAMEBIN", false, search_path, sizeof(search_path));
char addons_folder[12];
ke::SafeSprintf(addons_folder, sizeof(addons_folder), "%caddons%c", PLATFORM_SEP_CHAR, PLATFORM_SEP_CHAR);
std::istringstream iss(search_path);
for (std::string path; std::getline(iss, path, ';');)
{
if (path.length() > 0
&& path.find(addons_folder) == std::string::npos
&& m_lookup.find(path) == m_lookup.cend()
)
{
m_lookup.insert(path);
m_ordered.push_back(path);
}
}
iss.clear();
bridge->filesystem->GetSearchPath("EXECUTABLE_PATH", false, search_path, sizeof(search_path));
iss.str(search_path);
for (std::string path; std::getline(iss, path, ';');)
{
if (m_lookup.find(path) == m_lookup.cend())
{
m_lookup.insert(path);
m_ordered.push_back(path);
}
}
}
GameConfigManager::GameConfigManager()
{
}
@ -1091,6 +1129,8 @@ GameConfigManager::~GameConfigManager()
void GameConfigManager::OnSourceModStartup(bool late)
{
m_gameBinPathManager.Init();
LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
strncopy(g_Game, g_pSM->GetGameFolderName(), sizeof(g_Game));
@ -1132,9 +1172,17 @@ bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pCon
if (m_Lookup.retrieve(file, &pConfig))
{
bool ret = true;
time_t modtime = GetFileModTime(file);
if (pConfig->m_ModTime != modtime)
{
pConfig->m_ModTime = modtime;
ret = pConfig->Reparse(error, maxlength);
}
pConfig->AddRef();
*_pConfig = pConfig;
return true;
return ret;
}
pConfig = new CGameConfig(file);
@ -1205,3 +1253,75 @@ void GameConfigManager::RemoveCachedConfig(CGameConfig *config)
{
m_Lookup.remove(config->m_File);
}
void GameConfigManager::CacheGameBinaryInfo(const char* pszName)
{
GameBinaryInfo info;
char name[64];
bridge->FormatSourceBinaryName(pszName, name, sizeof(name));
char binary_path[PLATFORM_MAX_PATH];
for (auto it = m_gameBinPathManager.Paths().begin(); it != m_gameBinPathManager.Paths().end(); ++it)
{
ke::SafeSprintf(binary_path, sizeof(binary_path), "%s%s%s", it->c_str(), it->back() == PLATFORM_SEP_CHAR ? "" : PLATFORM_SEP, name);
#if defined PLATFORM_WINDOWS
HMODULE hModule = LoadLibraryA(binary_path);
if (hModule)
{
info.m_pAddr = GetProcAddress(hModule, "CreateInterface");
FreeLibrary(hModule);
}
#else
void *pHandle = dlopen(binary_path, RTLD_NOW);
if (pHandle)
{
info.m_pAddr = dlsym(pHandle, "CreateInterface");
dlclose(pHandle);
}
#endif
if (info.m_pAddr)
break;
}
// Don't bother trying to get CRC if we couldn't find the bin loaded
if (info.m_pAddr)
{
FILE *fp;
if ((fp = fopen(binary_path, "rb")) == 0)
{
info.m_crc = 0;
}
else
{
size_t size;
void* buffer;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = malloc(size);
fread(buffer, size, 1, fp);
info.m_crc = UTIL_CRC32(buffer, size);
free(buffer);
info.m_crcOK = true;
fclose(fp);
}
}
// But insert regardless, to cache the first lookup (even as failed)
m_gameBinInfos.insert(pszName, info);
}
bool GameConfigManager::TryGetGameBinaryInfo(const char* pszName, GameBinaryInfo* pDest)
{
if (m_gameBinInfos.retrieve(pszName, pDest))
return pDest->m_pAddr != nullptr;
CacheGameBinaryInfo(pszName);
return m_gameBinInfos.retrieve(pszName, pDest);
}

View File

@ -38,6 +38,7 @@
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include <sm_namehashset.h>
#include <unordered_set>
using namespace SourceMod;
@ -81,16 +82,18 @@ private:
char m_CurFile[PLATFORM_MAX_PATH];
StringHashMap<int> m_Offsets;
StringHashMap<SendProp *> m_Props;
StringHashMap<ke::AString> m_Keys;
StringHashMap<std::string> m_Keys;
StringHashMap<void *> m_Sigs;
/* Parse states */
int m_ParseState;
unsigned int m_IgnoreLevel;
char m_Class[64];
char m_Prop[64];
char m_offset[64];
char m_Game[256];
char m_Key[64];
std::string m_Class;
std::string m_Prop;
std::string m_offset;
std::string m_Game;
std::string m_Key;
unsigned int bCurrentBinCRC;
bool bCurrentBinCRC_Ok = false;
bool bShouldBeReadingDefault;
bool had_game;
bool matched_game;
@ -105,24 +108,49 @@ private:
/* Support for reading Addresses */
struct AddressConf
{
char signatureName[64];
std::string signatureName;
int readCount;
int read[8];
bool lastIsOffset;
AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read, bool lastIsOffset);
AddressConf(std::string&& sigName, unsigned readCount, int *read, bool lastIsOffset);
AddressConf() {}
};
char m_Address[64];
char m_AddressSignature[64];
std::string m_Address;
std::string m_AddressSignature;
int m_AddressReadCount;
int m_AddressRead[8];
bool m_AddressLastIsOffset;
StringHashMap<AddressConf> m_Addresses;
const char *m_pEngine;
const char *m_pBaseEngine;
time_t m_ModTime;
};
struct GameBinaryInfo
{
void *m_pAddr = nullptr;
uint32_t m_crc = 0;
bool m_crcOK = false;
};
class GameBinPathManager
{
public:
GameBinPathManager() {}
~GameBinPathManager() {}
public:
void Init();
inline const std::vector<std::string>& Paths() const
{
return m_ordered;
}
private:
std::unordered_set<std::string> m_lookup;
std::vector<std::string> m_ordered;
};
class GameConfigManager :
@ -147,11 +175,16 @@ public: //SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModAllShutdown();
public:
bool TryGetGameBinaryInfo(const char* pszName, GameBinaryInfo* pDest);
void RemoveCachedConfig(CGameConfig *config);
private:
void CacheGameBinaryInfo(const char* pszName);
private:
NameHashSet<CGameConfig *> m_Lookup;
StringHashMap<GameBinaryInfo> m_gameBinInfos;
public:
StringHashMap<ITextListener_SMC *> m_customHandlers;
GameBinPathManager m_gameBinPathManager;
};
extern GameConfigManager g_GameConfigs;

View File

@ -30,6 +30,7 @@
*/
#include "HandleSys.h"
#include <time.h>
#include <assert.h>
#include <string.h>
#include "common_logic.h"
@ -38,10 +39,20 @@
#include "PluginSys.h"
#include <am-string.h>
#include <bridge/include/ILogger.h>
#include <bridge/include/CoreProvider.h>
#include <ISourceMod.h>
#include "sm_platform.h"
#ifdef PLATFORM_WINDOWS
#include "sm_invalidparamhandler.h"
#endif
using namespace std::string_literals;
HandleSystem g_HandleSys;
QHandle *ignore_handle;
extern ConVar *g_datetime_format;
inline HandleType_t TypeParent(HandleType_t type)
{
@ -206,7 +217,7 @@ HandleType_t HandleSystem::CreateType(const char *name,
pType->dispatch = dispatch;
if (name && name[0] != '\0')
{
pType->name = new ke::AString(name);
pType->name = std::make_unique<std::string>(name);
m_TypeLookup.insert(name, pType);
}
@ -290,6 +301,28 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
}
}
if (owner)
{
owner->num_handles++;
if (!owner->warned_handle_usage && owner->num_handles >= HANDLESYS_WARN_USAGE)
{
owner->warned_handle_usage = true;
std::string path = "<unknown>";
if (auto plugin = scripts->FindPluginByIdentity(owner))
{
path = "plugin "s + plugin->GetFilename();
}
else if (auto ext = g_Extensions.GetExtensionFromIdent(owner))
{
path = "extension "s + ext->GetFilename();
}
logger->LogError("[SM] Warning: %s is using more than %d handles!",
path.c_str(), HANDLESYS_WARN_USAGE);
}
}
QHandle *pHandle = &m_Handles[handle];
assert(pHandle->set == false);
@ -311,7 +344,7 @@ HandleError HandleSystem::MakePrimHandle(HandleType_t type,
/* Create the hash value */
Handle_t hash = pHandle->serial;
hash <<= 16;
hash <<= HANDLESYS_HANDLE_BITS;
hash |= handle;
/* Add a reference count to the type */
@ -430,7 +463,7 @@ Handle_t HandleSystem::CreateHandleInt(HandleType_t type,
pHandle->object = object;
pHandle->clone = 0;
pHandle->timestamp = g_pSM->GetAdjustedTime();
return handle;
}
@ -475,7 +508,7 @@ HandleError HandleSystem::GetHandle(Handle_t handle,
unsigned int *in_index,
bool ignoreFree)
{
unsigned int serial = (handle >> 16);
unsigned int serial = (handle >> HANDLESYS_HANDLE_BITS);
unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES)
@ -631,7 +664,7 @@ Handle_t HandleSystem::FastCloneHandle(Handle_t hndl)
void HandleSystem::GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index)
{
#ifndef NDEBUG
unsigned int serial = (hndl >> 16);
unsigned int serial = (hndl >> HANDLESYS_HANDLE_BITS);
#endif
index = (hndl & HANDLESYS_HANDLE_MASK);
@ -655,6 +688,9 @@ HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
QHandleType *pType = &m_Types[pHandle->type];
if (pHandle->owner && pHandle->owner->num_handles > 0)
pHandle->owner->num_handles--;
if (pHandle->clone)
{
/* If we're a clone, decrease the parent reference count */
@ -925,7 +961,7 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
/* Remove it from the type cache. */
if (pType->name)
m_TypeLookup.remove(pType->name->chars());
m_TypeLookup.remove(pType->name->c_str());
return true;
}
@ -1014,6 +1050,8 @@ bool HandleSystem::TryAndFreeSomeHandles()
unsigned int * pCount = new unsigned int[HANDLESYS_TYPEARRAY_SIZE+1];
memset(pCount, 0, ((HANDLESYS_TYPEARRAY_SIZE + 1) * sizeof(unsigned int)));
const QHandle *oldest = nullptr;
const QHandle *newest = nullptr;
for (unsigned int i = 1; i <= m_HandleTail; ++i)
{
const QHandle &Handle = m_Handles[i];
@ -1030,6 +1068,15 @@ bool HandleSystem::TryAndFreeSomeHandles()
highest_index = ((Handle.type) + 1);
}
if (!oldest || oldest->timestamp > Handle.timestamp)
{
oldest = &Handle;
}
if (!newest || newest->timestamp < Handle.timestamp)
{
newest = &Handle;
}
if (Handle.clone != 0)
{
continue;
@ -1051,13 +1098,39 @@ bool HandleSystem::TryAndFreeSomeHandles()
}
if (m_Types[i].name)
pTypeName = m_Types[i].name->chars();
pTypeName = m_Types[i].name->c_str();
else
pTypeName = "ANON";
HANDLE_LOG_VERY_BAD("Type\t%-20.20s|\tCount\t%u", pTypeName, pCount[i]);
}
const char *fmt = bridge->GetCvarString(g_datetime_format);
char oldstamp[256], newstamp[256]; // 256 should be more than enough
// scope for InvalidParameterHandler
{
#ifdef PLATFORM_WINDOWS
InvalidParameterHandler p;
#endif
size_t written = strftime(oldstamp, sizeof(oldstamp), fmt, localtime(&oldest->timestamp));
if (!written)
{
ke::SafeStrcpy(oldstamp, sizeof(oldstamp), "INVALID");
}
written = strftime(newstamp, sizeof(newstamp), fmt, localtime(&newest->timestamp));
if (!written)
{
ke::SafeStrcpy(newstamp, sizeof(newstamp), "INVALID");
}
}
HANDLE_LOG_VERY_BAD("--------------------------------------------------------------------------");
HANDLE_LOG_VERY_BAD("Oldest Living Handle: %s created at %s", m_Types[oldest->type].name->c_str(), oldstamp);
HANDLE_LOG_VERY_BAD("Newest Living Handle: %s created at %s", m_Types[newest->type].name->c_str(), newstamp);
HANDLE_LOG_VERY_BAD("-- Approximately %d bytes of memory are in use by (%u) Handles.\n", total_size, total);
delete [] pCount;
@ -1081,8 +1154,10 @@ static void rep(const HandleReporter &fn, const char *fmt, ...)
void HandleSystem::Dump(const HandleReporter &fn)
{
unsigned int total_size = 0;
rep(fn, "%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory");
rep(fn, "--------------------------------------------------------------------------");
rep(fn, "%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s\t%-30.30s", "Handle", "Owner", "Type", "Memory", "Time Created");
rep(fn, "---------------------------------------------------------------------------------------------");
const char *fmt = bridge->GetCvarString(g_datetime_format);
for (unsigned int i = 1; i <= m_HandleTail; i++)
{
if (m_Handles[i].set != HandleSet_Used)
@ -1090,12 +1165,12 @@ void HandleSystem::Dump(const HandleReporter &fn)
continue;
}
/* Get the index */
unsigned int index = (m_Handles[i].serial << 16) | i;
unsigned int index = (m_Handles[i].serial << HANDLESYS_HANDLE_BITS) | i;
/* Determine the owner */
const char *owner = "UNKNOWN";
if (m_Handles[i].owner)
{
IdentityToken_t *pOwner = m_Handles[i].owner;
IdentityToken_t *pOwner = m_Handles[i].owner;
if (pOwner == g_pCoreIdent)
{
owner = "CORE";
@ -1131,7 +1206,7 @@ void HandleSystem::Dump(const HandleReporter &fn)
unsigned int parentIdx;
bool bresult;
if (pType->name)
type = pType->name->chars();
type = pType->name->c_str();
if ((parentIdx = m_Handles[i].clone) != 0)
{
@ -1150,19 +1225,33 @@ void HandleSystem::Dump(const HandleReporter &fn)
bresult = pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size);
}
char date[256]; // 256 should be more than enough
size_t written = 0;
// scope for InvalidParameterHandler
{
#ifdef PLATFORM_WINDOWS
InvalidParameterHandler p;
#endif
written = strftime(date, sizeof(date), fmt, localtime(&m_Handles[i].timestamp));
}
if (!written)
{
ke::SafeStrcpy(date, sizeof(date), "INVALID");
}
if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION
|| !bresult)
{
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1");
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s\t%-30.30s", index, owner, type, "-1", date);
}
else
{
char buffer[32];
ke::SafeSprintf(buffer, sizeof(buffer), "%d", size);
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer);
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s\t%-30.30s", index, owner, type, buffer, date);
total_size += size;
}
}
rep(fn, "-- Approximately %d bytes of memory are in use by Handles.\n", total_size);
}

View File

@ -32,22 +32,27 @@
#ifndef _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_
#define _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_
#include <IHandleSys.h>
#include <stdio.h>
#include <sm_namehashset.h>
#include <amtl/am-autoptr.h>
#include <memory>
#include <amtl/am-string.h>
#include <amtl/am-function.h>
#include <IHandleSys.h>
#include <sm_namehashset.h>
#include "common_logic.h"
#define HANDLESYS_MAX_HANDLES (1<<15)
#define HANDLESYS_HANDLE_BITS 20
#define HANDLESYS_MAX_HANDLES ((1 << HANDLESYS_HANDLE_BITS) - 1)
#define HANDLESYS_MAX_TYPES (1<<9)
#define HANDLESYS_MAX_SUBTYPES 0xF
#define HANDLESYS_SUBTYPE_MASK 0xF
#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1))
#define HANDLESYS_MAX_SERIALS 0xFFFF
#define HANDLESYS_SERIAL_MASK 0xFFFF0000
#define HANDLESYS_HANDLE_MASK 0x0000FFFF
#define HANDLESYS_SERIAL_BITS (32 - HANDLESYS_HANDLE_BITS)
#define HANDLESYS_MAX_SERIALS (1 << HANDLESYS_SERIAL_BITS)
#define HANDLESYS_SERIAL_MASK (((1 << HANDLESYS_SERIAL_BITS) - 1) << HANDLESYS_HANDLE_BITS)
#define HANDLESYS_HANDLE_MASK ((1 << HANDLESYS_HANDLE_BITS) - 1)
#define HANDLESYS_WARN_USAGE 100000
#define HANDLESYS_MEMUSAGE_MIN_VERSION 3
@ -88,6 +93,7 @@ struct QHandle
bool access_special; /* Whether or not access rules are special or type-derived */
bool is_destroying; /* Whether or not the handle is being destroyed */
HandleAccess sec; /* Security rules */
time_t timestamp; /* Creation timestamp */
/* The following variables are unrelated to the Handle array, and used
* as an inlined chain of information */
unsigned int freeID; /* ID of a free handle in the free handle chain */
@ -105,7 +111,7 @@ struct QHandleType
TypeAccess typeSec;
HandleAccess hndlSec;
unsigned int opened;
ke::AutoPtr<ke::AString> name;
std::unique_ptr<std::string> name;
static inline bool matches(const char *key, const QHandleType *type)
{
@ -117,7 +123,7 @@ struct QHandleType
}
};
typedef ke::Lambda<void(const char *)> HandleReporter;
typedef ke::Function<void(const char *)> HandleReporter;
class HandleSystem :
public IHandleSys

View File

@ -319,7 +319,7 @@ void Logger::_UpdateFiles(bool bLevelChange)
char buff[PLATFORM_MAX_PATH];
ke::SafeSprintf(buff, sizeof(buff), "%04d%02d%02d", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
ke::AString currentDate(buff);
std::string currentDate(buff);
if (m_Mode == LoggingMode_PerMap)
{
@ -327,7 +327,7 @@ void Logger::_UpdateFiles(bool bLevelChange)
{
for (size_t iter = 0; iter < static_cast<size_t>(-1); ++iter)
{
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/L%s%u.log", currentDate.chars(), iter);
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/L%s%u.log", currentDate.c_str(), iter);
if (!libsys->IsPathFile(buff))
{
break;
@ -336,12 +336,12 @@ void Logger::_UpdateFiles(bool bLevelChange)
}
else
{
ke::SafeStrcpy(buff, sizeof(buff), m_NormalFileName.chars());
ke::SafeStrcpy(buff, sizeof(buff), m_NormalFileName.c_str());
}
}
else
{
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/L%s.log", currentDate.chars());
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/L%s.log", currentDate.c_str());
}
if (m_NormalFileName.compare(buff))
@ -353,11 +353,11 @@ void Logger::_UpdateFiles(bool bLevelChange)
{
if (bLevelChange)
{
LogMessage("-------- Mapchange to %s --------", m_CurrentMapName.chars());
LogMessage("-------- Mapchange to %s --------", m_CurrentMapName.c_str());
}
}
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/errors_%s.log", currentDate.chars());
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/errors_%s.log", currentDate.c_str());
if (bLevelChange || m_ErrorFileName.compare(buff))
{
_CloseError();
@ -369,7 +369,7 @@ FILE *Logger::_OpenNormal()
{
_UpdateFiles();
FILE *pFile = fopen(m_NormalFileName.chars(), "a+");
FILE *pFile = fopen(m_NormalFileName.c_str(), "a+");
if (pFile == NULL)
{
_LogFatalOpen(m_NormalFileName);
@ -383,7 +383,7 @@ FILE *Logger::_OpenNormal()
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(pFile, "L %s: SourceMod log file session started (file \"%s\") (Version \"%s\")\n", date, m_NormalFileName.chars(), SOURCEMOD_VERSION);
fprintf(pFile, "L %s: SourceMod log file session started (file \"%s\") (Version \"%s\")\n", date, m_NormalFileName.c_str(), SOURCEMOD_VERSION);
m_DamagedNormalFile = true;
}
@ -394,7 +394,7 @@ FILE *Logger::_OpenError()
{
_UpdateFiles();
FILE *pFile = fopen(m_ErrorFileName.chars(), "a+");
FILE *pFile = fopen(m_ErrorFileName.c_str(), "a+");
if (pFile == NULL)
{
_LogFatalOpen(m_ErrorFileName);
@ -409,7 +409,7 @@ FILE *Logger::_OpenError()
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(pFile, "L %s: SourceMod error session started\n", date);
fprintf(pFile, "L %s: Info (map \"%s\") (file \"%s\")\n", date, m_CurrentMapName.chars(), m_ErrorFileName.chars());
fprintf(pFile, "L %s: Info (map \"%s\") (file \"%s\")\n", date, m_CurrentMapName.c_str(), m_ErrorFileName.c_str());
m_DamagedErrorFile = true;
}
@ -423,11 +423,11 @@ FILE *Logger::_OpenFatal()
return fopen(path, "at");
}
void Logger::_LogFatalOpen(ke::AString &str)
void Logger::_LogFatalOpen(std::string &str)
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", str.chars());
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", str.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
}

View File

@ -92,13 +92,13 @@ private:
FILE *_OpenError();
FILE *_OpenFatal();
void _LogFatalOpen(ke::AString &str);
void _LogFatalOpen(std::string &str);
void _PrintToGameLog(const char *fmt, va_list ap);
void _UpdateFiles(bool bLevelChange = false);
private:
ke::AString m_NormalFileName;
ke::AString m_ErrorFileName;
ke::AString m_CurrentMapName;
std::string m_NormalFileName;
std::string m_ErrorFileName;
std::string m_CurrentMapName;
int m_Day;

131
core/logic/LumpManager.cpp Normal file
View File

@ -0,0 +1,131 @@
/**
* vim: set ts=4 :
* =============================================================================
* Entity Lump Manager
* Copyright (C) 2021-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "LumpManager.h"
#include <iomanip>
#include <sstream>
EntityLumpParseResult::operator bool() const {
return m_Status == Status_OK;
}
EntityLumpParseResult EntityLumpManager::Parse(const char* pMapEntities) {
m_Entities.clear();
std::istringstream mapEntities(pMapEntities);
for (;;) {
std::string token;
mapEntities >> std::ws >> token >> std::ws;
// Assert that we're at the start of a new block, otherwise we're done parsing
if (token != "{") {
if (token == "\0") {
break;
} else {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
}
/**
* Parse key / value pairs until we reach a closing brace. We currently assume there
* are only quoted keys / values up to the next closing brace.
*
* The SDK suggests that there are cases that could use non-quoted symbols and nested
* braces (`shared/mapentities_shared.cpp::MapEntity_ParseToken`), but I haven't seen
* those in practice.
*/
EntityLumpEntry entry;
while (mapEntities.peek() != '}') {
std::string key, value;
if (mapEntities.peek() != '"') {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
mapEntities >> quoted(key) >> std::ws;
if (mapEntities.peek() != '"') {
return EntityLumpParseResult {
Status_UnexpectedChar, mapEntities.tellg()
};
}
mapEntities >> quoted(value) >> std::ws;
entry.emplace_back(key, value);
}
mapEntities.get();
m_Entities.push_back(std::make_shared<EntityLumpEntry>(entry));
}
return EntityLumpParseResult{};
}
std::string EntityLumpManager::Dump() {
std::ostringstream stream;
for (const auto& entry : m_Entities) {
// ignore empty entries
if (entry->empty()) {
continue;
}
stream << "{\n";
for (const auto& pair : *entry) {
stream << '"' << pair.first << "\" \"" << pair.second << '"' << '\n';
}
stream << "}\n";
}
return stream.str();
}
std::weak_ptr<EntityLumpEntry> EntityLumpManager::Get(size_t index) {
return m_Entities[index];
}
void EntityLumpManager::Erase(size_t index) {
m_Entities.erase(m_Entities.begin() + index);
}
void EntityLumpManager::Insert(size_t index) {
m_Entities.emplace(m_Entities.begin() + index, std::make_shared<EntityLumpEntry>());
}
size_t EntityLumpManager::Append() {
auto it = m_Entities.emplace(m_Entities.end(), std::make_shared<EntityLumpEntry>());
return std::distance(m_Entities.begin(), it);
}
size_t EntityLumpManager::Length() {
return m_Entities.size();
}

116
core/logic/LumpManager.h Normal file
View File

@ -0,0 +1,116 @@
/**
* vim: set ts=4 :
* =============================================================================
* Entity Lump Manager
* Copyright (C) 2021-2022 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _INCLUDE_LUMPMANAGER_H_
#define _INCLUDE_LUMPMANAGER_H_
#include <vector>
#include <memory>
#include <string>
/**
* Entity lump manager. Provides a list that stores a list of key / value pairs and the
* functionality to (de)serialize it from / to an entity string.
* This file and its corresponding .cpp should be compilable independently of SourceMod;
* the SourceMod interop is located within smn_entitylump.
*
* @file lumpmanager.h
* @brief Class definition for object that parses lumps.
*/
/**
* @brief A container of key / value pairs.
*/
using EntityLumpEntry = std::vector<std::pair<std::string, std::string>>;
enum EntityLumpParseStatus {
Status_OK,
Status_UnexpectedChar,
};
/**
* @brief Result of parsing an entity lump. On a parse error, m_Status is not Status_OK and
* m_Position indicates the offset within the string that caused the parse error.
*/
struct EntityLumpParseResult {
EntityLumpParseStatus m_Status;
std::streamoff m_Position;
operator bool() const;
const char* Description() const;
};
/**
* @brief Manages entity lump entries.
*/
class EntityLumpManager
{
public:
/**
* @brief Parses the map entities string into an internal representation.
*/
EntityLumpParseResult Parse(const char* pMapEntities);
/**
* @brief Dumps the current internal representation out to an std::string.
*/
std::string Dump();
/**
* @brief Returns a weak reference to an EntityLumpEntry. Used for handles on the scripting side.
*/
std::weak_ptr<EntityLumpEntry> Get(size_t index);
/**
* @brief Removes an EntityLumpEntry at the given index, shifting down all entries after it by one.
*/
void Erase(size_t index);
/**
* @brief Inserts a new EntityLumpEntry at the given index, shifting up the entries previously at the index and after it up by one.
*/
void Insert(size_t index);
/**
* @brief Adds a new EntityLumpEntry to the end. Returns the index of the entry.
*/
size_t Append();
/**
* @brief Returns the number of EntityLumpEntry items in the list.
*/
size_t Length();
private:
std::vector<std::shared_ptr<EntityLumpEntry>> m_Entities;
};
#endif // _INCLUDE_LUMPMANAGER_H_

View File

@ -49,6 +49,7 @@ MemoryUtils g_MemUtils;
MemoryUtils::MemoryUtils()
{
m_InfoMap.init();
#ifdef PLATFORM_APPLE
task_dyld_info_data_t dyld_info;
@ -77,20 +78,19 @@ void MemoryUtils::OnSourceModAllInitialized()
void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
{
DynLibInfo lib;
bool found;
char *ptr, *end;
const DynLibInfo* lib = nullptr;
memset(&lib, 0, sizeof(DynLibInfo));
if (!GetLibraryInfo(libPtr, lib))
if ((lib = GetLibraryInfo(libPtr)) == nullptr)
{
return NULL;
}
ptr = reinterpret_cast<char *>(lib.baseAddress);
end = ptr + lib.memorySize - len;
// Search in the original unaltered state of the binary.
char *start = lib->originalCopy.get();
char *ptr = start;
char *end = ptr + lib->memorySize - len;
bool found;
while (ptr < end)
{
found = true;
@ -103,8 +103,9 @@ void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t l
}
}
// Translate the found offset into the actual live binary memory space.
if (found)
return ptr;
return reinterpret_cast<char *>(lib->baseAddress) + (ptr - start);
ptr++;
}
@ -116,6 +117,8 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
{
#ifdef PLATFORM_WINDOWS
/* Add this this library into the cache */
GetLibraryInfo(handle);
return GetProcAddress((HMODULE)handle, symbol);
#elif defined PLATFORM_LINUX
@ -162,6 +165,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
}
/* Add this this library into the cache */
GetLibraryInfo((void *)dlmap->l_addr);
/* If we don't have a symbol table for this library, then create one */
if (table == NULL)
{
@ -325,6 +331,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
/* Uh oh, we couldn't find a matching handle */
return NULL;
}
/* Add this this library into the cache */
GetLibraryInfo((void *)dlbase);
/* See if we already have a symbol table for this library */
for (size_t i = 0; i < m_SymTables.size(); i++)
@ -429,21 +438,25 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
#endif
}
bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
const DynLibInfo *MemoryUtils::GetLibraryInfo(const void *libPtr)
{
uintptr_t baseAddr;
if (libPtr == NULL)
{
return false;
return nullptr;
}
DynLibInfo lib;
#ifdef PLATFORM_WINDOWS
#ifdef PLATFORM_X86
const WORD PE_FILE_MACHINE = IMAGE_FILE_MACHINE_I386;
const WORD PE_NT_OPTIONAL_HDR_MAGIC = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
#else
const WORD PE_FILE_MACHINE = IMAGE_FILE_MACHINE_AMD64;
const WORD PE_NT_OPTIONAL_HDR_MAGIC = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
#endif
MEMORY_BASIC_INFORMATION info;
@ -454,7 +467,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
{
return false;
return nullptr;
}
baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase);
@ -466,21 +479,21 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
opt = &pe->OptionalHeader;
/* Check PE magic and signature */
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != PE_NT_OPTIONAL_HDR_MAGIC)
{
return false;
return nullptr;
}
/* Check architecture */
if (file->Machine != PE_FILE_MACHINE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library */
if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
{
return false;
return nullptr;
}
/* Finally, we can do this */
@ -507,12 +520,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!dladdr(libPtr, &info))
{
return false;
return nullptr;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
return nullptr;
}
/* This is for our insane sanity checks :o */
@ -522,31 +535,31 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
/* Check ELF magic */
if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
{
return false;
return nullptr;
}
/* Check ELF version */
if (file->e_ident[EI_VERSION] != EV_CURRENT)
{
return false;
return nullptr;
}
/* Check ELF endianness */
if (file->e_ident[EI_DATA] != ELFDATA2LSB)
{
return false;
return nullptr;
}
/* Check ELF architecture */
if (file->e_ident[EI_CLASS] != ELF_CLASS || file->e_machine != ELF_MACHINE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library/shared object */
if (file->e_type != ET_DYN)
{
return false;
return nullptr;
}
phdrCount = file->e_phnum;
@ -596,12 +609,12 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
if (!dladdr(libPtr, &info))
{
return false;
return nullptr;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
return nullptr;
}
/* This is for our insane sanity checks :o */
@ -611,19 +624,19 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
/* Check Mach-O magic */
if (file->magic != MACH_MAGIC)
{
return false;
return nullptr;
}
/* Check architecture */
if (file->cputype != MACH_CPU_TYPE || file->cpusubtype != MACH_CPU_SUBTYPE)
{
return false;
return nullptr;
}
/* For our purposes, this must be a dynamic library */
if (file->filetype != MH_DYLIB)
{
return false;
return nullptr;
}
cmd_count = file->ncmds;
@ -644,5 +657,17 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
lib.baseAddress = reinterpret_cast<void *>(baseAddr);
return true;
LibraryInfoMap::Insert i = m_InfoMap.findForAdd(lib.baseAddress);
if (i.found())
{
// We already loaded this binary before.
return &i->value;
}
// Keep a copy of the binary in its initial unpatched state for lookup.
lib.originalCopy = std::make_unique<char[]>(lib.memorySize);
memcpy(lib.originalCopy.get(), lib.baseAddress, lib.memorySize);
m_InfoMap.add(i, lib.baseAddress, std::move(lib));
return &i->value;
}

View File

@ -32,6 +32,8 @@
#include "common_logic.h"
#include <IMemoryUtils.h>
#include <am-hashmap.h>
#include <memory>
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
#include <sh_vector.h>
#include "sm_symtable.h"
@ -49,6 +51,7 @@ struct DynLibInfo
{
void *baseAddress;
size_t memorySize;
std::unique_ptr<char[]> originalCopy;
};
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
@ -73,7 +76,7 @@ public: // IMemoryUtils
void *FindPattern(const void *libPtr, const char *pattern, size_t len);
void *ResolveSymbol(void *handle, const char *symbol);
public:
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
const DynLibInfo *GetLibraryInfo(const void *libPtr);
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE
private:
CVector<LibSymbolTable *> m_SymTables;
@ -83,6 +86,8 @@ private:
SInt32 m_OSXMinor;
#endif
#endif
typedef ke::HashMap<void *, DynLibInfo, ke::PointerPolicy<void> > LibraryInfoMap;
LibraryInfoMap m_InfoMap;
};
extern MemoryUtils g_MemUtils;

View File

@ -33,7 +33,6 @@
#include <IShareSys.h>
#include <IHandleSys.h>
#include <am-autoptr.h>
#include <am-string.h>
#include <am-utility.h>
#include <am-refcounting.h>
@ -47,16 +46,14 @@ struct FakeNative
FakeNative(const char *name, IPluginFunction *fun)
: name(name),
ctx(fun->GetParentContext()),
call(fun),
gate(NULL)
call(fun)
{
}
~FakeNative();
ke::AString name;
std::string name;
IPluginContext *ctx;
IPluginFunction *call;
SPVM_NATIVE_FUNC gate;
ke::RefPtr<INativeCallback> wrapper;
};
struct Native : public ke::Refcounted<Native>
@ -64,31 +61,25 @@ struct Native : public ke::Refcounted<Native>
Native(CNativeOwner *owner, const sp_nativeinfo_t *native)
: owner(owner),
native(native),
fake(NULL)
fake(nullptr)
{
}
Native(CNativeOwner *owner, FakeNative *fake)
Native(CNativeOwner *owner, std::unique_ptr<FakeNative>&& fake)
: owner(owner),
native(NULL),
fake(fake)
native(nullptr),
fake(std::move(fake))
{
}
CNativeOwner *owner;
const sp_nativeinfo_t *native;
ke::AutoPtr<FakeNative> fake;
std::unique_ptr<FakeNative> fake;
SPVM_NATIVE_FUNC func() const
{
if (native)
return native->func;
return fake->gate;
}
const char *name() const
{
if (native)
return native->name;
return fake->name.chars();
return fake->name.c_str();
}
static inline bool matches(const char *name, const ke::RefPtr<Native> &entry)

View File

@ -0,0 +1,321 @@
// vim: set sts=2 ts=8 sw=2 tw=99 et:
//
// Copyright (C) 2006-2015 AlliedModders LLC
//
// This file is part of SourcePawn. SourcePawn is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// You should have received a copy of the GNU General Public License along with
// SourcePawn. If not, see http://www.gnu.org/licenses/.
//
#include <stdio.h>
#include <string.h>
#include "NativeInvoker.h"
/********************
* FUNCTION CALLING *
********************/
NativeInvoker::NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native)
: context_(pContext),
m_curparam(0),
m_errorstate(SP_ERROR_NONE),
native_(native)
{
}
NativeInvoker::~NativeInvoker()
{
Cancel();
}
bool
NativeInvoker::IsRunnable()
{
return true;
}
IPluginContext *
NativeInvoker::GetParentContext()
{
return context_;
}
int NativeInvoker::PushCell(cell_t cell)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
return SetError(SP_ERROR_PARAMS_MAX);
m_info[m_curparam].marked = false;
m_params[m_curparam] = cell;
m_curparam++;
return SP_ERROR_NONE;
}
int
NativeInvoker::PushCellByRef(cell_t *cell, int flags)
{
return PushArray(cell, 1, flags);
}
int
NativeInvoker::PushFloat(float number)
{
cell_t val = sp::FloatCellUnion(number).cell;
return PushCell(val);
}
int
NativeInvoker::PushFloatByRef(float *number, int flags)
{
return PushCellByRef((cell_t *)number, flags);
}
int
NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
ParamInfo *info = &m_info[m_curparam];
info->flags = inarray ? copyback : 0;
info->marked = true;
info->size = cells;
info->str.is_sz = false;
info->orig_addr = inarray;
m_curparam++;
return SP_ERROR_NONE;
}
int
NativeInvoker::PushString(const char *string)
{
return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
}
int
NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
{
return _PushString(buffer, sz_flags, cp_flags, length);
}
int
NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
{
if (m_curparam >= SP_MAX_EXEC_PARAMS)
return SetError(SP_ERROR_PARAMS_MAX);
ParamInfo *info = &m_info[m_curparam];
info->marked = true;
info->orig_addr = (cell_t *)string;
info->flags = cp_flags;
info->size = len;
info->str.sz_flags = sz_flags;
info->str.is_sz = true;
m_curparam++;
return SP_ERROR_NONE;
}
void
NativeInvoker::Cancel()
{
if (!m_curparam)
return;
m_errorstate = SP_ERROR_NONE;
m_curparam = 0;
}
int
NativeInvoker::Execute(cell_t *result, cell_t buffer, cell_t size)
{
context_->ClearLastNativeError();
// For backward compatibility, we have to clear the exception state.
// Otherwise code like this:
//
// static cell_t native(cx, params) {
// for (auto callback : callbacks) {
// callback->Execute();
// }
// }
//
// Could unintentionally leak a pending exception back to the caller,
// which wouldn't have happened before the Great Exception Refactoring.
SourcePawn::ExceptionHandler eh(context_);
eh.Debug(!size);
if (!Invoke(result)) {
if(size)
context_->StringToLocalUTF8(buffer, size, eh.Message(), NULL);
int Err = context_->GetLastNativeError();
context_->ClearLastNativeError();
return Err;
}
return SP_ERROR_NONE;
}
bool
NativeInvoker::Invoke(cell_t *result)
{
if (!IsRunnable()) {
Cancel();
context_->ReportErrorNumber(SP_ERROR_NOT_RUNNABLE);
return false;
}
if (int err = m_errorstate) {
Cancel();
context_->ReportErrorNumber(err);
return false;
}
//This is for re-entrancy!
cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
cell_t *temp_params = &_temp_params[1];
ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
unsigned int numparams = m_curparam;
unsigned int i;
if (numparams)
{
//Save the info locally, then reset it for re-entrant calls.
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
}
m_curparam = 0;
/* Initialize 0th parameter */
_temp_params[0] = numparams;
/* Browse the parameters and build arrays */
bool ok = true;
for (i=0; i<numparams; i++) {
/* Is this marked as an array? */
if (temp_info[i].marked) {
if (!temp_info[i].str.is_sz) {
/* Allocate a normal/generic array */
int err = context_->HeapAlloc(
temp_info[i].size,
&(temp_info[i].local_addr),
&(temp_info[i].phys_addr));
if (err != SP_ERROR_NONE) {
context_->ReportErrorNumber(err);
ok = false;
break;
}
if (temp_info[i].orig_addr)
{
memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
}
} else {
/* Calculate cells required for the string */
size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
/* Allocate the buffer */
int err = context_->HeapAlloc(
cells,
&(temp_info[i].local_addr),
&(temp_info[i].phys_addr));
if (err != SP_ERROR_NONE) {
context_->ReportErrorNumber(err);
ok = false;
break;
}
/* Copy original string if necessary */
if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
{
/* Cut off UTF-8 properly */
if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8) {
context_->StringToLocalUTF8(
temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr,
NULL);
}
/* Copy a binary blob */
else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
{
memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
}
/* Copy ASCII characters */
else
{
context_->StringToLocal(
temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr);
}
}
} /* End array/string calculation */
/* Update the pushed parameter with the byref local address */
temp_params[i] = temp_info[i].local_addr;
} else {
/* Just copy the value normally */
temp_params[i] = m_params[i];
}
}
/* Make the call if we can */
if (ok)
{
*result = native_->func()(context_, _temp_params);
}
/* i should be equal to the last valid parameter + 1 */
bool docopies = ok;
while (i--) {
if (!temp_info[i].marked)
continue;
if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK)) {
if (temp_info[i].orig_addr) {
if (temp_info[i].str.is_sz) {
memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
} else {
if (temp_info[i].size == 1) {
*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
} else {
memcpy(temp_info[i].orig_addr,
temp_info[i].phys_addr,
temp_info[i].size * sizeof(cell_t));
}
}
}
}
if (int err = context_->HeapPop(temp_info[i].local_addr))
context_->ReportErrorNumber(err);
}
return context_->GetLastNativeError() == SP_ERROR_NONE;
}
int
NativeInvoker::SetError(int err)
{
m_errorstate = err;
return err;
}
int NativeInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
funcid_t NativeInvoker::GetFunctionID() { return 0; }
int NativeInvoker::Execute2(IPluginContext *ctx, cell_t *result) { return 0; }
int NativeInvoker::CallFunction2(IPluginContext *ctx, const cell_t *params, unsigned int num_params, cell_t *result) { return 0; }
IPluginRuntime *NativeInvoker::GetParentRuntime() { return NULL; }

View File

@ -0,0 +1,79 @@
// vim: set sts=2 ts=8 sw=2 tw=99 et:
//
// Copyright (C) 2006-2015 AlliedModders LLC
//
// This file is part of SourcePawn. SourcePawn is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// You should have received a copy of the GNU General Public License along with
// SourcePawn. If not, see http://www.gnu.org/licenses/.
//
#ifndef _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
#define _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
#include <sp_vm_api.h>
#include <amtl/am-autoptr.h>
#include <amtl/am-refcounting.h>
#include "Native.h"
struct ParamInfo
{
int flags; /* Copy-back flags */
bool marked; /* Whether this is marked as being used */
cell_t local_addr; /* Local address to free */
cell_t *phys_addr; /* Physical address of our copy */
cell_t *orig_addr; /* Original address to copy back to */
ucell_t size; /* Size of array in bytes */
struct {
bool is_sz; /* is a string */
int sz_flags; /* has sz flags */
} str;
};
class NativeInvoker : public IPluginFunction
{
public:
NativeInvoker(IPluginContext *pContext, const ke::RefPtr<Native> &native);
virtual ~NativeInvoker();
public:
int PushCell(cell_t cell);
int PushCellByRef(cell_t *cell, int flags);
int PushFloat(float number);
int PushFloatByRef(float *number, int flags);
int PushArray(cell_t *inarray, unsigned int cells, int copyback);
int PushString(const char *string);
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
int Execute(cell_t *result, cell_t buffer=0, cell_t size=0);
void Cancel();
int CallFunction(const cell_t *params, unsigned int num_params, cell_t *result);
IPluginContext *GetParentContext();
bool Invoke(cell_t *result);
bool IsRunnable();
funcid_t GetFunctionID();
int Execute2(IPluginContext *ctx, cell_t *result);
int CallFunction2(IPluginContext *ctx,
const cell_t *params,
unsigned int num_params,
cell_t *result);
IPluginRuntime *GetParentRuntime();
const char *DebugName() {
return native_->name();
}
private:
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
int SetError(int err);
private:
IPluginContext *context_;
cell_t m_params[SP_MAX_EXEC_PARAMS];
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
unsigned int m_curparam;
int m_errorstate;
ke::RefPtr<Native> native_;
};
#endif //_INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_

View File

@ -62,7 +62,7 @@ void CNativeOwner::AddNatives(const sp_nativeinfo_t *natives)
for (const sp_nativeinfo_t *native = natives; native->func && native->name; native++)
g_ShareSys.AddNativeToCache(this, native);
m_natives.append(natives);
m_natives.push_back(natives);
}
void CNativeOwner::UnbindWeakRef(const WeakNative &ref)
@ -90,14 +90,14 @@ void CNativeOwner::DropEverything()
}
/* Strip all of our natives from the cache */
for (size_t i = 0; i < m_natives.length(); i++) {
for (size_t i = 0; i < m_natives.size(); i++) {
const sp_nativeinfo_t *natives = m_natives[i];
for (const sp_nativeinfo_t *native = natives; native->func && native->name; native++)
g_ShareSys.ClearNativeFromCache(this, native->name);
}
m_natives.clear();
for (size_t i = 0; i < m_fakes.length(); i++)
for (size_t i = 0; i < m_fakes.size(); i++)
g_ShareSys.ClearNativeFromCache(this, m_fakes[i]->name());
m_fakes.clear();
}

View File

@ -33,7 +33,6 @@
#include <sp_vm_types.h>
#include <sh_list.h>
#include <am-linkedlist.h>
#include <am-vector.h>
#include "common_logic.h"
#include "Native.h"
@ -80,8 +79,8 @@ protected:
List<CPlugin *> m_Dependents;
unsigned int m_nMarkSerial;
List<WeakNative> m_WeakRefs;
ke::Vector<const sp_nativeinfo_t *> m_natives;
ke::Vector<ke::RefPtr<Native> > m_fakes;
std::vector<const sp_nativeinfo_t *> m_natives;
std::vector<ke::RefPtr<Native> > m_fakes;
};
extern CNativeOwner g_CoreNatives;

View File

@ -45,10 +45,11 @@
#include "Logger.h"
#include "frame_tasks.h"
#include <amtl/am-string.h>
#include <amtl/am-linkedlist.h>
#include <bridge/include/IVEngineServerBridge.h>
#include <bridge/include/CoreProvider.h>
#define SOURCEMOD_PLUGINAPI_VERSION 7
CPluginManager g_PluginSys;
HandleType_t g_PluginType = 0;
IdentityType_t g_PluginIdent = 0;
@ -79,7 +80,7 @@ CPlugin::CPlugin(const char *file)
memset(&m_info, 0, sizeof(m_info));
m_pPhrases = g_Translator.CreatePhraseCollection();
m_pPhrases.reset(g_Translator.CreatePhraseCollection());
}
CPlugin::~CPlugin()
@ -227,7 +228,7 @@ bool CPlugin::SetProperty(const char *prop, void *ptr)
IPluginRuntime *CPlugin::GetRuntime()
{
return m_pRuntime;
return m_pRuntime.get();
}
void CPlugin::EvictWithError(PluginStatus status, const char *error_fmt, ...)
@ -276,7 +277,7 @@ bool CPlugin::ReadInfo()
sm_plugininfo_c_t *cinfo;
cell_t local_addr;
auto update_field = [base](cell_t addr, ke::AString *dest) {
auto update_field = [base](cell_t addr, std::string *dest) {
const char* ptr;
if (base->LocalToString(addr, (char **)&ptr) == SP_ERROR_NONE)
*dest = ptr;
@ -310,12 +311,12 @@ bool CPlugin::ReadInfo()
base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
m_FileVersion = info->version;
if (m_FileVersion >= 4) {
if (m_FileVersion >= 5) {
base->LocalToString(info->date, (char **)&pDate);
base->LocalToString(info->time, (char **)&pTime);
ke::SafeSprintf(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
}
if (m_FileVersion > 5) {
if (m_FileVersion > SOURCEMOD_PLUGINAPI_VERSION) {
base->LocalToString(info->filevers, (char **)&pFileVers);
EvictWithError(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers);
return false;
@ -483,7 +484,7 @@ bool CPlugin::TryCompile()
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", m_filename);
char loadmsg[255];
m_pRuntime = g_pSourcePawn2->LoadBinaryFromFile(fullpath, loadmsg, sizeof(loadmsg));
m_pRuntime.reset(g_pSourcePawn2->LoadBinaryFromFile(fullpath, loadmsg, sizeof(loadmsg)));
if (!m_pRuntime) {
EvictWithError(Plugin_BadLoad, "Unable to load plugin (%s)", loadmsg);
return false;
@ -524,11 +525,11 @@ PluginType CPlugin::GetType()
const sm_plugininfo_t *CPlugin::GetPublicInfo()
{
m_info.author = info_author_.chars();
m_info.description = info_description_.chars();
m_info.name = info_name_.chars();
m_info.url = info_url_.chars();
m_info.version = info_version_.chars();
m_info.author = info_author_.c_str();
m_info.description = info_description_.c_str();
m_info.name = info_name_.c_str();
m_info.url = info_url_.c_str();
m_info.version = info_version_.c_str();
return &m_info;
}
@ -658,7 +659,7 @@ time_t CPlugin::GetFileTimeStamp()
IPhraseCollection *CPlugin::GetPhrases()
{
return m_pPhrases;
return m_pPhrases.get();
}
void CPlugin::DependencyDropped(CPlugin *pOwner)
@ -674,7 +675,7 @@ void CPlugin::DependencyDropped(CPlugin *pOwner)
}
unsigned int unbound = 0;
for (size_t i = 0; i < pOwner->m_fakes.length(); i++)
for (size_t i = 0; i < pOwner->m_fakes.size(); i++)
{
ke::RefPtr<Native> entry(pOwner->m_fakes[i]);
@ -773,13 +774,13 @@ bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKEN
if (!entry)
return false;
m_fakes.append(entry);
m_fakes.push_back(entry);
return true;
}
void CPlugin::BindFakeNativesTo(CPlugin *other)
{
for (size_t i = 0; i < m_fakes.length(); i++)
for (size_t i = 0; i < m_fakes.size(); i++)
g_ShareSys.BindNativeToPlugin(other, m_fakes[i]);
}
@ -790,7 +791,7 @@ void CPlugin::BindFakeNativesTo(CPlugin *other)
CPluginManager::CPluginIterator::CPluginIterator(ReentrantList<CPlugin *>& in)
{
for (PluginIter iter(in); !iter.done(); iter.next())
mylist.append(*iter);
mylist.push_back(*iter);
current = mylist.begin();
g_PluginSys.AddPluginsListener(this);
}
@ -847,11 +848,7 @@ CPluginManager::~CPluginManager()
void CPluginManager::Shutdown()
{
List<CPlugin *>::iterator iter;
for (PluginIter iter(m_plugins); !iter.done(); iter.next()) {
UnloadPlugin(*iter);
}
UnloadAll();
}
void CPluginManager::LoadAll(const char *config_path, const char *plugins_path)
@ -979,6 +976,20 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ
LoadRes res;
*wasloaded = false;
if (strstr(path, "..") != NULL)
{
ke::SafeStrcpy(error, maxlength, "Cannot load plugins outside the \"plugins\" folder");
return NULL;
}
const char *ext = libsys->GetFileExtension(path);
if (!ext || strcmp(ext, "smx") != 0)
{
ke::SafeStrcpy(error, maxlength, "Plugin files must have the \".smx\" file extension");
return NULL;
}
if ((res=LoadPlugin(&pl, path, true, PluginType_MapUpdated)) == LoadRes_Failure)
{
ke::SafeStrcpy(error, maxlength, pl->GetErrorMsg());
@ -1033,7 +1044,7 @@ void CPluginManager::LoadAutoPlugin(const char *plugin)
void CPluginManager::AddPlugin(CPlugin *pPlugin)
{
m_plugins.append(pPlugin);
m_plugins.push_back(pPlugin);
m_LoadLookup.insert(pPlugin->GetFilename(), pPlugin);
pPlugin->SetRegistered();
@ -1165,7 +1176,7 @@ bool CPlugin::ForEachExtVar(const ExtVarCallback& callback)
return true;
}
void CPlugin::ForEachLibrary(ke::Lambda<void(const char *)> callback)
void CPlugin::ForEachLibrary(ke::Function<void(const char *)> callback)
{
for (auto iter = m_Libraries.begin(); iter != m_Libraries.end(); iter++)
callback((*iter).c_str());
@ -1177,7 +1188,7 @@ void CPlugin::AddRequiredLib(const char *name)
m_RequiredLibs.push_back(name);
}
bool CPlugin::ForEachRequiredLib(ke::Lambda<bool(const char *)> callback)
bool CPlugin::ForEachRequiredLib(ke::Function<bool(const char *)> callback)
{
for (auto iter = m_RequiredLibs.begin(); iter != m_RequiredLibs.end(); iter++) {
if (!callback((*iter).c_str()))
@ -1211,7 +1222,7 @@ void CPluginManager::LoadExtensions(CPlugin *pPlugin)
}
return true;
};
pPlugin->ForEachExtVar(ke::Move(callback));
pPlugin->ForEachExtVar(std::move(callback));
}
bool CPluginManager::RequireExtensions(CPlugin *pPlugin)
@ -1247,7 +1258,7 @@ bool CPluginManager::RequireExtensions(CPlugin *pPlugin)
return true;
};
return pPlugin->ForEachExtVar(ke::Move(callback));
return pPlugin->ForEachExtVar(std::move(callback));
}
CPlugin *CPluginManager::CompileAndPrep(const char *path)
@ -1490,6 +1501,9 @@ void CPluginManager::Purge(CPlugin *plugin)
if (plugin->GetStatus() == Plugin_Running)
plugin->Call_OnPluginEnd();
m_pOnNotifyPluginUnloaded->PushCell(plugin->GetMyHandle());
m_pOnNotifyPluginUnloaded->Execute(NULL);
// Notify listeners of unloading.
if (plugin->EnteredSecondPass()) {
for (ListenerIter iter(m_listeners); !iter.done(); iter.next())
@ -1536,12 +1550,12 @@ CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
unsigned int CPluginManager::GetPluginCount()
{
return m_plugins.length();
return m_plugins.size();
}
void CPluginManager::AddPluginsListener(IPluginsListener *listener)
{
m_listeners.append(listener);
m_listeners.push_back(listener);
}
void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
@ -1578,6 +1592,7 @@ void CPluginManager::OnSourceModAllInitialized()
m_pOnLibraryAdded = forwardsys->CreateForward("OnLibraryAdded", ET_Ignore, 1, NULL, Param_String);
m_pOnLibraryRemoved = forwardsys->CreateForward("OnLibraryRemoved", ET_Ignore, 1, NULL, Param_String);
m_pOnNotifyPluginUnloaded = forwardsys->CreateForward("OnNotifyPluginUnloaded", ET_Ignore, 1, NULL, Param_Cell);
}
void CPluginManager::OnSourceModShutdown()
@ -1592,6 +1607,7 @@ void CPluginManager::OnSourceModShutdown()
forwardsys->ReleaseForward(m_pOnLibraryAdded);
forwardsys->ReleaseForward(m_pOnLibraryRemoved);
forwardsys->ReleaseForward(m_pOnNotifyPluginUnloaded);
}
ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
@ -1715,7 +1731,7 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", plnum, (plnum > 1) ? "s" : "");
}
ke::LinkedList<CPlugin *> fail_list;
std::list<CPlugin *> fail_list;
for (PluginIter iter(m_plugins); !iter.done(); iter.next(), id++) {
CPlugin *pl = (*iter);
@ -1727,7 +1743,7 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
len += ke::SafeSprintf(buffer, sizeof(buffer), " %0*d <%s>", plpadding, id, GetStatusText(pl->GetDisplayStatus()));
/* Plugin has failed to load. */
fail_list.append(pl);
fail_list.push_back(pl);
}
else
{
@ -2033,7 +2049,8 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
//the unload/reload attempt next frame will print a message
case PluginState::WaitingToUnload:
case PluginState::WaitingToUnloadAndReload:
return;
rootmenu->ConsolePrint("[SM] Plugin %s will be reloaded on the next frame.", name);
break;
default:
rootmenu->ConsolePrint("[SM] Failed to reload plugin %s.", name);
@ -2063,7 +2080,7 @@ bool CPluginManager::ReloadPlugin(CPlugin *pl, bool print)
if (state == PluginState::WaitingToUnloadAndReload)
return false;
ke::AString filename(pl->GetFilename());
std::string filename(pl->GetFilename());
PluginType ptype = pl->GetType();
int id = 1;
@ -2078,13 +2095,13 @@ bool CPluginManager::ReloadPlugin(CPlugin *pl, bool print)
{
pl->SetWaitingToUnload(true);
ScheduleTaskForNextFrame([this, id, filename, ptype, print]() -> void {
ReloadPluginImpl(id, filename.chars(), ptype, print);
ReloadPluginImpl(id, filename.c_str(), ptype, print);
});
}
return false;
}
ReloadPluginImpl(id, filename.chars(), ptype, false);
ReloadPluginImpl(id, filename.c_str(), ptype, false);
return true;
}
@ -2208,7 +2225,6 @@ void CPluginManager::UnloadAll()
int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
{
int id = 1;
List<CPlugin *>::iterator iter;
for (PluginIter iter(m_plugins); !iter.done(); iter.next()) {
if ((*iter) == pl)
@ -2273,7 +2289,7 @@ void CPluginManager::FreePluginList(const CVector<SMPlugin *> *list)
delete const_cast<CVector<SMPlugin *> *>(list);
}
void CPluginManager::ForEachPlugin(ke::Lambda<void(CPlugin *)> callback)
void CPluginManager::ForEachPlugin(ke::Function<void(CPlugin *)> callback)
{
for (PluginIter iter(m_plugins); !iter.done(); iter.next())
callback(*iter);
@ -2351,7 +2367,7 @@ public:
{
ke::RefPtr<PluginsListenerV1Wrapper> wrapper = new PluginsListenerV1Wrapper(listener);
v1_wrappers_.append(wrapper);
v1_wrappers_.push_back(wrapper);
g_PluginSys.AddPluginsListener(wrapper);
}
@ -2394,4 +2410,4 @@ static OldPluginAPI sOldPluginAPI;
IPluginManager *CPluginManager::GetOldAPI()
{
return &sOldPluginAPI;
}
}

View File

@ -32,9 +32,12 @@
#ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <memory>
#include <IPluginSys.h>
#include <IHandleSys.h>
#include <IForwardSys.h>
@ -129,10 +132,10 @@ public:
bool required;
};
typedef ke::Lambda<bool(const sp_pubvar_t *, const ExtVar& ext)> ExtVarCallback;
typedef ke::Function<bool(const sp_pubvar_t *, const ExtVar& ext)> ExtVarCallback;
bool ForEachExtVar(const ExtVarCallback& callback);
void ForEachLibrary(ke::Lambda<void(const char *)> callback);
void ForEachLibrary(ke::Function<void(const char *)> callback);
public:
/**
* Creates a plugin object with default values.
@ -215,7 +218,7 @@ public:
}
void AddRequiredLib(const char *name);
bool ForEachRequiredLib(ke::Lambda<bool(const char *)> callback);
bool ForEachRequiredLib(ke::Function<bool(const char *)> callback);
bool HasMissingFakeNatives() const {
return m_FakeNativesMissing;
@ -224,7 +227,7 @@ public:
return m_LibraryMissing;
}
bool HasFakeNatives() const {
return m_fakes.length() > 0;
return m_fakes.size() > 0;
}
// True if we got far enough into the second pass to call OnPluginLoaded
@ -267,8 +270,8 @@ private:
char m_errormsg[256];
// Internal properties that must by reset if the runtime is evicted.
ke::AutoPtr<IPluginRuntime> m_pRuntime;
ke::AutoPtr<CPhraseCollection> m_pPhrases;
std::unique_ptr<IPluginRuntime> m_pRuntime;
std::unique_ptr<CPhraseCollection> m_pPhrases;
IPluginContext *m_pContext;
sp_pubvar_t *m_MaxClientsVar;
StringHashMap<void *> m_Props;
@ -286,11 +289,11 @@ private:
// Cached.
sm_plugininfo_t m_info;
ke::AString info_name_;
ke::AString info_author_;
ke::AString info_description_;
ke::AString info_version_;
ke::AString info_url_;
std::string info_name_;
std::string info_author_;
std::string info_description_;
std::string info_version_;
std::string info_url_;
};
class CPluginManager :
@ -317,8 +320,8 @@ public:
void Release();
void OnPluginDestroyed(IPlugin *plugin) override;
private:
ke::LinkedList<CPlugin *> mylist;
ke::LinkedList<CPlugin *>::iterator current;
std::list<CPlugin *> mylist;
std::list<CPlugin *>::iterator current;
};
friend class CPluginManager::CPluginIterator;
public: //IScriptManager
@ -432,7 +435,7 @@ public:
void _SetPauseState(CPlugin *pPlugin, bool pause);
void ForEachPlugin(ke::Lambda<void(CPlugin *)> callback);
void ForEachPlugin(ke::Function<void(CPlugin *)> callback);
private:
LoadRes LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type);
@ -473,7 +476,7 @@ private:
private:
ReentrantList<IPluginsListener *> m_listeners;
ReentrantList<CPlugin *> m_plugins;
ke::LinkedList<CPluginIterator *> m_iterators;
std::list<CPluginIterator *> m_iterators;
typedef decltype(m_listeners)::iterator ListenerIter;
typedef decltype(m_plugins)::iterator PluginIter;
@ -484,10 +487,8 @@ private:
{
/* For windows & mac, we convert the path to lower-case in order to avoid duplicate plugin loading */
#if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
ke::AString original(key.chars());
ke::AString lower = original.lowercase();
return detail::CharsAndLength(lower.chars()).hash();
std::string lower = ke::Lowercase(key.c_str());
return detail::CharsAndLength(lower.c_str()).hash();
#else
return key.hash();
#endif
@ -497,8 +498,8 @@ private:
{
const char *pluginFileChars = const_cast<CPlugin*>(plugin)->GetFilename();
#if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
ke::AString pluginFile = ke::AString(pluginFileChars).lowercase();
ke::AString input = ke::AString(file).lowercase();
std::string pluginFile = ke::Lowercase(pluginFileChars);
std::string input = ke::Lowercase(file);
return pluginFile == input;
#else
@ -522,6 +523,7 @@ private:
// Forwards
IForward *m_pOnLibraryAdded;
IForward *m_pOnLibraryRemoved;
IForward *m_pOnNotifyPluginUnloaded;
};
extern CPluginManager g_PluginSys;

View File

@ -52,7 +52,7 @@ ProfileToolManager::OnSourceModShutdown()
IProfilingTool *
ProfileToolManager::FindToolByName(const char *name)
{
for (size_t i = 0; i < tools_.length(); i++) {
for (size_t i = 0; i < tools_.size(); i++) {
if (strcmp(tools_[i]->Name(), name) == 0)
return tools_[i];
}
@ -97,7 +97,7 @@ ProfileToolManager::StartFromConsole(IProfilingTool *tool)
void
ProfileToolManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs *args)
{
if (tools_.length() == 0) {
if (tools_.size() == 0) {
rootmenu->ConsolePrint("No profiling tools are enabled.");
return;
}
@ -107,7 +107,7 @@ ProfileToolManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs
if (strcmp(cmdname, "list") == 0) {
rootmenu->ConsolePrint("Profiling tools:");
for (size_t i = 0; i < tools_.length(); i++) {
for (size_t i = 0; i < tools_.size(); i++) {
rootmenu->DrawGenericOption(tools_[i]->Name(), tools_[i]->Description());
}
return;
@ -135,7 +135,7 @@ ProfileToolManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs
if (strcmp(cmdname, "start") == 0) {
if (!default_) {
default_ = FindToolByName("vprof");
if (!default_ && tools_.length() > 0)
if (!default_ && tools_.size() > 0)
default_ = tools_[0];
if (!default_) {
rootmenu->ConsolePrint("Could not find any profiler to use.");

View File

@ -51,7 +51,7 @@ public:
void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *args) override;
void RegisterTool(IProfilingTool *tool) {
tools_.append(tool);
tools_.push_back(tool);
}
bool IsActive() const {
@ -76,7 +76,7 @@ private:
void StartFromConsole(IProfilingTool *tool);
private:
ke::Vector<IProfilingTool *> tools_;
std::vector<IProfilingTool *> tools_;
IProfilingTool *active_;
IProfilingTool *default_;
bool enabled_;

View File

@ -221,11 +221,15 @@ void RootConsoleMenu::OnRootConsoleCommand(const char *cmdname, const ICommandAr
ConsolePrint(" Fyren");
ConsolePrint(" Nicholas \"psychonic\" Hastings");
ConsolePrint(" Asher \"asherkin\" Baker");
ConsolePrint(" Ruben \"Dr!fter\" Gonzalez");
ConsolePrint(" Josh \"KyleS\" Allard");
ConsolePrint(" Michael \"Headline\" Flaherty");
ConsolePrint(" Jannik \"Peace-Maker\" Hartung");
ConsolePrint(" Borja \"faluco\" Ferrer");
ConsolePrint(" Pavol \"PM OnoTo\" Marko");
ConsolePrint(" Special thanks to Liam, ferret, and Mani");
ConsolePrint(" Special thanks to Viper and SteamFriends");
ConsolePrint(" http://www.sourcemod.net/");
ConsolePrint(" https://www.sourcemod.net/");
}
else if (strcmp(cmdname, "version") == 0)
{

View File

@ -29,13 +29,16 @@
* Version: $Id$
*/
#include <assert.h>
#include <memory>
#include "ShareSys.h"
#include "ExtensionSys.h"
#include <ILibrarySys.h>
#include "common_logic.h"
#include "PluginSys.h"
#include "HandleSys.h"
#include <assert.h>
using namespace ke;
@ -361,11 +364,11 @@ void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, const sp_native_t *native
}
}
pPlugin->GetRuntime()->UpdateNativeBinding(
index,
pEntry->func(),
flags,
nullptr);
auto rt = pPlugin->GetRuntime();
if (pEntry->fake)
rt->UpdateNativeBindingObject(index, pEntry->fake->wrapper, flags, nullptr);
else
rt->UpdateNativeBinding(index, pEntry->native->func, flags, nullptr);
}
AlreadyRefed<Native> ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv)
@ -379,11 +382,6 @@ AlreadyRefed<Native> ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const s
return entry.forget();
}
FakeNative::~FakeNative()
{
g_pSourcePawn2->DestroyFakeNative(gate);
}
void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name)
{
NativeCache::Result r = m_NtvCache.find(name);
@ -400,21 +398,44 @@ void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name)
m_NtvCache.remove(r);
}
class DynamicNative final : public INativeCallback
{
public:
DynamicNative(SPVM_FAKENATIVE_FUNC callback, void* data)
: callback_(callback),
data_(data)
{}
void AddRef() override {
refcount_++;
}
void Release() override {
assert(refcount_ > 0);
if (--refcount_ == 0)
delete this;
}
int Invoke(IPluginContext* ctx, const cell_t* params) override {
return callback_(ctx, params, data_);
}
private:
size_t refcount_ = 0;
SPVM_FAKENATIVE_FUNC callback_;
void* data_;
};
AlreadyRefed<Native> ShareSystem::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
{
RefPtr<Native> entry(FindNative(name));
if (entry)
return nullptr;
AutoPtr<FakeNative> fake(new FakeNative(name, pFunc));
fake->gate = g_pSourcePawn2->CreateFakeNative(func, fake);
if (!fake->gate)
return nullptr;
std::unique_ptr<FakeNative> fake(new FakeNative(name, pFunc));
fake->wrapper = new DynamicNative(func, fake.get());
CNativeOwner *owner = g_PluginSys.GetPluginByCtx(fake->ctx->GetContext());
entry = new Native(owner, fake.take());
entry = new Native(owner, std::move(fake));
m_NtvCache.insert(name, entry);
return entry.forget();

View File

@ -49,15 +49,17 @@ namespace SourceMod
{
struct IdentityToken_t
{
Handle_t ident;
void *ptr;
IdentityType_t type;
Handle_t ident = 0;
void *ptr = nullptr;
IdentityType_t type = 0;
size_t num_handles = 0;
bool warned_handle_usage = false;
};
};
struct IfaceInfo
{
bool operator ==(const IfaceInfo &info)
bool operator ==(const IfaceInfo &info) const
{
return (info.iface == iface && info.owner == owner);
}

View File

@ -1,5 +1,5 @@
/**
* vim: set ts=4 sw=4 :
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
@ -29,17 +29,518 @@
* Version: $Id$
*/
#include <sm_platform.h>
#include <amtl/am-deque.h>
#include <amtl/am-maybe.h>
#include <amtl/am-thread.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread>
#include "BaseWorker.h"
#include "ThreadSupport.h"
#include "common_logic.h"
#if defined PLATFORM_POSIX
#include "thread/PosixThreads.h"
#elif defined PLATFORM_WINDOWS
#include "thread/WinThreads.h"
#endif
static constexpr unsigned int DEFAULT_THINK_TIME_MS = 20;
MainThreader g_MainThreader;
IThreader *g_pThreader = &g_MainThreader;
class CompatWorker final : public IThreadWorker
{
public:
explicit CompatWorker(IThreadWorkerCallbacks* callbacks);
~CompatWorker();
void MakeThread(IThread *pThread) override;
IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags) override;
IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params) override;
void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) override;
unsigned int RunFrame() override;
bool Pause() override;
bool Unpause() override;
bool Start() override;
bool Stop(bool flush) override;
WorkerState GetStatus(unsigned int *numThreads) override;
void SetMaxThreadsPerFrame(unsigned int threads) override;
void SetThinkTimePerFrame(unsigned int thinktime) override;
private:
void Flush();
void Worker();
void RunWork(SWThreadHandle* handle);
void RunWorkLocked(std::unique_lock<std::mutex>* lock, SWThreadHandle* handle);
private:
IThreadWorkerCallbacks* callbacks_;
WorkerState state_;
std::mutex mutex_;
std::condition_variable work_cv_;
std::deque<SWThreadHandle*> work_;
std::unique_ptr<std::thread> thread_;
std::atomic<unsigned int> jobs_per_wakeup_;
std::atomic<unsigned int> wait_between_jobs_;
};
CompatWorker::CompatWorker(IThreadWorkerCallbacks* callbacks)
: callbacks_(callbacks),
state_(Worker_Stopped),
jobs_per_wakeup_(SM_DEFAULT_THREADS_PER_FRAME),
wait_between_jobs_(DEFAULT_THINK_TIME_MS)
{
}
CompatWorker::~CompatWorker()
{
Stop(false /* ignored */);
Flush();
}
bool CompatWorker::Start()
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ != Worker_Stopped)
return false;
thread_ = ke::NewThread("SM CompatWorker Thread", [this]() -> void {
Worker();
});
state_ = Worker_Running;
return true;
}
bool CompatWorker::Stop(bool)
{
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ <= Worker_Stopped)
return false;
state_ = Worker_Stopped;
work_cv_.notify_all();
}
thread_->join();
thread_ = nullptr;
Flush();
return true;
}
bool CompatWorker::Pause()
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ != Worker_Running)
return false;
state_ = Worker_Paused;
work_cv_.notify_all();
return true;
}
bool CompatWorker::Unpause()
{
std::lock_guard<std::mutex> lock(mutex_);
if (state_ != Worker_Paused)
return false;
state_ = Worker_Running;
work_cv_.notify_all();
return true;
}
void CompatWorker::Flush()
{
while (!work_.empty()) {
auto handle = ke::PopFront(&work_);
handle->GetThread()->OnTerminate(handle, true);
if (handle->m_params.flags & Thread_AutoRelease)
delete handle;
}
}
void CompatWorker::Worker()
{
// Note: this must be first to ensure an ordering between Worker() and
// Start(). It must also be outside of the loop to ensure the lock is
// held across wakeup and retesting the predicates.
std::unique_lock<std::mutex> lock(mutex_);
if (callbacks_) {
lock.unlock();
callbacks_->OnWorkerStart(this);
lock.lock();
}
typedef std::chrono::system_clock Clock;
typedef std::chrono::time_point<Clock> TimePoint;
auto can_work = [this]() -> bool {
return state_ == Worker_Running && !work_.empty();
};
ke::Maybe<TimePoint> wait;
unsigned int work_in_frame = 0;
for (;;) {
if (state_ == Worker_Stopped)
break;
if (!can_work()) {
// Wait for work or a Stop.
work_cv_.wait(lock);
continue;
}
if (wait.isValid()) {
// Wait until the specified time has passed. If we wake up with a
// timeout, then the wait has elapsed, so reset the holder.
if (work_cv_.wait_until(lock, wait.get()) == std::cv_status::timeout)
wait = ke::Nothing();
continue;
}
assert(state_ == Worker_Running);
assert(!work_.empty());
SWThreadHandle* handle = ke::PopFront(&work_);
RunWorkLocked(&lock, handle);
work_in_frame++;
// If we've reached our max jobs per "frame", signal that the next
// immediate job must be delayed. We retain the old ThreadWorker
// behavior by checking if the queue has more work. Thus, a delay
// only occurs if two jobs would be processed in the same wakeup.
if (work_in_frame >= jobs_per_wakeup_ && wait_between_jobs_ && can_work())
wait = ke::Some(Clock::now() + std::chrono::milliseconds(wait_between_jobs_));
}
assert(lock.owns_lock());
while (!work_.empty()) {
SWThreadHandle* handle = ke::PopFront(&work_);
RunWorkLocked(&lock, handle);
}
}
unsigned int CompatWorker::RunFrame()
{
unsigned int nprocessed = 0;
for (unsigned int i = 1; i <= jobs_per_wakeup_; i++) {
SWThreadHandle* handle;
{
std::lock_guard<std::mutex> lock(mutex_);
if (work_.empty())
break;
handle = ke::PopFront(&work_);
}
RunWork(handle);
nprocessed++;
}
return nprocessed;
}
void CompatWorker::RunWorkLocked(std::unique_lock<std::mutex>* lock, SWThreadHandle* handle)
{
lock->unlock();
RunWork(handle);
lock->lock();
}
void CompatWorker::RunWork(SWThreadHandle* handle)
{
bool autorelease = !!(handle->m_params.flags & Thread_AutoRelease);
handle->m_state = Thread_Running;
handle->GetThread()->RunThread(handle);
handle->m_state = Thread_Done;
handle->GetThread()->OnTerminate(handle, false);
if (autorelease)
delete handle;
}
void CompatWorker::MakeThread(IThread *pThread)
{
ThreadParams params;
params.flags = Thread_AutoRelease;
MakeThread(pThread, &params);
}
IThreadHandle *CompatWorker::MakeThread(IThread *pThread, ThreadFlags flags)
{
ThreadParams params;
params.flags = flags;
return MakeThread(pThread, &params);
}
IThreadHandle *CompatWorker::MakeThread(IThread *pThread, const ThreadParams *params)
{
std::lock_guard<std::mutex> lock(mutex_);
ThreadParams def_params;
if (!params)
params = &def_params;
if (state_ <= Worker_Stopped)
return nullptr;
SWThreadHandle* handle = new SWThreadHandle(this, params, pThread);
work_.push_back(handle);
work_cv_.notify_one();
return handle;
}
void CompatWorker::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min)
{
min = ThreadPrio_Normal;
max = ThreadPrio_Normal;
}
void CompatWorker::SetMaxThreadsPerFrame(unsigned int threads)
{
jobs_per_wakeup_ = threads;
}
void CompatWorker::SetThinkTimePerFrame(unsigned int thinktime)
{
wait_between_jobs_ = thinktime;
}
WorkerState CompatWorker::GetStatus(unsigned int *numThreads)
{
std::lock_guard<std::mutex> lock(mutex_);
// This number is meaningless and the status is racy.
if (numThreads)
*numThreads = jobs_per_wakeup_;
return state_;
}
class CompatThread final : public IThreadHandle
{
public:
CompatThread(IThread* callbacks, const ThreadParams* params);
bool WaitForThread() override;
void DestroyThis() override;
IThreadCreator *Parent() override;
void GetParams(ThreadParams *ptparams) override;
ThreadPriority GetPriority() override;
bool SetPriority(ThreadPriority prio) override;
ThreadState GetState() override;
bool Unpause() override;
private:
void Run();
private:
IThread* callbacks_;
ThreadParams params_;
std::unique_ptr<std::thread> thread_;
std::mutex mutex_;
std::condition_variable check_cv_;
std::atomic<bool> finished_;
};
CompatThread::CompatThread(IThread* callbacks, const ThreadParams* params)
: callbacks_(callbacks),
params_(*params)
{
if (!(params_.flags & Thread_CreateSuspended))
Unpause();
}
bool CompatThread::Unpause()
{
std::unique_lock<std::mutex> lock(mutex_);
if (thread_)
return false;
thread_ = ke::NewThread("SM CompatThread", [this]() -> void {
Run();
});
return true;
}
void CompatThread::Run()
{
// Create an ordering between when the thread runs and when thread_ is assigned.
std::unique_lock<std::mutex> lock(mutex_);
lock.unlock();
callbacks_->RunThread(this);
finished_ = true;
callbacks_->OnTerminate(this, false);
if (params_.flags & Thread_AutoRelease) {
// There should be no handles outstanding, so it's safe to self-destruct.
thread_->detach();
delete this;
return;
}
lock.lock();
callbacks_ = nullptr;
check_cv_.notify_all();
}
bool CompatThread::WaitForThread()
{
std::unique_lock<std::mutex> lock(mutex_);
for (;;) {
// When done, callbacks are unset. If paused, this will deadlock.
if (!callbacks_)
break;
check_cv_.wait(lock);
}
thread_->join();
return true;
}
ThreadState CompatThread::GetState()
{
std::unique_lock<std::mutex> lock(mutex_);
if (!thread_)
return Thread_Paused;
return finished_ ? Thread_Done : Thread_Running;
}
void CompatThread::DestroyThis()
{
delete this;
}
ThreadPriority CompatThread::GetPriority()
{
return ThreadPrio_Normal;
}
bool CompatThread::SetPriority(ThreadPriority prio)
{
return prio == ThreadPrio_Normal;
}
IThreadCreator *CompatThread::Parent()
{
return g_pThreader;
}
void CompatThread::GetParams(ThreadParams *ptparams)
{
*ptparams = params_;
}
class CompatMutex : public IMutex
{
public:
bool TryLock() {
return mutex_.try_lock();
}
void Lock() {
mutex_.lock();
}
void Unlock() {
mutex_.unlock();
}
void DestroyThis() {
delete this;
}
private:
std::mutex mutex_;
};
class CompatThreader final : public IThreader
{
public:
void MakeThread(IThread *pThread) override;
IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags) override;
IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params) override;
void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) override;
IMutex *MakeMutex() override;
void ThreadSleep(unsigned int ms) override;
IEventSignal *MakeEventSignal() override;
IThreadWorker *MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded) override;
void DestroyWorker(IThreadWorker *pWorker) override;
} sCompatThreader;
void CompatThreader::MakeThread(IThread *pThread)
{
ThreadParams params;
params.flags = Thread_AutoRelease;
MakeThread(pThread, &params);
}
IThreadHandle *CompatThreader::MakeThread(IThread *pThread, ThreadFlags flags)
{
ThreadParams params;
params.flags = flags;
return MakeThread(pThread, &params);
}
IThreadHandle *CompatThreader::MakeThread(IThread *pThread, const ThreadParams *params)
{
ThreadParams def_params;
if (!params)
params = &def_params;
return new CompatThread(pThread, params);
}
void CompatThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min)
{
min = ThreadPrio_Normal;
max = ThreadPrio_Normal;
}
IMutex *CompatThreader::MakeMutex()
{
return new CompatMutex();
}
void CompatThreader::ThreadSleep(unsigned int ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
class CompatEventSignal final : public IEventSignal
{
public:
void Wait() override {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock);
}
void Signal() override {
std::lock_guard<std::mutex> lock(mutex_);
cv_.notify_all();
}
void DestroyThis() override {
delete this;
}
private:
std::mutex mutex_;
std::condition_variable cv_;
};
IEventSignal *CompatThreader::MakeEventSignal()
{
return new CompatEventSignal();
}
IThreadWorker *CompatThreader::MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded)
{
if (!threaded)
return new BaseWorker(hooks);
return new CompatWorker(hooks);
}
void CompatThreader::DestroyWorker(IThreadWorker *pWorker)
{
delete pWorker;
}
IThreader *g_pThreader = &sCompatThreader;
class RegThreadStuff : public SMGlobalClass
{

View File

@ -32,49 +32,13 @@
#ifndef _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H
#define _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H
#include <mutex>
#include <IThreader.h>
#include <am-thread-utils.h>
#include <am-utility.h>
using namespace SourceMod;
class CompatMutex : public IMutex
{
public:
bool TryLock() {
return mutex_.TryLock();
}
void Lock() {
mutex_.Lock();
}
void Unlock() {
mutex_.Unlock();
}
void DestroyThis() {
delete this;
}
private:
ke::Mutex mutex_;
};
class CompatCondVar : public IEventSignal
{
public:
void Wait() {
ke::AutoLock lock(&cv_);
cv_.Wait();
}
void Signal() {
ke::AutoLock lock(&cv_);
cv_.Notify();
}
void DestroyThis() {
delete this;
}
private:
ke::ConditionVariable cv_;
};
extern IThreader *g_pThreader;
#endif //_INCLUDE_SOURCEMOD_THREAD_SUPPORT_H

View File

@ -56,6 +56,7 @@
#include "LibrarySys.h"
#include "RootConsoleMenu.h"
#include "CellArray.h"
#include "smn_entitylump.h"
#include <bridge/include/BridgeAPI.h>
#include <bridge/include/IProviderCallbacks.h>
@ -89,6 +90,8 @@ CNativeOwner g_CoreNatives;
PseudoAddressManager pseudoAddr;
#endif
EntityLumpParseResult lastParseResult;
static void AddCorePhraseFile(const char *filename)
{
g_pCorePhrases->AddPhraseFile(filename);
@ -135,6 +138,35 @@ static uint32_t ToPseudoAddress(void *addr)
#endif
}
static void SetEntityLumpWritable(bool writable)
{
g_bLumpAvailableForWriting = writable;
// write-lock causes the map entities to be serialized out to string
if (!writable)
{
g_strMapEntities = lumpmanager->Dump();
}
}
static bool ParseEntityLumpString(const char *pMapEntities, int &status, size_t &position)
{
lastParseResult = lumpmanager->Parse(pMapEntities);
status = static_cast<int>(lastParseResult.m_Status);
position = static_cast<size_t>(lastParseResult.m_Position);
return lastParseResult;
}
// returns nullptr if the original lump failed to parse
static const char* GetEntityLumpString()
{
if (!lastParseResult)
{
return nullptr;
}
return g_strMapEntities.c_str();
}
// Defined in smn_filesystem.cpp.
extern bool OnLogPrint(const char *msg);
@ -170,6 +202,9 @@ static sm_logic_t logic =
CellArray::Free,
FromPseudoAddress,
ToPseudoAddress,
SetEntityLumpWritable,
ParseEntityLumpString,
GetEntityLumpString,
&g_PluginSys,
&g_ShareSys,
&g_Extensions,

View File

@ -26,16 +26,17 @@
// or <http://www.sourcemod.net/license.php>.
#include "frame_tasks.h"
#include <am-vector.h>
#include <utility>
using namespace SourceMod;
ke::Vector<ke::Lambda<void()>> sNextTasks;
ke::Vector<ke::Lambda<void()>> sWorkTasks;
std::vector<ke::Function<void()>> sNextTasks;
std::vector<ke::Function<void()>> sWorkTasks;
void
SourceMod::ScheduleTaskForNextFrame(ke::Lambda<void()>&& task)
SourceMod::ScheduleTaskForNextFrame(ke::Function<void()>&& task)
{
sNextTasks.append(ke::Forward<decltype(task)>(task));
sNextTasks.push_back(std::forward<decltype(task)>(task));
}
void
@ -45,11 +46,11 @@ SourceMod::RunScheduledFrameTasks(bool simulating)
return;
// Swap.
ke::Vector<ke::Lambda<void()>> temp(ke::Move(sNextTasks));
sNextTasks = ke::Move(sWorkTasks);
sWorkTasks = ke::Move(temp);
std::vector<ke::Function<void()>> temp(std::move(sNextTasks));
sNextTasks = std::move(sWorkTasks);
sWorkTasks = std::move(temp);
for (size_t i = 0; i < sWorkTasks.length(); i++)
for (size_t i = 0; i < sWorkTasks.size(); i++)
sWorkTasks[i]();
sWorkTasks.clear();
}
}

View File

@ -31,10 +31,10 @@
namespace SourceMod {
void ScheduleTaskForNextFrame(ke::Lambda<void()>&& task);
void ScheduleTaskForNextFrame(ke::Function<void()>&& task);
void RunScheduledFrameTasks(bool simulating);
}
#endif // _include_sourcemod_logic_frame_tasks_h_
#endif // _include_sourcemod_logic_frame_tasks_h_

View File

@ -89,6 +89,6 @@ unsigned int UTIL_CRC32(const void *pdata, size_t data_length)
crc = CRCTable[c] ^ (crc >> 8);
}
return crc;
return ~crc;
}

View File

@ -75,7 +75,11 @@ static cell_t CreateArray(IPluginContext *pContext, const cell_t *params)
if (params[2])
{
array->resize(params[2]);
if (!array->resize(params[2]))
{
delete array;
return pContext->ThrowNativeError("Failed to resize array to startsize \"%u\".", params[2]);
}
}
Handle_t hndl = handlesys->CreateHandle(htCellArray, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
@ -277,7 +281,19 @@ static cell_t GetArrayString(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
}
cell_t *blk = array->at(idx);
// the blocknumber is not guaranteed to always be passed
size_t blocknumber = 0;
if (params[0] >= 5)
{
blocknumber = (size_t)params[5];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
cell_t *blk = &array->base()[idx * array->blocksize() + blocknumber];
size_t numWritten = 0;
pContext->StringToLocalUTF8(params[3], params[4], (char *)blk, &numWritten);
@ -303,7 +319,19 @@ static cell_t GetArrayArray(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
}
cell_t *blk = array->at(idx);
// the blocknumber is not guaranteed to always be passed
size_t blocknumber = 0;
if (params[0] >= 5)
{
blocknumber = (size_t)params[5];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
cell_t *blk = &array->base()[idx * array->blocksize() + blocknumber];
size_t indexes = array->blocksize();
if (params[4] != -1 && (size_t)params[4] <= array->blocksize())
{
@ -375,12 +403,30 @@ static cell_t SetArrayString(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
}
cell_t *blk = array->at(idx);
// the blocknumber is not guaranteed to always be passed
size_t blocknumber = 0;
if (params[0] >= 5)
{
blocknumber = (size_t)params[5];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
cell_t *blk = &array->base()[idx * array->blocksize() + blocknumber];
char *str;
pContext->LocalToString(params[3], &str);
return strncopy((char *)blk, str, array->blocksize() * sizeof(cell_t));
size_t maxlength = array->blocksize() * sizeof(cell_t);
if (params[0] >= 4 && params[4] != -1 && (size_t)params[4] <= array->blocksize())
{
maxlength = (size_t)params[4];
}
return strncopy((char*)blk, str, maxlength);
}
static cell_t SetArrayArray(IPluginContext *pContext, const cell_t *params)
@ -401,7 +447,19 @@ static cell_t SetArrayArray(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
}
cell_t *blk = array->at(idx);
// the blocknumber is not guaranteed to always be passed
size_t blocknumber = 0;
if (params[0] >= 5)
{
blocknumber = (size_t)params[5];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
cell_t *blk = &array->base()[idx * array->blocksize() + blocknumber];
size_t indexes = array->blocksize();
if (params[4] != -1 && (size_t)params[4] <= array->blocksize())
{
@ -529,12 +587,24 @@ static cell_t FindStringInArray(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
// the blocknumber is not guaranteed to always be passed
size_t blocknumber = 0;
if (params[0] >= 3)
{
blocknumber = (size_t)params[3];
}
if (blocknumber >= array->blocksize())
{
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", blocknumber, array->blocksize());
}
char *str;
pContext->LocalToString(params[2], &str);
for (unsigned int i = 0; i < array->size(); i++)
{
const char *array_str = (const char *)array->at(i);
const char *array_str = (const char *)&array->base()[i * array->blocksize() + blocknumber];
if (strcmp(str, array_str) == 0)
{
return (cell_t) i;

View File

@ -83,6 +83,48 @@ static cell_t CreateStack(IPluginContext *pContext, const cell_t *params)
return hndl;
}
static cell_t ClearStack(IPluginContext *pContext, const cell_t *params)
{
CellArray *array;
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellStack, &sec, (void **)&array)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
array->clear();
return 1;
}
static cell_t CloneStack(IPluginContext *pContext, const cell_t *params)
{
CellArray *oldArray;
HandleError err;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellStack, &sec, (void **)&oldArray)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
ICellArray *array = oldArray->clone();
if (!array)
{
return pContext->ThrowNativeError("Failed to clone stack. Out of memory.");
}
Handle_t hndl = handlesys->CreateHandle(htCellStack, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (!hndl)
{
delete array;
}
return hndl;
}
static cell_t PushStackCell(IPluginContext *pContext, const cell_t *params)
{
CellArray *array;
@ -198,9 +240,9 @@ static cell_t PopStackCell(IPluginContext *pContext, const cell_t *params)
}
else
{
if (idx >= array->blocksize() * 4)
if (idx >= array->blocksize() * sizeof(cell_t))
{
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4);
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * sizeof(cell_t));
}
*buffer = (cell_t)*((char *)blk + idx);
}
@ -314,8 +356,8 @@ static cell_t ArrayStack_Pop(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize());
rval = blk[idx];
} else {
if (idx >= array->blocksize() * 4)
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4);
if (idx >= array->blocksize() * sizeof(cell_t))
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * sizeof(cell_t));
rval = (cell_t)*((char *)blk + idx);
}
@ -323,6 +365,32 @@ static cell_t ArrayStack_Pop(IPluginContext *pContext, const cell_t *params)
return rval;
}
static cell_t ArrayStack_Top(IPluginContext *pContext, const cell_t *params)
{
OpenHandle<CellArray> array(pContext, params[1], htCellStack);
if (!array.Ok())
return 0;
if (array->size() == 0)
return pContext->ThrowNativeError("stack is empty");
cell_t *blk = array->at(array->size() - 1);
size_t idx = (size_t)params[2];
cell_t rval;
if (params[3] == 0) {
if (idx >= array->blocksize())
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize());
rval = blk[idx];
} else {
if (idx >= array->blocksize() * sizeof(cell_t))
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * sizeof(cell_t));
rval = (cell_t)*((char *)blk + idx);
}
return rval;
}
static cell_t ArrayStack_PopString(IPluginContext *pContext, const cell_t *params)
{
OpenHandle<CellArray> array(pContext, params[1], htCellStack);
@ -345,6 +413,27 @@ static cell_t ArrayStack_PopString(IPluginContext *pContext, const cell_t *param
return 1;
}
static cell_t ArrayStack_TopString(IPluginContext *pContext, const cell_t *params)
{
OpenHandle<CellArray> array(pContext, params[1], htCellStack);
if (!array.Ok())
return 0;
if (array->size() == 0)
return pContext->ThrowNativeError("stack is empty");
size_t idx = array->size() - 1;
cell_t *blk = array->at(idx);
cell_t *pWritten;
pContext->LocalToPhysAddr(params[4], &pWritten);
size_t numWritten;
pContext->StringToLocalUTF8(params[2], params[3], (char *)blk, &numWritten);
*pWritten = (cell_t)numWritten;
return 1;
}
static cell_t ArrayStack_PopArray(IPluginContext *pContext, const cell_t *params)
{
OpenHandle<CellArray> array(pContext, params[1], htCellStack);
@ -369,6 +458,29 @@ static cell_t ArrayStack_PopArray(IPluginContext *pContext, const cell_t *params
return 0;
}
static cell_t ArrayStack_TopArray(IPluginContext *pContext, const cell_t *params)
{
OpenHandle<CellArray> array(pContext, params[1], htCellStack);
if (!array.Ok())
return 0;
if (array->size() == 0)
return pContext->ThrowNativeError("stack is empty");
cell_t *addr;
pContext->LocalToPhysAddr(params[2], &addr);
size_t idx = array->size() - 1;
cell_t *blk = array->at(idx);
size_t indexes = array->blocksize();
if (params[3] != -1 && (size_t)params[3] <= array->blocksize())
indexes = params[3];
memcpy(addr, blk, sizeof(cell_t) * indexes);
return 0;
}
static cell_t GetStackBlockSize(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
@ -384,9 +496,25 @@ static cell_t GetStackBlockSize(IPluginContext *pContext, const cell_t *params)
return array->blocksize();
}
static cell_t GetStackSize(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
CellArray *array;
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
if ((err = handlesys->ReadHandle(params[1], htCellStack, &sec, (void **)&array))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
}
return array->size();
}
REGISTER_NATIVES(cellStackNatives)
{
{"CreateStack", CreateStack},
{"CloneStack", CloneStack},
{"IsStackEmpty", IsStackEmpty},
{"PopStackArray", PopStackArray},
{"PopStackCell", PopStackCell},
@ -398,14 +526,20 @@ REGISTER_NATIVES(cellStackNatives)
// Transitional syntax support.
{"ArrayStack.ArrayStack", CreateStack},
{"ArrayStack.Clear", ClearStack},
{"ArrayStack.Clone", CloneStack},
{"ArrayStack.Pop", ArrayStack_Pop},
{"ArrayStack.Top", ArrayStack_Top},
{"ArrayStack.PopString", ArrayStack_PopString},
{"ArrayStack.TopString", ArrayStack_TopString},
{"ArrayStack.PopArray", ArrayStack_PopArray},
{"ArrayStack.TopArray", ArrayStack_TopArray},
{"ArrayStack.Push", PushStackCell},
{"ArrayStack.PushString", PushStackString},
{"ArrayStack.PushArray", PushStackArray},
{"ArrayStack.Empty.get", IsStackEmpty},
{"ArrayStack.BlockSize.get", GetStackBlockSize},
{"ArrayStack.Length.get", GetStackSize},
{NULL, NULL},
};

Some files were not shown because too many files have changed in this diff Show More