Compare commits

...
715 Commits
Author SHA1 Message Date
Your Name 0cf8f2b11f update amtl 2023-08-30 22:18:47 +02:00
BotoX 91cc6b300c Add "Restart Current Map" to sm_map menu. 2023-08-30 22:08:46 +02:00
BotoX ec3351a320 Fix GetClientCount(false) 2023-08-30 22:08:46 +02:00
Your Name aa2a71fc67 Add damageCustom argument to SDKHooks_TakeDamage native.
# Conflicts:
#	extensions/sdkhooks/natives.cpp
#	plugins/include/sdkhooks.inc
2023-08-30 22:08:46 +02:00
BotoX 142e76bd92 fix IsMapValid behavior change by a2246af121 2023-08-30 22:08:46 +02:00
BotoX 632429700b Changes on sm_*say 2023-08-30 22:08:46 +02:00
BotoX a8d95ca044 Add GetClientIClient native. 2023-08-30 22:08:46 +02:00
Your Name 28b4b248f7 Extend function calling API for natives and allow catching exceptions.
Change sourcepawn url.

# Conflicts:
#	.gitmodules
#	core/logic/AMBuilder
2023-08-30 22:08:46 +02:00
BotoX 964c9ae835 Fix @spec not targeting clients in unassigned team. 2023-08-30 22:08:46 +02:00
Your Name d099dbb659 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.

# Conflicts:
#	tools/buildbot/PackageScript
2023-08-30 22:08:46 +02:00
BotoX 688e274c30 Add more macros to CDetour. 2023-08-30 22:08:46 +02:00
Mikusch a5ff53f00f TF2: Add TFCond_ImmuneToPushback condition (#2023)
(cherry picked from commit 531b053096)
2023-08-30 22:08:46 +02:00
Mikusch 38fc001a1d TF2: Update StunPlayer signature (#2024) (fixes #2022)
(cherry picked from commit 0dafe04899)
2023-08-30 22:08:46 +02:00
data-bomb 4b949e8228 Add LookupAttachment signature for ND (#1942)
Adds the LookupAttachment signature for Nuclear Dawn

(cherry picked from commit a9a1939f75)
2023-08-30 22:08:46 +02:00
Dysphie 080c4bb8e2 Fix LookupAttachment signature for NMRiH (#1940)
(cherry picked from commit 693e584dcd)
2023-08-30 22:08:46 +02:00
Nick Hastings 149b16cbf3 Enable ShowMenu support for Reactive Drop (see #1938)
Note: Currently only works on beta version of game
(cherry picked from commit 397b70add0)
2023-08-30 22:08:46 +02:00
Corey D b940b01be4 Fix LookupAttachment signature (#1933)
(cherry picked from commit a0eb6a9550)
2023-08-30 22:08:46 +02:00
Poggu 45258b8561 Fix EntityFactoryCaller signature (#1925)
(cherry picked from commit 6e839a95c6)
2023-08-30 22:08:46 +02:00
El Diablo ab7b9b231d 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.

(cherry picked from commit 2dcee81f22)
2023-08-30 22:08:46 +02:00
GAMMACASE 76c2addeb9 Gamedata update after 2/2/2023 CSGO update (#1921) 2023-08-30 22:08:46 +02:00
nosoop 7cb7703cf9 Update TF2 gamedata for version 7757534 (2023-01-05) (#1901)
(cherry picked from commit 27b1817b10)
2023-08-30 22:08:46 +02:00
clague c2b204e810 Fix ReadMapList ignoring file's last modified time (#1891)
(cherry picked from commit 0d6179299c)
2023-08-30 22:08:46 +02:00
zer0.k 24c92c021c Add missing null pointer check to protobuf messages (#1883)
(cherry picked from commit ef7d3abefd)
2023-08-30 22:08:46 +02:00
nosoop dd370c3904 entitylump: Output separator as spaces instead of tabs
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.
2023-08-30 22:08:46 +02:00
Erin 384fb668d1 Trigger build for TF2 SDK update 2023-08-30 22:08:46 +02:00
Nick Hastings 1903c0bd2b Trigger build against SDK update 2023-08-30 22:08:46 +02:00
nosoop 13034eff05 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.

(cherry picked from commit 34c8220e5d)
2023-08-30 22:08:46 +02:00
Nick Hastings a4703722be Update TF2 gamedata.
(cherry picked from commit 89bd4d7329)
2023-08-30 22:08:46 +02:00
nosoop 2e13460823 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.
2023-08-30 22:08:46 +02:00
nosoop d356d80537 Add functions for working with entity lumps (#1673) 2023-08-30 22:08:45 +02:00
Erin Baker 412759cbc4 Enable CI on release branches (#1854) 2023-08-30 22:08:45 +02:00
Maxim Telezhenko 08841227a4 Fix DHooks jit code stack memory alignment (#1849) 2023-08-30 22:08:45 +02:00
Nick Hastings 18b2ffa786 Trigger build for hl2sdk-csgo update 2023-08-30 22:08:45 +02:00
Vauff 3e9a48c778 Correct missed team offsets in CheckRestartRound (#1844) 2023-08-30 22:08:45 +02:00
Nick Hastings 339fd85c6f Trigger build for hl2sdk-csgo update 2023-08-30 22:08:45 +02:00
Vauff 59144cf868 Update gamedata for 2022/10/21 CS:GO update (#1842) 2023-08-30 22:08:45 +02:00
David Anderson ad3a44427a Update SourcePawn. 2023-08-30 22:08:45 +02:00
David Anderson 737d8c3240 Fix build. 2023-08-30 22:08:45 +02:00
David Anderson 4688783baf Revert "Introduce a pbproxy library to solve macOS linker issues."
This reverts commit e5ddbd9886.
2023-08-30 22:08:45 +02:00
David Anderson ba5c6ad07c Revert "Fix use-after-free when creating custom user messages"
This reverts commit 15450a6d0c.
2023-08-30 22:08:45 +02:00
David Anderson 69045ccd52 Update SourcePawn on 1.11-dev. 2023-08-30 22:08:45 +02:00
42 cf39d4b820 Fix support for SDKCall returning non-networked entity (#1797)
(cherry picked from commit 625c7a98f2)
2023-08-30 22:08:45 +02:00
peace-maker cb218a4911 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.
2023-08-30 22:08:45 +02:00
peace-maker 344284196c Update SQLite library to 3.38.5 (#1792)
Bring in the changes of the last four years of SQLite development.

Fixes #1728
Fixes #1592
2023-08-30 22:08:45 +02:00
Nick Hastings e7c0726acd Trigger build against hl2sdk-csgo changes. 2023-08-30 22:08:45 +02:00
Nick Hastings d0b34e783d Trigger build against hl2sdk-csgo changes.
(cherry picked from commit 5714e7695a653dcade1832cb482855e77b5f13dd)
2023-08-30 22:08:45 +02:00
Deathreus d481e5fcb7 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
2023-08-30 22:08:45 +02:00
dysphie 1459c10112 Add sdktools gamerules support for NMRiH (#1784) 2023-08-30 22:08:45 +02:00
David Anderson 8b9131f6e4 Update SourcePawn. 2023-08-30 22:08:45 +02:00
Margen67 ce1a21ba0b .gitmodules: Make submodules shallow (#1769) 2023-08-30 22:08:45 +02:00
Vauff e2237ce875 Expand ShowHudText message buffer for Protobuf games (#1777)
* Expand ShowHudText message buffer for CS:GO

* Expand buffer on Blade Symphony too
2023-08-30 22:08:45 +02:00
peace-maker 250bbf5955 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.
2023-08-30 22:08:45 +02:00
peace-maker 0478912d73 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.
2023-08-30 22:08:45 +02:00
Nick Hastings d50e4b7a2f Update TF2 gamedata. 2023-08-30 22:08:45 +02:00
Nick Hastings abf59a4f4b Update Reactive Drop gamedata (closes #1771) 2023-08-30 22:08:45 +02:00
Headline 9b80e46097 Lift raw pointers out of DatabaseConfBuilder (#1637)
* Lift raw pointers out of DatabaseConfBuilder

* Maybe: not
2023-08-30 22:08:45 +02:00
David Anderson bbbaebd32a Update SourcePawn. 2023-08-30 22:08:45 +02:00
Vauff 1204d1fd0f Update WriteBaselines signature for CS:GO update (#1766) 2023-08-30 22:08:45 +02:00
Bone ef34cc67b6 fix WeaponPrice offset on windows (#1765) 2023-08-30 22:08:45 +02:00
Psykotikism 25184aa9c7 Add "DispatchKeyValueInt" stock (#1764) 2023-08-30 22:08:45 +02:00
Arron Vinyard b789d30658 Remove unnecessary timer typeset entry (#1735) 2023-08-30 22:08:45 +02:00
Arron Vinyard 305799b441 NPOTB: Use camel casing for variables in adminhelp.sp (#1750)
This is a stylistic change to ensure more adhered-to consistency throughout base plugins
2023-08-30 22:08:45 +02:00
Sikari 92fc27f67f 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
2023-08-30 22:08:45 +02:00
domino_ 5292721f0b 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.
2023-08-30 22:08:45 +02:00
Mikusch 4e0c47a1d5 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
2023-08-30 22:08:45 +02:00
Mikusch 65b6602a19 SDKTools: Clear gamerules pointer on level shutdown (#1755)
* Clear gamerules pointer on level shutdown

* Move LevelShutdown up
2023-08-30 22:08:45 +02:00
Alienmario 1723f97202 Add recent gamedata for BMS, HL2DM (#1756) 2023-08-30 22:08:45 +02:00
Kyle ec92653bb7 Honor AUTOLOAD_EXTENSIONS in clientprefs include (#1718)
* Optional autoload extensions

* revert autoload for cstrike/tf2
2023-08-30 22:08:45 +02:00
peace-maker b25d25890a 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
2023-08-30 22:08:45 +02:00
peace-maker b27243c3ac 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.
2023-08-30 22:08:45 +02:00
XeroX be648382d7 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
2023-08-30 22:08:45 +02:00
XeroX b5ed266941 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.
2023-08-30 22:08:45 +02:00
MartLegion 2ffaa04130 Moved pvkii section for better readbility (#1744)
Moved pvkii section above for better readbility
(requested by psychonic)
2023-08-30 22:08:45 +02:00
MartLegion eddf9cf843 Added PVKII game with related team colors (beacon) (#1743)
Spectators = Yellow
Pirates = Red
Vikings = Green
Knights = Blue
2023-08-30 22:08:45 +02:00
Gaben 7c1095e4e7 Allow h-flag admins to bypass vote delay (#1733) 2023-08-30 22:08:45 +02:00
Arron Vinyard 686e8b4d7e Add GetCmdArgFloat(Ex) stocks (#1742) 2023-08-30 22:08:45 +02:00
David Anderson edc73649ff Update SourcePawn. 2023-08-30 22:08:45 +02:00
Mikusch 1ad8bb4560 Remove misleading note on DynamicHook.RemoveHook docs (#1725) 2023-08-30 22:08:45 +02:00
James Dickens 7eabeaa384 Fix Int64ToString producing incorrect output (#1723) 2023-08-30 22:08:45 +02:00
rtldg 4547270934 Correct some function docs that return char count (#1721) 2023-08-30 22:08:45 +02:00
V 3a2c5cb0f7 Inline one-expression single-use functions (#1700) 2023-08-30 22:08:45 +02:00
XeroX 7648c7fe03 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.
2023-08-30 22:08:45 +02:00
Corey D ffb6d769e8 Add MAX_AUTHID_LENGTH (#1696) 2023-08-30 22:08:45 +02:00
Erik Minekus 77f999f810 Database.Format destination buffer should not be marked const (#1714) 2023-08-30 22:08:45 +02:00
Corey D c5049990a3 Add bitwise SetBitFlags for AdminId and GroupId (#1677)
* Add bitwise SetFlags for AdminId

* Inline SetFlags natives
2023-08-30 22:08:45 +02:00
XeroX 0e4894e8db 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
2023-08-30 22:08:45 +02:00
Asher Baker 19f1ffa401 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
2023-08-30 22:08:45 +02:00
Asher Baker c206ac3099 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.
2023-08-30 22:08:45 +02:00
Alienmario e03320c80e Enable nextmap for Black Mesa (#1695) 2023-08-30 22:08:45 +02:00
Ҝℴţأķ 2ae4624344 Remove hardcoded question mark from sm_vote (#1699) 2023-08-30 22:08:45 +02:00
66c8005666 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 <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2023-08-30 22:08:45 +02:00
Arron Vinyard 210219030e 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
2023-08-30 22:08:45 +02:00
komashchenko 370fa8209b Fix GetGameSoundParams in CSGO (#1631)
* Fix GetGameSoundParams CSGO

* Added preprocessor comments

* Revert InternalPrecacheScriptSound
2023-08-30 22:08:45 +02:00
peace-maker 88a0a458a6 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
2023-08-30 22:08:45 +02:00
dysphie e4afe31fc0 Add NMRiH support for new collision and attachment natives (#1702)
- Add "GetAttachment" offset
- Add "SetOwnerEntity" offset
- Add "LookupAttachment" signature
2023-08-30 22:08:45 +02:00
Corey D fb0144900f Add ArrayStack.Clear native (#1676) 2023-08-30 22:08:45 +02:00
Mikusch 542b7673d6 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?)
2023-08-30 22:08:45 +02:00
Corey D cc1c02e3de Fix ArrayList return types (#1679)
ArrayList.SetString and ArrayList.SetArray both have int return types but their methodmap natives use void.
2023-08-30 22:08:45 +02:00
naydef 4fc0a85581 Fix crash in non-bypass-hooks DropWeapon implementation (#1672)
Fixes #1670
2023-08-30 22:08:45 +02:00
Nick Hastings 6733c4182d Fix helpers module path lookup on newer Perl versions. 2023-08-30 22:08:45 +02:00
Nick Hastings 28ef59ada5 Missing ;s. 2023-08-30 22:08:45 +02:00
Nicholas Hastings 647e320b57 [Buildbot] Delete package after successful upload. (#1665)
* [Buildbot] Delete package after successful upload.

* Use autodie.

* ;
2023-08-30 22:08:45 +02:00
Nick Hastings cdfbdcc29d Remove dead code. 2023-08-30 22:08:45 +02:00
Nick Hastings 4f94b4f900 Use SteamWorks C++ API in CStrike RulesFix, for better interface compatibility. 2023-08-30 22:08:45 +02:00
Asher Baker 53be0e0812 Upload libsourcepawn Breakpad symbols as well 2023-08-30 22:08:45 +02:00
Asher Baker 88dd142f5c Upload libsourcepawn debug symbols 2023-08-30 22:08:45 +02:00
David Anderson 1d5ec7b982 Update SourcePawn/AMTL to fix build. 2023-08-30 22:08:45 +02:00
Fyren f1a71b0d5a Update SP. 2023-08-30 22:08:45 +02:00
Headline 85b8e09e8a Prevent enum shadowing & pin sourcemod for build fixes (#1661) 2023-08-30 22:08:45 +02:00
David Anderson df9b0ef1b3 Update AMTL. 2023-08-30 22:08:45 +02:00
David Anderson f81412b4fa Fix vote menu handler to work with direct arrays. 2023-08-30 22:08:45 +02:00
David Anderson bd478ebdfc Dedent some menu code for readability. 2023-08-30 22:08:45 +02:00
Benoist a57573864a Fix ShouldCollide originalResult behaviour (#1657) 2023-08-30 22:08:45 +02:00
David Anderson 6b6436db17 Update SourcePawn.
Fix fix an hl2sdk-mock build error.
2023-08-30 22:08:45 +02:00
Stanislav Polshyn c88b18e520 gamedata: Add SetEntityOwner L4D1 offset (#1649) 2023-08-30 22:08:45 +02:00
8c001872a8 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 <[email protected]>
Co-authored-by: Kyle Sanderson <[email protected]>
2023-08-30 22:08:45 +02:00
Adrián 5db571bbc6 menusys: Correct GetMenuExitBackButton returncode (#1646) 2023-08-30 22:08:45 +02:00
peace-maker 1f561b54f3 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.
2023-08-30 22:08:45 +02:00
Peace-Maker 77a0fb4402 Ignore all build* folders in .gitignore
I frequently have one like `buildwin` and `buildlin` for the different OS in WSL.
2023-08-30 22:08:45 +02:00
Peace-Maker c72736dcfa Use SourceMod's version number 2023-08-30 22:08:45 +02:00
Peace-Maker d16f45c4b6 Print OS errors if mprotect fails
This helped catch a bug in DHooks before, so it's worth adding it in here as well.
2023-08-30 22:08:45 +02:00
Peace-Maker e280d33a28 Use JMP patching of CDetour 2023-08-30 22:08:45 +02:00
Peace-Maker fb1d501c24 Add copyright headers 2023-08-30 22:08:45 +02:00
Peace-Maker 3c0f700a6b Import DHooks + Dynamic Detouring
This is the latest DHooks version from https://github.com/peace-maker/DHooks2/tree/1314f2d1b4d870677a1b9f628c770f2799878f9b
2023-08-30 22:08:45 +02:00
Adrián 45e9da90fc Fix translations getting truncated prematurely (#1640) 2023-08-30 22:08:45 +02:00
Nick Hastings a60c443b29 Update Contagion SDKHooks gamedata. 2023-08-30 22:08:45 +02:00
Nick Hastings b064366169 Disable Reserved Slots plugin on Contagion.
(It doesn't have sv_visiblemaxplayers).
2023-08-30 22:08:45 +02:00
Nick Hastings a06a903989 Make separate copy of makesig script for IDA 7.x. 2023-08-30 22:08:45 +02:00
Mikusch e67fdf2331 Update Team Fortress 2 gamedata
Shift virtual offsets up by 3
2023-08-30 22:08:45 +02:00
Nick Hastings e2ca3a9493 Update Contagion SDKTools gamedata. 2023-08-30 22:08:45 +02:00
dysphie 9099634b95 Fix "ForcePlayerSuicide" linux offset in NMRiH (#1627) 2023-08-30 22:08:45 +02:00
David Anderson f182feed9d Add support for ASAN. 2023-08-30 22:08:45 +02:00
David Anderson 02dd6a9c00 Update SourcePawn.
This fixes a few more regressions found in the new compiler.
2023-08-30 22:08:45 +02:00
David Anderson b421a0c594 Update SourcePawn.
This fixes a recent stability regression.
2023-08-30 22:08:45 +02:00
Nick Hastings 9ec5cb819c Fix server binary name for CRC lookups. 2023-08-30 22:08:45 +02:00
Nick Hastings f8e664b797 Invert CRC to match most popular implementations. 2023-08-30 22:08:45 +02:00
dysphie 42575f7cc3 Update gamedata for NMRiH 1.12 (#1623)
* Fix NMRiH's sdktools gamedata

* Fix NMRiH's sdkhooks gamedata

* Fix GroundEntChanged linux offset
2023-08-30 22:08:45 +02:00
David Anderson aa98f60ca3 Update SourcePawn. 2023-08-30 22:08:45 +02:00
David Anderson d5f47a88e6 Switch to clang-8.
This is the new minimum supported Clang version going forward.
2023-08-30 22:08:45 +02:00
FortyTwoFortyTwo 62f08a31fc Fix compiling with tf2_stocks 2023-08-30 22:08:45 +02:00
David Anderson e565c83494 Fix build. 2023-08-30 22:08:45 +02:00
David Anderson a68ff2a587 Fix error return of FormatNativeString. (#1613) 2023-08-30 22:08:45 +02:00
David Anderson 377888276e 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.
2023-08-30 22:08:45 +02:00
Nick Hastings 52b8e95aae Throw error if unknown SDK call type specified. 2023-08-30 22:08:45 +02:00
Nick Hastings bb6e6fd3b6 Reorder SDKCallType enum for better compatibility with existing plugins. 2023-08-30 22:08:45 +02:00
dragokas 0449342bb0 Exposed SDKCall_Server call type to use CBaseServer methods 2023-08-30 22:08:45 +02:00
David Anderson 3171d4fb7b 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.
2023-08-30 22:08:45 +02:00
Kyle 066818fbec Update TerminateRound signature for CSGO 2023-08-30 22:08:45 +02:00
Vladimir 255f5ea745 Add OnClientLanguageChanged() forward (#1597) 2023-08-30 22:08:45 +02:00
Asher Baker 2e763c287c 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.
2023-08-30 22:08:45 +02:00
Nick Hastings 29c33bfcc9 Fix up OnClientSettingsChange logic order.
- Fixes bot name cache not getting updated (see #1579)
- Fixes IClientListener::OnClientSettingsChanged not being called for bots.
2023-08-30 22:08:45 +02:00
Nick Hastings 88bdbf4866 Add support for other Steam ID formats to admin-sql-threaded (#1520). 2023-08-30 22:08:45 +02:00
Nick Hastings 1559b75ae7 Don't cache replay_enable value when evaling whether to use FileExists hook.
Speculative fix for #1581
2023-08-30 22:08:45 +02:00
Nick Hastings ff00e3d036 Mark unloaded extensions as not fully loaded (fixes #1574). 2023-08-30 22:08:45 +02:00
Charles daa1a4df14 Removed incorrect parameter RemoveChangeHook desc 2023-08-30 22:08:45 +02:00
Charles 4e2ace9d2f Removed incorrect parameter in description 2023-08-30 22:08:45 +02:00
Corey D ee722d778f Use words instead of quotes for better clarity
As suggested by sneak-it
2023-08-30 22:08:45 +02:00
Corey D e82f0acdb8 Missed Database.Escape 2023-08-30 22:08:45 +02:00
Corey D 383cd81be6 Clarify SQL_EscapeString usage
It's hard to see the difference between 2 single-quotes and 1 double-quote unless you highlight it.
2023-08-30 22:08:45 +02:00
Nick Hastings 6250db7dba Add missing parameters for CSWeaponDrop. 2023-08-30 22:08:45 +02:00
GAMMACASE 8ae3be7185 Fix CS_OnCSWeaponDrop after latest CSGO update (21/10/21) (#1602) 2023-08-30 22:08:45 +02:00
Vauff 64b897822f Update HandleCommand_Buy_Internal sig and related offsets for CS:GO Win 2023-08-30 22:08:45 +02:00
Vauff 0ad0d839a0 Update CSWeaponDropBB signature for CS:GO Linux 2023-08-30 22:08:45 +02:00
Nick Hastings 55382e0a0f Add option to not bypass hooks with TakeDamage and DropWeapon natives. 2023-08-30 22:08:45 +02:00
Nick Hastings d8b00675d6 Remove obsolete core-legacy checks. 2023-08-30 22:08:45 +02:00
Nick Hastings 633298b6a9 Fix attempting to link win32 libprotobuf on win64. 2023-08-30 22:08:45 +02:00
Nick Hastings 656a9fa94f Use MM:S master for Windows builds (already doing for Linux/Mac). 2023-08-30 22:08:45 +02:00
Nicholas Hastings bb7929594d Typo fix. 2023-08-30 22:08:45 +02:00
Nick Hastings d4545a105e Fix incorrect param types in CS:GO GNI (otherwise broken for x64). 2023-08-30 22:08:45 +02:00
Nick Hastings 1c788b5c0a Remove manual ptr math in vnatives for params not mapped to cell params. 2023-08-30 22:08:45 +02:00
Nick Hastings 7ce8a80bf6 Fix Blade Symphony GiveNamedItem call on x64. 2023-08-30 22:08:45 +02:00
Nick Hastings d0c4657b75 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.
2023-08-30 22:08:45 +02:00
Nick Hastings 167e6d8571 On Linux, link against server libs instead of client (fixes META_CONPRINT). 2023-08-30 22:08:45 +02:00
Nick Hastings b909ea07ec Update multiple SDKTools calls to be x64-safe [sizeof(void*) != sizeof(int)] 2023-08-30 22:08:45 +02:00
Nick Hastings 410eb01f91 Add missing WriteBaselines signatures for Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings f18c6fb9af Fix crash with IBinTools calls on x64 for functions with void return. 2023-08-30 22:08:45 +02:00
Nick Hastings c66175d64e Use new GetIServer call and updated IServerTools calls on Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings a3fbbe41f2 Enable more CS:GO engine logic for Blade Symphony. 2023-08-30 22:08:45 +02:00
Nick Hastings ed5713b3fd Blade Symphony gamedata updates, round 1. 2023-08-30 22:08:45 +02:00
Nick Hastings 4b01feea45 Fix CS:GO transposing of GiveNamedItem last param and return. 2023-08-30 22:08:45 +02:00
Nick Hastings 800f613f92 Enable 64-bit builds for Blade Symphony. 2023-08-30 22:08:44 +02:00
Nick Hastings 4c9e856197 Add .vs and .vscode dirs to .gitignore. 2023-08-30 22:08:44 +02:00
dysphie 0cb3fcaed8 Fix NMRiH's 'GivePlayerAmmo' offset on Windows (#1593) 2023-08-30 22:08:44 +02:00
naydef 02e2659ba5 Update float.inc (#1591) 2023-08-30 22:08:44 +02:00
A1m` 507b09c975 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.
2023-08-30 22:08:44 +02:00
David Anderson 907fa77381 Update bootstrap.pl 2023-08-30 22:08:44 +02:00
David Anderson c5c302dccc Use master for Metamod:Source builds. 2023-08-30 22:08:44 +02:00
David Anderson c49d51206c Don't require hl2sdk-mock if sdks=all 2023-08-30 22:08:44 +02:00
pedrotski f48cbaab75 Update CS:GO Reload gamedata (#1585) 2023-08-30 22:08:44 +02:00
nosoop 61c634a870 TF2Tools: Prevent CalcIsAttackCriticalHelper* from being called twice (#1573)
* Ensure CalcIsAttackCriticalHelper only gets called once

* fixup: move origReturnValue up
2023-08-30 22:08:44 +02:00
David Anderson fec978afb4 Add support for hl2sdk-mock. 2023-08-30 22:08:44 +02:00
David Anderson 74ad70ba30 Add support for hl2sdk-mock. 2023-08-30 22:08:44 +02:00
GAMMACASE fe27c8810e Update gamedata after latest CSGO update (22/09/21) (#1583) 2023-08-30 22:08:44 +02:00
Nick Hastings b4c5726fc2 Trigger build for TF2 SDK INetChannel changes. 2023-08-30 22:08:44 +02:00
Nick Hastings 53c8d5533a Trigger build for TF2 SDK changes. 2023-08-30 22:08:44 +02:00
Bara 4e66452e58 Update sourcepawn 2023-08-30 22:08:44 +02:00
Vladimir 63545eb741 Fix crash in FindSendPropInfo() when the prop was DPT_DataTable (#1575) 2023-08-30 22:08:44 +02:00
Asher Baker a7af242453 Revert "Switch internal SM concept of frames to use Think (#1540)" (#1572)
This reverts commit b383302128.
2023-08-30 22:08:44 +02: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
Vladimirandkomashchenko 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 <[email protected]>
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
VladimirandAsher Baker 54364d213d Fix output hooks when caller/activator are flipped (#1411)
Co-authored-by: Asher Baker <[email protected]>
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
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 <[email protected]>
Co-authored-by: Headline <[email protected]>
Co-authored-by: Accelerator74 <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
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 ShortandKyle Sanderson 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 <[email protected]>
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 DandPeace-Maker 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 <[email protected]>
2021-03-15 12:46:04 -07:00
DeathreusandAsher Baker 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 <[email protected]>

* Update based on notes

* Normalize

* Pass along cookie handle instead of invalid

Co-authored-by: Asher Baker <[email protected]>
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-makerandErik Minekus 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 <[email protected]>
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
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 <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
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 https://github.com/alliedmodders/sourcemod/blob/aae71612731eaa6771067a31f3a7ad34bdb0df28/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 <[email protected]>

* Offset updates for ZPS 3.1

Signed-off-by: Mr.Silence <[email protected]>

* Updated offsets and signatures.

Signed-off-by: Mr.Silence <[email protected]>
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
Rostu13andKyle Sanderson c38b392fdf sdktools: permit symbol signatures on win32 (#1346)
* Fix sig scanner for windows

* fix linux build

* Update vcaller.cpp

Co-authored-by: Kyle Sanderson <[email protected]>
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
ScagsandKyle Sanderson 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 <[email protected]>
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
Accelerator74andKyle Sanderson 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 <[email protected]>
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
BotoXandKyle Sanderson 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 <[email protected]>
2020-08-06 20:31:00 -07:00
BotoXandKyle Sanderson 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 <[email protected]>
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
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 <[email protected]>

* Move CSendPropExtra_UtlVector offset to gamedata

* Update name of offset + add gamedata entry

* tiny nits

Co-authored-by: Asher Baker <[email protected]>
Co-authored-by: Kyle Sanderson <[email protected]>
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
HeadlineandKyle Sanderson 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 <[email protected]>
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 AndersonandKyle Sanderson 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 <[email protected]>
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
HeadlineandKyle Sanderson 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 <[email protected]>
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
ҜℴţأķandKyle Sanderson 611bad4036 ArrayStack: add Clone method (#1304)
* Provide ArrayStack.Clone method

* Clean definition for old syntax.

Co-authored-by: Kyle Sanderson <[email protected]>
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
stickzandKyle Sanderson 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 <[email protected]>
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
AndrewandKyle Sanderson 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 <[email protected]>
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
BotoXandKyle Sanderson 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 <[email protected]>
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 <[email protected]>

* Removed padding.

Co-Authored-By: Headline <[email protected]>

* Removed more padding.

Co-Authored-By: Headline <[email protected]>

Co-authored-by: Headline <[email protected]>
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
ImpactandKyle Sanderson 9f4c6c61d9 Add comment about common.phrases to FindTarget (#1155)
* Add note about common.phrases

* Update helpers.inc

Co-authored-by: Kyle Sanderson <[email protected]>
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
David Anderson c3102c7e9f Bump version to 1.11. 2019-10-18 21:07:36 -07:00
Asher Baker 77f4ddf2c2 Fix virtual SDKCall with object params (#1104)
PR #705 made some changes for 64-bit support that required callers to
provide a buffer for object storage, but only added the required code to
the non-virtual call builder, not the virtual call builder.

Copy it over to CreateValveVCall so that ValveParamToBinParam stops
writing to a null pointer.

Fixes #1059 (Hopefully.)
2019-10-18 00:23:45 +01: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 024ae2b9ed Use map display name for admin map chooser menu (#1097) 2019-10-07 19:15:36 +01:00
Headline 8a4295d699 NPOTB: Correct GitHub's Linguist inaccuracies (#1096) 2019-10-07 19:14:09 +01:00
David Anderson f05e39afe1 Merge pull request #1090 from alliedmodders/update-sp
Update SourcePawn to 1.10-dev tip.
2019-10-01 09:50:25 -07:00
Headline 3b1fb85bb8 Fix quote mismatch (#1092) 2019-09-30 22:43:18 -07:00
Playa a405f85eee Remove SourcePawn JIT license references (#1091) 2019-09-30 15:09:40 -07:00
David Anderson 78511afa4c Update SourcePawn to 1.10-dev tip. 2019-09-28 18:32:47 -07:00
David Anderson c37c4f830f Merge pull request #1088 from alliedmodders/update-sp
Update SourcePawn to the tip of 1.10-dev.
2019-09-26 13:14:29 -07:00
David Anderson 4fe2ae780b Update SourcePawn to the tip of 1.10-dev. 2019-09-25 21:59:32 -07:00
Einyux e02e6bcb4f Add TR_GetHitBoxIndex() native (#1085)
Fixes #1080
2019-09-17 21:24:36 +01:00
David Anderson c7392aeded Fully convert dynamicmenu to transitional enum structs. (#1082) 2019-09-15 14:02:46 -07:00
David Anderson 14247af323 Merge pull request #1081 from alliedmodders/enum-structs
Update SourcePawn.
2019-09-15 11:55:56 -07:00
David Anderson 32cb00a4c6 Update SourcePawn. 2019-09-15 11:38:21 -07:00
BotoX d93f6984cc cstrike: Fix wrong timeleft calculation (#1072)
* Fix wrong timeleft calculation

* reorder function calls
2019-09-02 15:33:28 -07:00
42 207584818f Add RequestFrameCallback optional data param (#1068) 2019-09-02 14:00:22 -07:00
Asher Baker 7a42d6b564 Fix build with latest hl2sdk-csgo 2019-09-02 21:45:04 +01:00
David Anderson 94b3ca08a2 Update SourcePawn & amtl submodules (#1067) 2019-09-01 13:24:39 -07:00
RumbleFrog ab5f00b65b Correct FrameIterator retval regression (#1066) 2019-08-26 19:26:51 -07:00
JoinedSenses b8fd7db58d Create menu if multiple nom matches found (#983)
This change checks the nomination against the map arraylist. If the nomination matches multiple results, those results are then added to a menu to allow the client to select available maps. The maps added to the menu go against the same checks as the normal nomination menu and wont allow nomination of disabled maps.

Example image of /nominate jump_b
https://i.imgur.com/ZdzB0gk.png

If reply source is console, menus wont be generated and it will attempt to nominate as normal.
2019-08-01 13:48:10 +01:00
hydrogen-mvm 22635d8d35 Add File.Flush methodmap (#1056)
This allows us to do FileHandle.Flush() instead of FlushFile(FileHandle) and maintain consistency in always using methodmaps for file I/O operations.
2019-07-31 22:18:07 +00:00
hydrogen-mvm fb9b284bcf Correct malformed Regex documentation (#1055) 2019-07-31 10:48:46 -07:00
nosoop 5293815bf6 Check other filename sources on remote extensions 2019-07-29 04:00:18 -07:00
David Anderson 5868ffd04e Merge pull request #1050 from alliedmodders/update-sp-093edef
Update SourcePawn to 093edef
2019-07-24 09:38:43 -07:00
Asher Baker 8ce99e0540 Link pthread library for Linux binaries 2019-07-24 14:47:13 +01:00
Asher Baker efb02755bc Update SourcePawn to 093edef 2019-07-24 13:51:15 +01:00
Asher Baker fcafd0784c Workaround CS:GO Stringtable bug (#1046)
The cause of the infamous "Index error writing string table baseline"
error appears to be a timing issue between the engine's network message
processing, the stringtable encoding, and command processing when adding
stringtable entries in OnConfigsExecuted.

When the first client connects the map is re-loaded which causes a full
refresh, the game's stringtable entries are added at tick 65, the client
connection is registered at tick 66, and stringtable entries added in
OnConfigsExecuted are registered as being added in tick 67. The engine
later calls WriteBaselines with the client's signon tick, and neglects
to encode the SM added entries as it considers them from the future.

To avoid this, always pass INT_MAX as the current tick when encoding
the baseline, so all stringtable entries are included regardless of when
they were added. Tested on both Windows and Linux.
2019-07-19 09:13:34 +01:00
42 199802150b Update TF2_IgnitePlayer to support setting duration of fire (#1038)
Related to #713
2019-07-05 22:37:25 +01:00
MartLegion e47c1a840a Pad plugin ID based on the total plugin count (#1027)
Closes #994
2019-06-17 01:06:03 +01:00
Nicholas Hastings 14227c04b8 Fix matchmaking_ds bin not being found for gamedata (#1006). (#1028) 2019-06-01 14:30:22 -04:00
Batfoxkid cf0a49881e TFCond Descriptions (#1005)
This is mainly for API (https://sm.alliedmods.net/new-api/tf2/TFCond) to show description of the condition.
2019-06-01 15:43:06 +01:00
JoinedSenses 527a998740 Fix whitespace regression from #1012 (#1026) 2019-05-30 20:11:43 -07:00
JoinedSenses 352f078b07 Add Cookie methodmap (#1012) 2019-05-30 18:27:14 -07:00
JoinedSenses 4f3c3175e6 Create GlobalForward & PrivateForward methodmaps (#1004) 2019-05-29 14:17:42 -07:00
JoinedSenses 81dc80fbd1 Correct Plugin API Documentation Formatting (#1019) 2019-05-28 18:38:58 -07:00
Headline 28036966a6 Fix Miscellaneous Regressions and UBs (#1022) 2019-05-28 18:04:08 -07:00
Headline 2164f5191e Fix GCC 9 Builds (#1024) 2019-05-28 17:57:40 -07:00
Asher Baker 216ba8b4e9 Create FUNDING.yml 2019-05-27 17:15:04 +01:00
Bara 9c55eed549 Add Shield and Bumpmine to cstrike.inc (#991) 2019-05-24 06:03:42 -07:00
Amine B f9ce25a7fe PassType typo fixup (#1018)
* PassType typo fixup

Just some little observations, I will look up for more in other files whenever I can

* Update natives.cpp

* Update outputnatives.cpp
2019-05-15 07:22:34 -04:00
Nicholas Hastings 7e418933e7 Re-add missing GetEconItemView param (fixes #1001). (#1017) 2019-05-14 20:55:44 -04:00
Nicholas Hastings 05b3fc0fb9 Re-add float cast on TF2_AddCondition param (fixes #1013). (#1016)
Broken by ArgScuffer
2019-05-14 19:22:43 -04:00
Bruno Ronning d7990596f9 Update CS:GO "Reload" offset. (#1011) 2019-05-14 18:56:14 -04:00
Kyle Sanderson 86f974f242 NPOTB: Enable cstrike builds w/ Travis-CI. (#1014)
Travis-CI seems to have finally improved their environment so we should be okay to test this unique code-path now with CI.
2019-05-14 11:38:20 -07:00
Loïc 6addc5c8dd Fix wrong formatting to "Vote Delay Minutes" when delay above 60s (#1009)
Fixes #1007
2019-05-13 00:20:47 +01:00
David Anderson 15a7faeafd Merge pull request #1008 from alliedmodders/update-sp
Update SourcePawn to 4cba235.
2019-05-11 14:13:28 -07:00
David Anderson 626963fc49 Update SourcePawn to 4cba235. 2019-05-11 13:53:09 -07:00
JoinedSenses 2e72fc115d Fix malformed documentation indentation in Array.FindValue (#1002)
Modified indention for uniformity
2019-05-09 16:56:17 -07:00
JoinedSenses 26462f2848 Add Sort and SortCustom to ArrayList methodmap (#1003) 2019-05-09 18:44:31 +01:00
Michael Flaherty 9cd2a74271 Fix ArgBuffer String Serialization (#999) 2019-05-04 01:46:55 -07:00
iNilo e1648ba8f9 Add missing semicolon to profiler.inc (#996) 2019-05-03 02:11:03 -07:00
Michael Flaherty dae6d13f06 Fix CSS Builds (#992) 2019-05-01 17:29:55 -04:00
PerfectLaugh ccf1d7abbd Remove unneeded paramter in CS_DropWeapon (#988)
Updated in 4/30/2019 csgo update
2019-05-01 11:38:02 -04:00
Nick Hastings b92381505a Update Linux CSWeaponDropBB signature. 2019-05-01 11:35:13 -04:00
Nicholas Hastings 5ba6b1426b Revert "Update CS:GO CSWeaponDrop Signature (#985)" (#990)
This reverts commit fc7453a6c7.
2019-05-01 11:31:26 -04:00
Anthony fc7453a6c7 Update CS:GO CSWeaponDrop Signature (#985) 2019-04-30 23:36:39 -07:00
404UNF ae00144e84 Add new custom kill identifier for TF2 (#972) 2019-04-22 22:53:31 +01:00
Playa 59baaf1123 Fix param order in CancelClientMenu documentation (#982) 2019-04-22 20:33:27 +01:00
Michael Flaherty e2eac382c0 Add templated helper class to promote type-safety (#965) 2019-04-12 12:10:37 -07:00
Michael Flaherty dd81d7c78f Incorperate Licensing Into Project Tree (#961) 2019-04-12 12:10:13 -07:00
peace-maker e3f4d239f1 IBinTools: Block loading incompatible interface versions (#979)
In #705 SourceMod received support for x64 binaries. The `IBinTools` interface was updated to call functions in 64bit binaries. The `PassInfo` struct's size was increased and the `Create(V)Call()` functions signatures changed, thus making the interface incompatible for consumers which were compiled against an earlier version.

`SMInterface::IsVersionCompatible` wasn't adjusted to that fact, so extensions compiled against pre SM 1.10 could request an `IBinTools` interface pointer, but crash when they try to use it.

This change makes requests to older interface versions invalid, thus letting `RequestInterface` return `NULL` for older extensions. It doesn't fix the backwards incompatibility, but at least makes the problem more blatant, so extensions can handle it themselves.
2019-04-11 14:51:38 -07:00
⭐ B3none 8f0527487f SQL-Admin-Manager: style clean-up (#974)
* Switched to sizeof instead of hard-coding for loop size

* Switched back to pre-increment.

* Dynamically pad variables
2019-04-06 11:47:21 -07:00
Nicholas Hastings 318b36cdcf Trigger build for hl2sdk-csgo update. 2019-03-28 20:00:20 -04:00
TheRandomLabs f4b9dfb2e7 Fix misspelling of "runoff" in mapchooser (#973) 2019-03-23 18:21:54 -07:00
David Anderson 2e11d3400e Fix MSVC2015 build. 2019-03-16 17:25:36 -07:00
David Anderson 364cd094d9 Trigger a build. 2019-03-16 16:57:22 -07:00
David Anderson 2af7600057 Switch to MSVC2015 builders. 2019-03-16 16:21:57 -07:00
komashchenko 8031e42bda pb: Add natives to work with 64 bit values (#943)
* Add natives to work with 64 bit Protobuf values

* Fix linux build

* FIX alignment requirements

* FIX alignment requirements V2

* Remove legacy API

* Inattention
2019-03-04 09:06:43 -08:00
Ҝờţأķ 7f9ceaac06 basevotes: Fix buffer size for workshop obtained levels. (#956) 2019-03-03 15:00:24 -08:00
David Anderson ae0a03d438 Merge pull request #964 from Bara/patch-5
Add missing semicolon
2019-02-19 21:49:07 -08:00
Bara d105ce778d Add missing semicolon
Otherwise you'll get this error:
regex.inc(182) : error 001: expected token: ";", but found "<newline>"

[Example with latest sourcemod (1.10.6383) version](https://travis-ci.org/Bara/TroubleinTerroristTown/jobs/495529570#L640)
2019-02-19 17:21:17 +01:00
David Anderson 653e9325fc Merge pull request #963 from alliedmodders/update-sp
Update SourcePawn.
2019-02-18 12:53:21 -08:00
David Anderson 5d7d7c122e Update SourcePawn. 2019-02-17 21:21:43 -08:00
Ross Bemrose 45fb26d282 Change MultiTargetFilter to a typeset that allows ArrayList as its second argument. (#955) 2019-02-03 13:27:26 -08:00
Asher Baker c501c837d0 Ignore SM site certificate when downloading GeoIP
Build servers don't have a root for it, and the address is internal
2019-01-19 12:34:23 +00:00
Asher Baker 590f71436f Get GeoIP data from SM site for now 2019-01-19 12:18:36 +00:00
Michael Flaherty b7e252dfd6 Prevent FrameIterator OOB Errors (#949) 2019-01-13 13:36:35 -08:00
Nicholas Hastings a2246af121 Unify map lookup logic across tf2esque engines. (fixes #927) (#931) 2019-01-07 18:17:28 -05:00
Bara 3e454db2d8 Add snowball to CSWeaponID (#936) 2019-01-05 14:56:07 -05:00
OkyHp b88e0d7875 Add getter for Event.BroadcastDisabled (#946)
In a pre-hook, the hook param is not reliable if multiple plugins have hooked the same event.
2019-01-03 19:42:42 +00:00
Nick Hastings 821251b176 Fix BfRead.BytesLeft not being able to be optional. (fixes #945). 2019-01-01 15:34:26 -05:00
Deathreus 1b3599c189 Bump non-critical versioning to 1.10 (#939) 2018-12-27 21:02:14 -05:00
Nicholas Hastings 5e7596dea6 Upgrade SQLite from v3.22.0 to v3.26.0. (#935) 2018-12-19 17:26:29 -05:00
David Anderson ab7519530e Merge pull request #934 from alliedmodders/enum-structs
Inauguration of SourcePawn enum structs
2018-12-16 22:43:43 -08:00
David Anderson faa10f52a8 Move dynamicmenu.sp enum structs to transitional syntax. 2018-12-16 18:49:36 -08:00
David Anderson 53df09fbb7 Fix dynamicmenu.sp indentation. 2018-12-16 18:41:55 -08:00
Michael Flaherty c8f3035cb0 Introduce enum structs to core sm plugins 2018-12-15 13:51:03 -08:00
David Anderson f465985370 Update SourcePawn and AMTL to master.
This brings in enum struct support.
2018-12-15 12:17:11 -08:00
David Anderson 470486ba7e Don't double-format in ReplyToCommand. (#932) 2018-12-12 23:30:27 -08:00
Nick Hastings 9a4fe1c475 Fix signed/unsigned compare on Windows. 2018-12-12 20:02:23 -05:00
Ruben Gonzalez dd61223c0b Add new CSGO weapon id's/itemdefs. (#929) 2018-12-12 11:35:36 -08:00
Nick Hastings c196082988 Typo fix for CS:GO POSIX Extinguish gamedata. 2018-12-08 09:05:10 -05:00
Nick Hastings 9e58476631 Update CS:GO contribution score offset. 2018-12-07 07:24:30 -05:00
Nick Hastings 4bebdc18a2 Hmm... 2018-12-06 20:31:29 -05:00
Nick Hastings a01529b210 Add new GiveNamedItem param for CS:GO. 2018-12-06 20:18:21 -05:00
Nick Hastings 0b35c84bf0 Update CS:GO SDKTools and SDKHooks offsets. 2018-12-06 19:23:32 -05:00
Ruben Gonzalez 846a99e57f Merge branch 'master' of github.com:alliedmodders/sourcemod 2018-12-06 18:49:15 -05:00
Ruben Gonzalez c28f957fb3 Update csgo gamedata part 1 2018-12-06 18:47:44 -05:00
peace-maker f0aec6b0b6 Fix typo in fallback to "default" maplists section (#923)
When lookup of a more specialized entry in the maplists.cfg failed, it wouldn't fallback to the "default" section, but just try to parse the same section again.
2018-11-13 16:32:18 +00:00
Ruben Gonzalez d79c5e0dfa Switch CS:GO Clantag set/get to use netprops + offset over sig + offset. (#922)
* Switch CS:GO Clantag set/get to use netprops + offset over sig+offset.

* Make sure the new functions are defined before using them...

* Fix offset name

* Fix incorrectly dereferencing.
2018-11-09 07:13:17 -05:00
Kyle Sanderson cb8d92edeb SDKHooks: Reset global hookid when unhooking in SH. (#916)
Fixes https://github.com/alliedmodders/sourcemod/issues/912
2018-10-28 11:06:57 -07:00
Asher Baker db9925493a Update game.empires.txt 2018-10-14 13:06:30 +01:00
peace-maker 1b795a70b0 Add GameData methodmap (#766) 2018-10-11 20:27:56 -07:00
Kruzya ccfd2ffe38 Added sm_reload_databases (#773)
Added command sm_reload_databases for refreshing "registered" Databases Configurations cache.

Closes #772
2018-10-11 17:36:52 +01:00
Asher Baker c1d235a59c Use natural sorting for map lists (Fixes #892) (#907)
This produces consistently better results, especially for games such as L4D(2).
2018-10-11 15:20:10 +01:00
Asher Baker b5320a4f8a Add a note about FormatTime platform dependence (#908)
This has been another constant source of confusion since the C99 ones were added to the strftime documentation we link.

It'd be nice to have a consistent implementation inside SM in the future.
2018-10-11 11:07:07 +01:00
Asher Baker a1271ec3a8 Handle NULLs in SDKCall string return (Fixes #874) (#906)
`SDKCall` has existing semantics that a `-1` retval indicates null, so use that and writes an empty string to the buffer.

Consumers can tell the difference between `""` and `NULL` by checking if the return value is `0` or `-1`.
2018-10-11 00:18:03 +01:00
Asher Baker a659896f8a Revert "Use natural sorting for map lists (Fixes #892)"
This reverts commit a4e169aa8f.
2018-10-11 00:04:46 +01:00
Asher Baker a4e169aa8f Use natural sorting for map lists (Fixes #892) 2018-10-11 00:03:34 +01:00
Asher Baker 7dd733cf46 Improve CreateNative failure message (#903)
This confuses everyone.
2018-10-10 13:35:17 +01:00
Ruben Gonzalez 6eb319f46e Update CS:GO gamedata. (#904) 2018-10-09 20:18:53 -04:00
SlidyBat 3add38f665 Add additional TraceRay natives (#885)
* More TR natives

* Fix small typo

* Fix another typo

* Fix TR_AllSolid description

* Change Disp to Displacement

* Reorder params in TR_GetSurfaceName

* Fix TR_GetSurfaceName

* Another fix for TR_GetSurfaceName -_-

* Change if block to switch

* Make handle first param in TR_GetSurfaceName

* Reorder TR_GetStartPosition params

* Make TranslatePartitionFlags static

* Change tabs used for formatting to spaces

* Change inline comment style to be consistent with rest of file
2018-10-07 14:15:54 -07:00
Michael Flaherty 34e91fbd81 Restore SQL_CheckConfig Behavior (#873)
* Change lookup strategy for SQL_CheckConfig

* Ensure db conf lookup failures default to ..default

* Revert "Ensure db conf lookup failures default to ..default"

This reverts commit 03dc703daeeeadaa134e2160bf2c7cd812402758.
2018-10-07 14:08:11 -07:00
Michael Flaherty a9ca1a2fd6 NPOTB: Clarify preprocessor logic (#900) 2018-10-05 04:22:29 -07:00
Michael Flaherty a21c7564c1 [NMRiH] Gamedata update (Fixes #898) (#899) 2018-10-05 07:09:19 -04:00
Asher Baker 1ca4517f46 Revert "Remove arch loops from build scripts. (#889)"
This reverts commit 7ed329c11f.
2018-10-04 17:59:40 +00:00
Michael Flaherty 22b43e3869 Fix regression in vstk size (#894)
* Fix regression in vstk size

* Fix macro comment mistake

* More macro comment removals
2018-10-04 08:08:29 -04:00
Michael Flaherty fc14e37d0f Fix CS_TerminateRound calls & detour (#893) 2018-10-03 22:50:31 -04:00
David Anderson 7ed329c11f Remove arch loops from build scripts. (#889)
This simplifies non-sdk build scripts by removing their loops over SM.archs. Instead the top-level
build script will re-evaluate them for each architecture.
2018-09-30 09:59:28 -07:00
Michael Flaherty 6ad6abd319 Update sourcepawn submodule 2018-09-29 21:12:40 -07:00
SlidyBat bece74491d NPOTB: Update sample extension params to be sourcehook-friendly (#886) 2018-09-22 21:55:05 -07:00
Michael Flaherty 990a02dbe0 Fix grammar error in File docs (#883) 2018-09-22 12:27:05 -07:00
Ruben Gonzalez 75193e9ee9 Add new weapons to CSWeaponID enum. (#869) 2018-09-12 10:13:39 -04:00
JoinedSenses a5b22498ce Update to sm_ban, sm_kick, & sm_map in chat with no args to display menu (#838)
This change makes it so /kick, /ban, and /map open the already created methods for displaying their menus when there are no args.

The reason for the feature is to take advantage of menus that already exist and to make the commands easier to use.

The client == 0 check prevents them from opening if it was ran via rcon, sm_rcon, or server command. Client auth is also checked because its a registered admin command.

Usage params will display if client == 0 and args < min

For example, a moderator wants to change a map, instead of running through the admin menu, they can instead type just /map to display available maps and choose one.

If a mod wants to quickly ban or kick someone without having to either run through the admin menu or type it out, they could then type the corresponding commands with no args to open the menus.
2018-09-04 00:41:22 +01:00
Maxim Telezhenko 398522712e Add missing MarkNativeAsOptional for SetAuthIdCookie native (#879) 2018-09-01 00:19:48 +01:00
Asher Baker caf0914289 Remove MAPLIST_FLAG_NO_DEFAULT misuses (#870)
These two base plugins are using the MAPLIST_FLAG_NO_DEFAULT when they shouldn't, which causes much user (and SM developer) confusion as it is completely counter to the documentation.
2018-08-19 12:41:53 +01:00
Michael Flaherty 17700e45b0 Fix SetClanTag CS:GO Windows Signature (#868) 2018-08-15 17:56:04 -07:00
Asher Baker ba8b42ef1b Burninate IDataPack (#864)
This doesn't break any extensions NOT using IDataPack, and we do not know of any that are.

* The extension storage utility of this interface has been broken for the last 9 months, with ISourceMod::CreateDataPack being disabled.
* The plugin interop utility of this interface (its stated purpose) has been broken for the last 11+ years, with ISourceMod::GetDataPackHandleType being disabled.

I imagine it only survived the first cleanup 11 years ago because CSS:DM was using it internally, which it has now been migrated away from.

Compiled all the included extensions without changes (API compat), and loaded extensions build pre-change without issue (ABI compat).
2018-08-13 23:03:48 +01:00
SlidyBat 144fb907f1 Add new Traceray natives (#754) 2018-08-13 23:02:12 +01:00
Asher Baker e7c6b23439 Update blacklist.plugins.txt 2018-08-12 22:32:59 +01:00
Michael Flaherty a65c215217 Fix DataPack Overwrite Regression (#862)
Some tests passed with the implementation prior to this commit, but those were edge cases. Good older behavior is now fully restored
2018-08-11 13:31:33 +01:00
peace-maker 7e3b1c395b Mark new methodmap natives as optional (#867)
The `TopMenu` methodmap natives weren't marked as optional if the extension was optional like the other normal natives.

The `Regex` natives weren't marked as optional at all before if the regex extension was included optionally.

This makes the error message cleaner in case topmenus aren't loaded.
```
adminmenu.smx (Admin Menu): Required extension "TopMenus" file("topmenus.ext") not running
basebans.smx (Basic Ban Commands): Native "TopMenu.Display" was not found
```
2018-08-06 20:11:01 +01:00
Nick Hastings 3511d2a44d Trigger build against hl2sdk-tf2 update. 2018-08-02 17:29:09 -04:00
ddhoward beaf812909 Deprecate FormatUserLogText (#856)
Allow client index 0, too. (Console)
2018-07-31 19:19:18 -07:00
Michael Flaherty 7068c3e865 Fix LogStackTrace Anomaly (#863) 2018-07-31 04:17:36 -07:00
Kyle Sanderson 255b9323cc Flip sm_debug_connect to "1" (#860) 2018-07-27 22:02:31 -07:00
hydrogen-mvm d14c94c353 Add missing return value documentation (#854) 2018-07-26 18:33:22 -07:00
Michael Flaherty 11abe77eed Allow any returns in native callbacks (#857) 2018-07-25 21:46:44 -07:00
hydrogen-mvm 7d8dfb09d8 Fix English style in CONTRIBUTING.md (#855) 2018-07-21 23:17:35 -06:00
Kyle Sanderson 3baa703256 NPOTB: Fix clang-3.9 failures with Travis-CI (#851)
fix clang-3.9 failures, maybe; hopefully. I forget why this was broken.
2018-07-17 13:07:55 -07:00
Kyle Sanderson 2b3159f418 NPOTB: Add Discord and IRC Server to README.md 2018-07-17 10:43:23 -07:00
Kyle Sanderson 72acca4b9e Fix DataPack memory leak regression from #848. (#850)
* Fix memory leak regression from https://github.com/alliedmodders/sourcemod/pull/848.
2018-07-16 08:34:49 -07:00
Michael Flaherty 32d12ea4a6 Preserve old DataPack behavior when overwriting data (#848)
* Match old DataPack behavior when overwriting data

* Make RemoveItem more flexible

* Ditch implied RemoveItem behavior & asher fixes

* KyleS nits - but fixed before he says them

* Add back implicit behavior

* Update CDataPack.cpp

Committing to the spec.

* Update CDataPack.h

* fixup removing last item if explicitly requested

* Fix logic for accepting pack pos, rather than index

* Fixup IsReadable

* headache is over now
2018-07-14 17:00:17 -07:00
Ruben Gonzalez e4862dade8 Update CS:GO mac64/linux64 gamedata. 2018-07-13 19:44:40 -04:00
Ruben Gonzalez ed908b6545 Fix CS:GO weaponPrice offset. 2018-07-13 07:47:53 -04:00
KliPPy a81b52a787 Add move semantics for StringHashMap (#589)
* Move semantics for StringHashMap

* style

* Update sm_stringhashmap.h
2018-07-12 11:25:22 -07:00
komashchenko 52cdbb4fc1 Fix sm_help erroring out if invoked during the connection process (#723)
* Fix "Exception reported: Client 12 is not in game"

* Fix

* Update adminhelp.sp
2018-07-12 11:18:57 -07:00
Kyle Sanderson d0bf26135c Collection of plugin cleanups (#777)
* Fix rockthevote not clearing g_Voted on fakeclients.

* remove g_RTVAllowed in rockthevote as it's unused.

* Fix basebans using the wrong forward for configs.

* Prevent sm_addban from banning an immune steam adminid.

* Sprinkle in IsChatTrigger checks to plugins with plaintext chat hooks.

* fixup g_Voted[client] in OnClientDisconnect
2018-07-12 11:16:18 -07:00
Nick Hastings 4f2bb62ff9 Enable radio menus for IOSoccer. 2018-07-10 20:19:36 -04:00
Michael Flaherty 28f1ea82b6 Add base CommandIterator implementation (#819)
* Add base CommandIterator implementation

* Add check for invalid pos & finalize pr
2018-07-10 17:39:31 -04:00
Michael Flaherty aaac0b9eb2 Individualize NameHashSet Hashing & Revisit #709 (#740)
* Make mac/win lookups lowercase'd

* Revert #709 & 81042cc

* Adjust HashPolicy implementation across sourcemod

Basically, in order to implement our own (actual) hash policy in
`PluginSys.h`, we needed to remove the blanket implementation of `hash`
that was used before. Now, each policy must implement `hash` along with
`matches` in order to be used with `NameHashSet`. While this does force
us to change every implementation of policies across the entirety of
sourcemod, it allows core to use flexible implementations of `hash`.

* Remove logic duplication

* Improve lowercase checks
2018-07-10 17:38:40 -04:00
Alfred Llagas daee19d502 Changed "entiy" to "entity" in IsEntNetworkable (#847)
* Changed "entiy" to "entity" in IsEntNetworkable

* Update entity.inc
2018-07-08 11:11:35 -04:00
Mattia 70145dfd74 Make SQL_QuoteString deprecated (#792)
* Make SQL_QuoteString deprecated

* Fix message
2018-07-08 11:06:06 -04:00
Michael Flaherty c09b32010f Add Profiler methodmap (#814)
* Add profiler methodmap

* No tag mismatches here

thanks pm

* Lets fix some documentation while we're here.

* Use proper methodmap documentation style

Across sm we use `//` for methodmap docs, so lets just keep this consistent
2018-07-08 11:05:29 -04:00
Mattia 85a1d3e78a "sound" folder instead "sounds" (#790)
The folder is actually "sound" and not "sounds"
2018-07-08 11:04:20 -04:00
Nick Hastings a9c9da1066 Update SDKTools sound functionality for hl2sdk-csgo changes. 2018-07-07 08:57:00 -04:00
Nick Hastings 2860f31cb0 Trigger build for hl2sdk-csgo update. 2018-07-06 20:54:36 -04:00
Ruben Gonzalez 54cf1337c1 Merge branch 'master' of github.com:alliedmodders/sourcemod 2018-07-06 19:18:44 -04:00
Ruben Gonzalez dcde5927b1 Update SetClanTag signature 2018-07-06 19:18:14 -04:00
Nick Hastings c458d1e600 Trigger build for hl2sdk-csgo update. 2018-07-06 19:08:58 -04:00
Mr. Silence aae7161273 Updated some offsets and all signatures for ZPS. (#839)
Signed-off-by: Mr.Silence <[email protected]>
2018-06-27 17:15:27 -04:00
Nick Hastings 6287774d69 Update SDKTools gamedata for IOSoccer (fixes #833). 2018-06-22 16:30:22 -04:00
voed dc4d597fb9 Add link to SourceMod plugin API reference in project readme (#830) 2018-06-22 08:08:00 -06:00
Michael Flaherty 9ceb1af4fe Add LogStackTrace native (#685) 2018-06-19 23:18:37 -07:00
Michael Flaherty b9b6832a11 Streamline ConfDb loading proceedure (#791)
* Create DatabaseConfBuilder & remove locking
* Remove all refcounting

This is part 1/n in regards to this PR's rework
* Move db conf lookup out of RunThreadPart
* Return default configuration for failed lookups
* RefPtr members & stop leaks
* fix uint comparison warning
2018-06-19 09:35:37 -07:00
Nick Hastings 53c63def07 Use env vars to find MySQL src on Linux, macOS. 2018-06-07 20:40:14 -04:00
Asher Baker 4f70185aef Fix amtl submodule pin 2018-06-05 19:06:04 +01:00
peace-maker 6fcb411fe4 MySQL: Support multiple result sets in prepared queries (#825)
Prepared statements can return multiple result sets since MySQL 5.5. That can happen when calling stored procedures using `CALL x();`.

This change removes the previous caching of result bindings, since the number of fields in a result can differ from result set to result set. This could potentially have a negative impact on prepared statements always only returning one result set, since the result binding buffers are recreated everytime the statement is executed instead of once. That difference should be negligible.

Fixes #823.
2018-06-05 18:33:33 +01:00
peace-maker 9cc518e408 Fix MySQL extension build using vs2015 (#824)
MySQL defines its own timespec_t. Official mysql builds are built using VS2013, so you still need to compile the client library yourself to be able to link it, but this change makes the dbi extension compatible with future versions already.
2018-06-05 09:29:11 -04:00
Nick Hastings ddebc2a075 Fix oops with SetConVarBounds. 2018-06-03 12:36:58 -04:00
David Anderson fe6abb0c48 Merge pull request #822 from alliedmodders/update-sourcepawn
Update SourcePawn.
2018-05-31 23:32:51 -07:00
David Anderson 90e66cbeef Update SourcePawn. 2018-05-31 23:13:36 -07:00
David Anderson 487e01e4ed Merge pull request #821 from alliedmodders/update-amtl
Update AMTL.
2018-05-31 21:44:09 -07:00
David Anderson 25b3d8f354 Update AMTL. 2018-05-31 21:23:59 -07:00
Ruben Gonzalez d430bd2f5c Fix CompileRegex not actually setting a valid error code. (#775)
Fix CompileRegex not actually setting a valid error code.
2018-05-28 13:59:43 -04:00
Ruben Gonzalez e2767a3c80 Remove goto from ExtensionSys.cpp (#729) 2018-05-25 09:49:10 -04:00
RumbleFrog 7f552e4d75 Deprecate GetMaxClients (#818) 2018-05-23 12:58:36 +01:00
Nick Hastings cbcda61135 Swap _XKEYCHECK_H for _ALLOW_KEYWORD_MACROS, in case it moves to a
different header.
2018-05-21 19:54:12 -04:00
jason-e 2904c87b62 Send ShowActivity messages to TV clients (#813)
`ShowActivity` and related functions are used to inform all clients about the usage of admin commands (such as bans or mutes). These messages are not sent to any fake clients, however they _should_ be sent to SourceTV/GOTV fake clients because messages they receive are actually visible to broadcast viewers and in demos recorded with `tv_record`. 

Player chat messages as well as SM functions like `PrintToChat`/`PrintToChatAll` broadcast to TV clients, but `ShowActivity` does not.
2018-05-09 09:13:49 +01:00
Nick Hastings cfdedce362 Missed converting a GetFlags instance. 2018-05-06 13:18:04 -04:00
Nick Hastings 467d57059a Out with the old ConVar accessibility hacks, in with the new. 2018-05-06 12:59:33 -04:00
Nick Hastings c993712438 Update BM:S gamedata. 2018-05-06 12:17:47 -04:00
Nick Hastings a69d18ae1c Fix warning/error on newer Clang versions from abort not being defined. 2018-05-06 10:28:24 -04:00
Nick Hastings 95dae67d5b Fix wrong matchmaking_ds bin path being used in some instances. 2018-05-06 10:26:43 -04:00
Nick Hastings f6a64da672 Update Insurgency FireOutput gamedata. 2018-05-05 11:35:06 -04:00
Nick Hastings 11b23a6d07 Update ConVar hacks for BMS.
This is also to trigger an MM:S build for hl2sdk changes in multiple branches.
2018-05-05 11:01:55 -04:00
Spirrwell 82f1c5a20b Update Function Offsets For PVKII (#808)
* Update Function Offsets For PVKII

-Updates offsets
-Removes "Reload" as no corresponding function actually exists

* Offset Fixes

-Re-add "Reload" as it's defined for CBaseCombatWeapon
-Fix Linux\Mac offsets for GroundEntChanged
2018-05-01 20:28:54 -04:00
404 7e080d259e Update helpers.inc (#805)
Update GetClientAuthString to GetClientAuthId
2018-04-28 11:18:12 +01:00
Kyle Sanderson 1ab6ab1e5c NPOTB: Add basic issue template for new GH issues. 2018-04-05 22:20:49 -07:00
Nick Hastings b0e7bab980 Merge branch 'master' of https://github.com/alliedmodders/sourcemod 2018-03-28 21:11:14 -04:00
Nick Hastings 9b8d28de56 Allow -1/null for attacker and inflictor for OTD (bug 6494). 2018-03-28 21:10:32 -04:00
Michael Flaherty 5611ec54a2 Migrate extensions to common AMTL string funcs (#785) 2018-03-25 13:00:53 -07:00
Hexah 6c7e29cbdb Documentation fixes from methodmap implementation (#788)
* There is no "menu" param

* No "menu" param on GetTitle()

* Misspell "Replies"
2018-03-25 12:58:11 -07:00
Michael Flaherty 118d6dc9b1 Improve error message for EmitSound (#787)
Improve some error messages in sdktools.
2018-03-20 19:42:43 -06:00
Nicholas Hastings 08fe840bfd Probably fix Windows build. 2018-03-20 17:25:48 -04:00
Nicholas Hastings 4637cf9221 Use MySQL 5.5 for MySQL extension. (#786) 2018-03-20 17:12:30 -04:00
Joël Troch 4fd68939d3 [ZPS] Update gamedata and engine migration (#782)
* Migrate ZPS from EP2 to SDK 2013 engine

* Update ZPS gamedata (for version 3.0.7-Hotfix3)
2018-03-19 19:19:01 -04:00
peace-maker be6a930e82 Update SQLite to version 3.22.0 (#783)
This includes numerous bug fixes, optimizations and new features from releases over the past 5 years.

http://sqlite.org/chronology.html
2018-03-19 19:09:12 -04:00
Michael Flaherty 956f264b85 Replace ke::SafeSprintf with ke::SafeStrcpy (#784) 2018-03-19 20:12:51 +00:00
Nicholas Hastings 0a91b1f5b1 Increase MAX_NAME_LENGTH to match CS:GO MAX_PLAYER_NAME_LENGTH. 2018-02-25 10:51:18 -05:00
Nicholas Hastings a17b47a2dc Add Protobuf.HasField native. 2018-02-25 10:49:37 -05:00
Michael Flaherty 2a8c390498 Un-const string retrievals (#769) 2018-02-20 18:38:17 +00:00
Asher Baker 41ffe975fd Update blacklist.plugins.txt 2018-02-15 22:35:56 +00:00
Ruben Gonzalez 5ac3390656 Add new regex natives. (#767)
Add new regex natives to get multiple/all matches.
2018-02-15 15:31:24 -05:00
peace-maker f9faf9e48c Allow different values per platform in "Keys" gamedata section (#765)
This adds support for platform specific custom key values in the `Keys` section in game config files. Now you can have different values for the same key per platform.

Previously you could only add general key values in gamedata files that were the same on all platforms like
```
"Keys"
{
	"key"	"value"
}
```

This patch allows you to set the value per platform in a subsection like
```
"Keys"
{
	"key"
	{
		"windows"	"value1"
		"linux"	"value2"
		"mac"	"value3"
	}
}
```

Looking up the `key` will return the value matching the host platform.
2018-02-06 14:40:51 +00:00
Michael Flaherty e57dce76e1 Add support for msvc 1912 (Fixes #761) (#764)
* Add support for msvc 1912

* Default new msvc versions to VS 2017

* Use 2015 lib with 2017
2018-02-04 17:06:22 -05:00
Michael Flaherty 64a6972757 Fix description for CharToLower and CharToUpper v2 (#750) 2018-02-02 23:30:12 +00:00
StrangeThings 1293d15f72 Remove the @brief tag in sdktools_hooks.inc (#759) 2018-02-02 23:28:20 +00:00
Michael Flaherty 12fca79006 Prevent use of primitive float operation functions (#763)
Apparently people use the float natives for actual float arithmetic; they're not really intended to be used directly.

* Prevent use of primitive float operation functions
* Deprecate float operation functs
* Alias Float natives to internal names
* Add clarifying note
* Remove use of internal FloatDiv native
2018-02-02 23:27:38 +00:00
thewavelength 55b8371fa8 Add VS Preview 2017 as valid compiler (msvc-1911) (#678) 2018-01-28 11:39:23 +00:00
peace-maker a8080c0bbb Fix description of nominations convars (#758) 2018-01-27 12:00:04 +00:00
JRiipinen 8c61fdc99d Add more TF2 specific DMG_ definitions (#756)
* Add more TF2 DMG_ definitions

* Guard them all

* Inline doccomments
2018-01-26 14:06:54 +00:00
Nicholas Hastings 9374620b8b Add RemoveEntity native. (bug 5714) (#745) 2018-01-07 13:52:36 -05:00
Ruben Gonzalez 5a35760215 Merge pull request #744 from alliedmodders/show-required-exts
Address issue #737
2017-12-31 11:06:35 -05:00
Alex 6b257de75a Add internationalization to basechat and fix CS:GO colours (#674)
* Added translation more to basechat

Also fixed colors, because it didn't work without `\x01` at begin of string.

* Fixed cs:go colors, thanks Bara20

* Added 'space' at the start of string.

* Added check for Engine_CSGO, to remove offset on other games

* forgot one row
2017-12-30 12:25:32 -08:00
Ruben Gonzalez 40356fc9db Address issue #737 2017-12-30 14:08:56 -05:00
Kyle Sanderson de108cca44 Simplify Logger internals (Fixes Issue #663). (#691)
* Simplify Logger internals (Issue #663).

* Avoid reformatting strings on every call.
2017-12-29 19:56:25 -08:00
Kyle Sanderson 5f5a6b3a16 Migrate CDataPack from a Compact Cassette tape. (#688)
* Migrate CDataPack from a Cassette Tape.

Tested-By: Headline22.

* Remove last IsReadable param pass.

* populate len still if CDataPack::ReadString is unreadable or the wrong type.

* Fyren Fixes(TM)(R)(C).

* Deprecate IDataPack.
2017-12-29 19:56:02 -08:00
Nicholas Hastings 29b1926432 A couple of fixups to last commit (NPOTB). 2017-12-29 04:55:56 -08:00
Nicholas Hastings e0a154faa0 Add TF2 NextBot symbol data from Pelipoka to GDC (NPOTB). 2017-12-29 04:38:47 -08:00
Byte 1706fdfed9 Fix typo and update CSGOBadList (#738)
* Fix typo

* Update CSGOBadList
2017-12-28 18:43:49 -05:00
Byte 945df342d8 Fix typo in SwitchTeam Linux Signature (#743) 2017-12-28 17:39:30 -05:00
Scott Ehlert bfb11c7712 Remove extra CS:GO Mac offset for GetItemInLoadout 2017-12-20 04:17:29 -06:00
Scott Ehlert fa7d8757dc Rebuild 64-bit Linux libpcre with -Wa,-mrelax-relocations=no 2017-12-20 03:50:12 -06:00
Scott Ehlert 3c7978153b Rebuild 64-bit Linux libpcre with -fPIC. 2017-12-20 03:15:57 -06:00
Scott Ehlert 7cb3d4d8ff Fix Linux and macOS builds. 2017-12-20 02:13:56 -06:00
Scott Ehlert 870e118150 Fix BreakpadSymbols script for 64-bit builds. 2017-12-20 02:07:30 -06:00
Scott Ehlert e27f8fba1a Hopefully fix Windows build. 2017-12-20 02:04:05 -06:00
Scott Ehlert ce1a4dcac0 64-bit support for CSGO on Linux and macOS (#705) 2017-12-20 01:56:23 -06:00
Scott Ehlert 057a5790e2 Update CS:GO TerminateRound signature for Linux. 2017-12-19 23:23:29 -06:00
Nicholas Hastings 87858ef264 Test build. 2017-12-18 21:00:04 -05:00
Nicholas Hastings 98fea02eaa Roll version. >_> 2017-12-18 20:39:57 -05:00
585 changed files with 213978 additions and 68473 deletions
+12
View File
@@ -0,0 +1,12 @@
# GitHub views all .h files as C, let's assume it's C++
*.h linguist-language=c++
# Internal tools overriding
tools/* linguist-vendored
editor/* linguist-vendored
# Third-party overriding
extensions/curl/curl-src/* linguist-vendored
extensions/geoip/GeoIP.c linguist-vendored
extensions/geoip/GeoIP.h linguist-vendored
extensions/sqlite/sqlite-source/* linguist-vendored
+7 -7
View File
@@ -7,17 +7,17 @@ Please consider the following guidelines when reporting an issue.
#### Not for general support
This is not the right place to get help with using or installing SourceMod, or for issues with specific, third-party SourceMod plugins or extensions.
For help with SourceMod, please consult the [AlliedModders forums](https://forums.alliedmods.net/forumdisplay.php?f=52). Similary, for assistance with, or to report issues with third-party SourceMod plugins or extension, you should post in the existing thread for the plugin or extension at the [AlliedModders forums](https://forums.alliedmods.net/forumdisplay.php?f=52).
For help with SourceMod, please consult the [AlliedModders forums](https://forums.alliedmods.net/forumdisplay.php?f=52). Similarly, for assistance with, or to report issues with, third-party SourceMod plugins or extensions, you should post in the existing thread for that plugin or extension on the [AlliedModders forums](https://forums.alliedmods.net/forumdisplay.php?f=52).
#### Details, details, details
Provide as much detail as possible when reporting an issue.
For bugs or other undesired behavior, answers to the follow questions are a great start.
For bugs or other undesired behavior, answers to the following questions are a great start:
* What is the issue?
* What behavior are you expecting instead?
* On what operating system is the game server running?
* What game is the game server running?
* What exact (full x.y.z.a version number) version of Metamod:Source and SourceMod are installed on the game server?
* What exact version (full x.y.z.a version number) of Metamod:Source and SourceMod are installed on the game server?
* What is the specific, shortest path to reproducing this issue? If this issue can be reproduced with plugin code, please try to shorten it to the minimum required to trigger the problem.
If this is a feature request, the following are helpful. Generally, not all will apply, but whatever you can answer ahead of time will shorten back and forth conversation.
@@ -29,18 +29,18 @@ If this is a feature request, the following are helpful. Generally, not all will
Please report any security bugs to [[email protected]](mailto:[email protected]) rather than to this public issue tracker.
#### We're only human
Please keep in mind that we maintain the project in our spare time, at no cost. There is no SLA, and you are not owed a response or fix.
Please keep in mind that we maintain this project in our spare time, at no cost. There is no SLA, and you are not owed a response or a fix.
#### Conduct
Please refer to the [AlliedModders forum rules.](https://forums.alliedmods.net/misc.php?do=showrules)
## Pull Requests
Firstly, thank you for considering to contribute changes to the project!
Firstly, thank you for considering contributing changes to the project!
However, if this is anything more than a small fix such an a gamedata update, a glaring code flaw, or a simple typo in a file like this one, please file an issue first so that it can be discussed, unless you have already spoken to multiple members of the development team about it on IRC or the AlliedModders forums.
However, if this is anything more than a small fix such as a gamedata update, a glaring code flaw, or a simple typo in a file like this one, please file an issue first so that it can be discussed, unless you have already spoken to multiple members of the development team about it on IRC or the AlliedModders forums.
We don't like to have to reject pull requests, so we want to avoid those scenarios. We wouldn't want you to feel that you wasted your time writing something only for us to have it then be shot down.
We don't like to have to reject pull requests, so we want to avoid those scenarios. We wouldn't want you to feel like you wasted your time writing something only for us to shoot it down.
#### Rejection
*Copied from Phabricator's [Contributing Code guidelines](https://secure.phabricator.com/book/phabcontrib/article/contributing_code/#rejecting-patches), as we largely feel the same way about this.*
+8
View File
@@ -0,0 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://www.sourcemod.net/donate.php
+26
View File
@@ -0,0 +1,26 @@
# Help us help you
- [ ] I have checked that my issue [doesn't exist yet](https://github.com/alliedmodders/sourcemod/issues).
- [ ] I have tried my absolute best to reduce the problem-space and have provided the absolute smallest test-case possible.
- [ ] I can always reproduce the issue with the provided description below.
# Environment
* Operating System version:
* Game/AppID (with version if applicable):
* Current SourceMod version:
* Current SourceMod snapshot:
* Current Metamod: Source snapshot:
- [ ] I have updated SourceMod to the [latest version](https://www.sourcemod.net/downloads.php) and it still happens.
- [ ] I have updated SourceMod to the [latest snapshot](https://www.sourcemod.net/downloads.php?branch=dev) and it still happens.
- [ ] I have updated SourceMM to the [latest snapshot](https://sourcemm.net/downloads.php?branch=dev) and it still happens.
# Description
# Problematic Code (or Steps to Reproduce)
```
// TODO(you): code here to reproduce the problem
```
# Logs
* Please attach in separate files: game output, library logs, kernel logs, and any other supporting information.
* In case of a crash, please attach minidump or dump analyze output.
+98
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-18.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-18.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.10'
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
path: sourcemod
- name: Cache dependencies
uses: actions/cache@v2
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@v2
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
+79
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@v2
with:
submodules: recursive
# Setup Python for AMBuild
- uses: actions/setup-python@v2
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 only for x64 on macOS
if: startsWith(runner.os, 'macOS')
run: echo "ARCH=x86_64" >> $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@v2
with:
name: sourcemod-tooling-${{ env.SM_VERSION }}-${{ matrix.os_short }}
path: build/package
+5 -1
View File
@@ -28,7 +28,7 @@ LIB-Debug/
[Tt]humbs.db
# AMBuild build directories
build/
build*/
obj-*/
*~
*.rej
@@ -36,3 +36,7 @@ obj-*/
*.smx
*.swp
*.gdb_history
objdir
.vs/*
.vscode/*
+2
View File
@@ -1,6 +1,8 @@
[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
+57 -87
View File
@@ -1,116 +1,86 @@
git:
depth: 3
sudo: false
language: cpp
os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- 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
- 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,tf2,l4d2,csgo,dota
- eval "${CC} --version"
- eval "${CXX} --version"
- python3 ../configure.py --enable-${MODE} --sdks=${SDKS} --targets=${ARCH}
- ambuild
+384 -120
View File
@@ -1,5 +1,8 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import collections
import os, sys
import subprocess
import traceback
class SDK(object):
def __init__(self, sdk, ext, aDef, name, platform, dir):
@@ -11,12 +14,41 @@ class SDK(object):
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, targets):
for cxx in targets:
if cxx.target.platform in self.platformSpec:
if cxx.target.arch in self.platformSpec[cxx.target.platform]:
return True
return False
WinOnly = ['windows']
WinLinux = ['windows', 'linux']
WinLinuxMac = ['windows', 'linux', 'mac']
Blade = {
'windows': ['x86', 'x86_64'],
'linux': ['x86_64'],
'mac': ['x86_64']
}
CSGO = {
'windows': ['x86'],
'linux': ['x86', 'x86_64'],
'mac': ['x86_64']
}
Mock = {
'windows': ['x86', 'x86_64'],
'linux': ['x86', 'x86_64'],
'mac': ['x86_64']
}
PossibleSDKs = {
SDKMap = {
'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'),
@@ -31,15 +63,21 @@ PossibleSDKs = {
'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', WinLinux, 'csgo'),
'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'),
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', Blade, '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'),
'mock': SDK('HL2SDK-MOCK', '2.mock', '999', 'MOCK', Mock, 'mock'),
}
# Stable sorting for command equivalence in AMBuild.
PossibleSDKs = collections.OrderedDict()
for key in sorted(SDKMap.keys()):
PossibleSDKs[key] = SDKMap[key]
def ResolveEnvPath(env, folder):
if env in os.environ:
path = os.environ[env]
@@ -60,18 +98,54 @@ def ResolveEnvPath(env, folder):
def Normalize(path):
return os.path.abspath(os.path.normpath(path))
def SetArchFlags(compiler):
if compiler.behavior == 'gcc':
if compiler.target.arch == 'x86_64':
compiler.cflags += ['-fPIC']
elif compiler.like('msvc'):
if compiler.target.arch == 'x86_64':
compiler.defines += ['WIN64']
class SMConfig(object):
def __init__(self):
self.sdks = {}
self.binaries = []
self.spvm = []
self.extensions = []
self.generated_headers = None
self.mms_root = None
self.mysql_root = None
self.mysql_root = {}
self.spcomp = None
self.spcomp_bins = None
self.smx_files = {}
self.versionlib = None
self.all_targets = []
self.target_archs = set()
self.enable_asan = getattr(builder.options, 'enable_asan', False)
self.asan_libs = {}
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':
@@ -100,31 +174,34 @@ class SMConfig(object):
def detectSDKs(self):
sdk_list = builder.options.sdks.split(',')
use_none = sdk_list[0] == 'none'
use_all = sdk_list[0] == 'all'
use_present = sdk_list[0] == 'present'
for sdk_name in PossibleSDKs:
sdk = PossibleSDKs[sdk_name]
if builder.target.platform in sdk.platform:
if sdk.shouldBuild(self.all_targets):
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:
if (use_all and sdk_name != 'mock') 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:
raise Exception('At least one SDK must be available.')
if len(self.sdks) < 1 and len(sdk_list) and not use_none:
raise Exception('No applicable SDKs were found, nothing to do')
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('MMSOURCE111', 'mmsource-1.11')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
if not self.mms_root:
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source')
if not self.mms_root:
@@ -135,21 +212,49 @@ class SMConfig(object):
self.mms_root = Normalize(self.mms_root)
if builder.options.hasMySql:
if builder.options.mysql_path:
self.mysql_root = builder.options.mysql_path
else:
for i in range(7):
self.mysql_root = ResolveEnvPath('MYSQL5', 'mysql-5.' + str(i))
if self.mysql_root:
break
if not self.mysql_root or not os.path.isdir(self.mysql_root):
raise Exception('Could not find a path to MySQL!')
self.mysql_root = Normalize(self.mysql_root)
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['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. 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['x86_64'] = Normalize(self.mysql_root['x86_64'])
def configure(self):
builder.AddConfigureFile('pushbuild.txt')
if not set(self.target_archs).issubset(['x86', 'x86_64']):
raise Exception('Unknown target architecture: {0}'.format(self.target_archs))
cxx = builder.DetectCxx()
for cxx in self.all_targets:
self.configure_cxx(cxx)
def configure_cxx(self, cxx):
if cxx.family == 'msvc':
if cxx.version < 1900:
raise Exception('Only MSVC 2015 and later are supported, c++14 support is required.')
if cxx.family == 'gcc':
if cxx.version < 'gcc-4.9':
raise Exception('Only GCC versions 4.9 or greater are supported, c++14 support is required.')
if cxx.family == 'clang':
if cxx.version < 'clang-3.4':
raise Exception('Only clang versions 3.4 or greater are supported, c++14 support is required.')
if cxx.like('gcc'):
self.configure_gcc(cxx)
@@ -165,11 +270,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.
@@ -205,26 +310,28 @@ class SMConfig(object):
'-Wno-switch',
'-Wno-array-bounds',
'-msse',
'-m32',
'-fvisibility=hidden',
]
if cxx.version == 'apple-clang-6.0' or cxx.version == 'clang-3.4':
cxx.cxxflags += ['-std=c++1y']
else:
cxx.cxxflags += ['-std=c++14']
cxx.cxxflags += [
'-std=c++11',
'-fno-exceptions',
'-fno-threadsafe-statics',
'-Wno-non-virtual-dtor',
'-Wno-overloaded-virtual',
'-fvisibility-inlines-hidden',
]
cxx.linkflags += ['-m32']
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']
@@ -234,7 +341,8 @@ class SMConfig(object):
cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
if cxx.version >= 'gcc-4.8':
cxx.cflags += ['-Wno-unused-result']
if cxx.version >= 'gcc-9.0':
cxx.cxxflags += ['-Wno-class-memaccess', '-Wno-packed-not-aligned']
if have_clang:
cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch']
if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4':
@@ -243,14 +351,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']
@@ -271,7 +400,6 @@ class SMConfig(object):
'/TP',
]
cxx.linkflags += [
'/MACHINE:X86',
'kernel32.lib',
'user32.lib',
'gdi32.lib',
@@ -297,6 +425,29 @@ 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.linkflags += ['-lm']
@@ -304,23 +455,33 @@ class SMConfig(object):
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.7']
cxx.linkflags += [
'-mmacosx-version-min=10.5',
'-arch', 'i386',
'-lstdc++',
'-stdlib=libstdc++',
'-mmacosx-version-min=10.7',
'-stdlib=libc++',
'-lc++',
]
cxx.cxxflags += ['-stdlib=libstdc++']
cxx.cxxflags += ['-stdlib=libc++']
def configure_windows(self, cxx):
cxx.defines += ['WIN32', '_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 builder.target.platform == 'windows':
if binary.compiler.target.platform == 'windows':
binary.sources += ['version.rc']
binary.compiler.rcdefines += [
'BINARY_NAME="{0}"'.format(binary.outputFile),
@@ -328,14 +489,14 @@ 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]
binary.compiler.postlink += [self.versionlib[binary.compiler.target.arch]]
binary.compiler.sourcedeps += SM.generated_headers
return binary
@@ -344,6 +505,13 @@ class SMConfig(object):
self.AddVersioning(binary)
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
# 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):
@@ -351,28 +519,32 @@ class SMConfig(object):
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'):
binary.compiler.linkflags += ['-lstdc++']
binary.compiler.linkflags += ['-lstdc++', '-lpthread']
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:CONSOLE']
return binary
def StaticLibraryBuilder(self, compiler, name):
binary = compiler.StaticLibrary(name)
return binary;
return compiler.StaticLibrary(name)
def Library(self, context, name):
compiler = context.cxx.clone()
def Library(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.LibraryBuilder(compiler, name)
def Program(self, context, name):
compiler = context.cxx.clone()
def Program(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.ProgramBuilder(compiler, name)
def StaticLibrary(self, context, name):
compiler = context.cxx.clone()
def StaticLibrary(self, context, compiler, name):
compiler = compiler.clone()
SetArchFlags(compiler)
return self.StaticLibraryBuilder(compiler, name)
def ConfigureForExtension(self, context, compiler):
@@ -386,13 +558,15 @@ class SMConfig(object):
]
return compiler
def ExtLibrary(self, context, name):
binary = self.Library(context, name)
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):
def ConfigureForHL2(self, context, binary, sdk):
compiler = binary.compiler
SetArchFlags(compiler)
compiler.cxxincludes += [
os.path.join(self.mms_root, 'core'),
@@ -429,108 +603,160 @@ class SMConfig(object):
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']
compiler.defines += ['COMPILER_MSVC']
if compiler.target.arch == 'x86':
compiler.defines += ['COMPILER_MSVC32']
elif compiler.target.arch == 'x86_64':
compiler.defines += ['COMPILER_MSVC64']
compiler.linkflags += ['legacy_stdio_definitions.lib']
else:
compiler.defines += ['COMPILER_GCC']
if compiler.target.arch == 'x86_64':
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']:
if compiler.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++']
if compiler.target.platform == 'linux':
if sdk.name in ['csgo', 'blade']:
compiler.linkflags.remove('-static-libstdc++')
compiler.linkflags += ['-lstdc++']
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
elif compiler.target.platform == 'mac':
if sdk.name in ['csgo']:
# Switch libc++ to libstdc++ for protobuf linkage.
compiler.cxxflags.remove('-stdlib=libc++')
compiler.linkflags.remove('-stdlib=libc++')
compiler.linkflags.remove('-lc++')
compiler.cxxflags += ['-stdlib=libstdc++']
compiler.linkflags += ['-stdlib=libstdc++']
compiler.linkflags += ['-lstdc++']
if 'c++1y' in compiler.cxxflags:
compiler.cxxflags.remove('-std=c++1y')
compiler.cxxflags += ['-std=c++11']
elif 'c++14' in compiler.cxxflags:
compiler.cxxflags.remove('-std=c++14')
compiler.cxxflags += ['-std=c++11']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
if builder.target.platform == 'linux':
if compiler.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 compiler.target.arch == 'x86_64':
lib_folder = os.path.join(sdk.path, 'lib', 'linux64')
else:
lib_folder = os.path.join(sdk.path, 'lib', 'linux')
elif builder.target.platform == 'mac':
elif compiler.target.platform == 'mac':
if sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32')
elif compiler.target.arch == 'x86_64':
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']:
if compiler.target.platform in ['linux', 'mac']:
if sdk.name in ['sdk2013', 'bms'] or compiler.target.arch == 'x86_64':
compiler.postlink += [
compiler.Dep(os.path.join(lib_folder, 'tier1.a')),
compiler.Dep(os.path.join(lib_folder, 'mathlib.a'))
os.path.join(lib_folder, 'tier1.a'),
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'))
os.path.join(lib_folder, 'tier1_i486.a'),
os.path.join(lib_folder, 'mathlib_i486.a')
]
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))]
if compiler.target.arch == 'x86_64':
compiler.postlink += [os.path.join(lib_folder, 'interfaces.a')]
else:
compiler.postlink += [os.path.join(lib_folder, 'interfaces_i486.a')]
dynamic_libs = []
if builder.target.platform == 'linux':
if compiler.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 compiler.target.arch == 'x86_64' and sdk.name in ['csgo', 'mock']:
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':
elif compiler.target.platform == 'mac':
compiler.linkflags.append('-liconv')
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
elif builder.target.platform == 'windows':
elif compiler.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))
if compiler.target.arch == 'x86':
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
elif compiler.target.arch == 'x86_64':
lib_path = os.path.join(sdk.path, 'lib', 'public', 'win64', lib) + '.lib'
compiler.linkflags.append(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
# Ensure the output path exists.
context.AddFolder(binary.localFolder)
output = context.AddSymlink(source_path, output_path)
linker = make_linker(source_path, output_path)
compiler.linkflags[0:0] = [compiler.Dep(library, linker)]
compiler.weaklinkdeps += [output]
compiler.linkflags[0:0] = [library]
return binary
def HL2Library(self, context, name, sdk):
binary = self.Library(context, name)
def HL2Library(self, context, compiler, name, sdk):
binary = self.Library(context, compiler, name)
self.ConfigureForExtension(context, binary.compiler)
return self.ConfigureForHL2(binary, sdk)
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, name, sdk):
binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name))
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.AddVersioning(binary)
return self.ConfigureForHL2(binary, sdk)
return self.ConfigureForHL2(context, binary, sdk)
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.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(
@@ -542,41 +768,79 @@ if SM.use_auto_versioning():
{ 'SM': SM }
)
class SPRoot(object):
# 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,
})
SM.spcomp = SP.spcomp
SM.binaries += [
SP.libsourcepawn
]
if len(SP.spcomp) > 1:
SM.spcomp = SP.spcomp['x86']
else:
SM.spcomp = SP.spcomp[list(SP.spcomp.keys())[0]]
SM.spcomp_bins = list(SP.spcomp.values())
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 not getattr(builder.options, 'scripting_only', False):
for cxx in SM.all_targets:
SM.spvm += [
SP.libsourcepawn[cxx.target.arch]
]
if builder.backend == 'amb2':
BuildScripts += [
'plugins/AMBuilder',
'tools/buildbot/PackageScript',
if getattr(builder.options, 'scripting_only', False):
BuildScripts = [
'tools/buildbot/PackageHelpers',
'tools/buildbot/ToolsPackageScript',
]
else:
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',
]
if builder.backend == 'amb2':
BuildScripts += [
'plugins/AMBuilder',
'tools/buildbot/PackageHelpers',
'tools/buildbot/PackageScript',
]
builder.Build(BuildScripts, { 'SM': SM })
+10
View File
@@ -14,4 +14,14 @@ Development
- [Issue tracker](https://github.com/alliedmodders/sourcemod/issues): Issues that require back and forth communication
- [Building SourceMod](https://wiki.alliedmods.net/Building_SourceMod): Instructions on how to build SourceMod itself using [AMBuild](https://github.com/alliedmodders/ambuild)
- [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
Contact
-------
- Connect with us on [GameSurge](https://gamesurge.net) IRC in #sourcemod
- Alternatively feel free to join our [Discord](https://discord.gg/HgZctSS) server
License
-------
SourceMod is licensed under the GNU General Public License version 3. Special exceptions are outlined in the LICENSE.txt file inside of the licenses folder.
+17
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
+1 -1
View File
@@ -35,7 +35,7 @@ namespace SourceMod {
// Add 1 to the RHS of this expression to bump the intercom file
// This is to prevent mismatching core/logic binaries
static const uint32_t SM_LOGIC_MAGIC = 0x0F47C0DE - 55;
static const uint32_t SM_LOGIC_MAGIC = 0x0F47C0DE - 57;
} // namespace SourceMod
+3 -1
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
{
@@ -115,6 +115,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;
+5 -3
View File
@@ -50,7 +50,6 @@ class IProviderCallbacks;
class IExtensionSys;
class ITextParsers;
class ILogger;
class IDataPack;
class ICellArray;
struct sm_logic_t
@@ -70,10 +69,13 @@ struct sm_logic_t
void (*GenerateError)(IPluginContext *, cell_t, int, const char *, ...);
void (*AddNatives)(sp_nativeinfo_t *natives);
void (*RegisterProfiler)(IProfilingTool *tool);
IDataPack * (*CreateDataPack)();
void (*FreeDataPack)(IDataPack *pack);
ICellArray * (*CreateCellArray)(size_t blocksize);
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;
+11 -1
View File
@@ -145,5 +145,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.
@@ -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;
@@ -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');
+37 -28
View File
@@ -1,40 +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('--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()
+79 -47
View File
@@ -1,7 +1,8 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
# 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,67 +38,98 @@ 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]
binary_name = 'sourcemod.' + sdk.ext
for cxx in builder.targets:
if not cxx.target.arch in sdk.platformSpec[cxx.target.platform]:
continue
binary = SM.HL2Config(project, binary_name, sdk)
compiler = binary.compiler
binary_name = 'sourcemod.' + sdk.ext
compiler.cxxincludes += [
builder.sourcePath
]
binary = SM.HL2Config(project, builder, cxx, binary_name, sdk)
SM.ConfigureForExtension(builder, binary.compiler)
if sdk.name == 'csgo':
compiler = binary.compiler
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')
builder.sourcePath
]
if builder.target.platform == 'linux':
compiler.postlink += ['-lpthread', '-lrt']
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')
]
elif sdk.name == 'blade':
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', 'berimbau', 'protobuf')
]
if sdk.name == 'csgo':
if builder.target.platform == 'linux':
lib_path = os.path.join(sdk.path, 'lib', 'linux32', 'release', 'libprotobuf.a')
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a']
elif builder.target.platform == 'mac':
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a')
elif builder.target.platform == 'windows':
msvc_ver = compiler.version
vs_year = ''
if msvc_ver == 1800:
vs_year = '2013'
elif msvc_ver == 1900:
vs_year = '2015'
elif msvc_ver == 1910:
vs_year = '2017'
else:
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"')
if compiler.like('msvc'):
compiler.defines += ['_ALLOW_KEYWORD_MACROS']
if cxx.target.platform == 'linux':
compiler.postlink += ['-lpthread', '-lrt']
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 in ['csgo', 'blade']:
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 sdk.name == 'csgo':
binary.sources += ['smn_protobuf.cpp']
else:
binary.sources += ['smn_bitbuffer.cpp']
if sdk.name in ['csgo', 'blade']:
binary.sources += ['smn_protobuf.cpp']
else:
binary.sources += ['smn_bitbuffer.cpp']
if sdk.name == 'csgo':
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'),
]
if sdk.name != 'blade':
binary.sources += [
'vprof_tool.cpp',
]
if sdk.name == 'csgo':
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'),
]
elif sdk.name == 'blade':
binary.sources += [
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessages.pb.cc'),
os.path.join(sdk.path, 'public', 'game', 'shared', 'berimbau', 'protobuf', 'berimbau_usermessage_helpers.cpp'),
]
SM.binaries += builder.Add(project)
+15 -21
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);
}
+3 -3
View File
@@ -73,9 +73,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;
+39 -61
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;
@@ -317,7 +296,7 @@ bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmi
char buffer[128];
if (!logicore.CoreTranslate(buffer, sizeof(buffer), "%T", 2, NULL, "No Access", &client))
{
ke::SafeSprintf(buffer, sizeof(buffer), "You do not have access to this command");
ke::SafeStrcpy(buffer, sizeof(buffer), "You do not have access to this command");
}
unsigned int replyto = g_ChatTriggers.GetReplyTo();
@@ -342,9 +321,10 @@ bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
const char *group,
int adminflags,
const char *description,
int flags)
int flags,
IPlugin *pPlugin)
{
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags, pPlugin);
if (!pInfo)
return false;
@@ -358,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,
@@ -379,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;
@@ -388,15 +368,16 @@ bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
const char *name,
const char *description,
int flags)
int flags,
IPlugin *pPlugin)
{
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags, pPlugin);
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);
@@ -423,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)
@@ -493,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;
@@ -555,15 +536,11 @@ bool ConCmdManager::LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags)
return true;
}
ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags, IPlugin *pPlugin)
{
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);
@@ -580,6 +557,7 @@ ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *descri
char *new_name = sm_strdup(name);
char *new_help = sm_strdup(description);
pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
pInfo->pPlugin = pPlugin;
pInfo->sourceMod = true;
}
else
@@ -645,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);
+43 -19
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,11 +49,7 @@
#include <IAdminSystem.h>
#include "concmd_cleaner.h"
#include "GameHooks.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;
@@ -54,7 +58,7 @@ struct ConCmdInfo;
struct CommandGroup : public ke::Refcounted<CommandGroup>
{
ke::LinkedList<CmdHook *> hooks;
std::list<CmdHook *> hooks;
};
struct AdminCmdInfo
@@ -77,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)
{
}
@@ -88,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;
@@ -98,15 +104,37 @@ struct ConCmdInfo
{
ConCmdInfo()
{
pPlugin = nullptr;
sourceMod = false;
pCmd = NULL;
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;
@@ -131,13 +159,14 @@ public: //IRootConsoleCommand
public: //IConCommandTracker
void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name) override;
public:
bool AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
bool AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags, IPlugin *pPlugin);
bool AddAdminCommand(IPluginFunction *pFunction,
const char *name,
const char *group,
int adminflags,
const char *description,
int flags);
int flags,
IPlugin *pPlugin);
ResultType DispatchClientCommand(int client, const char *cmd, int args, ResultType type);
void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove);
bool LookForSourceModCommand(const char *cmd);
@@ -145,15 +174,10 @@ public:
private:
bool InternalDispatch(int client, const ICommandArgs *args);
ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags);
ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags, IPlugin *pPlugin);
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()
@@ -163,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 */
};
+47 -10
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;
}
+21 -5
View File
@@ -43,6 +43,7 @@
#include <compat_wrappers.h>
#include "concmd_cleaner.h"
#include "PlayerManager.h"
#include <sm_stringhashmap.h>
using namespace SourceHook;
@@ -61,12 +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 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();
}
};
};
/**
@@ -139,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);
@@ -156,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;
+2 -2
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;
+9 -6
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()
@@ -412,8 +412,11 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
for (iter = convars->begin(); iter != convars->end(); iter++)
{
const ConVar *cvar = (*iter);
if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
#if SOURCE_ENGINE >= SE_ORANGEBOX
if (cvar->IsFlagSet(FCVAR_DONTRECORD))
#else
if (cvar->IsBitSet(FCVAR_DONTRECORD))
#endif
{
continue;
}
+1 -1
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();
+4 -4
View File
@@ -364,8 +364,8 @@ void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
void EventManager::FireEventToClient(EventInfo *pInfo, IClient *pClient)
{
// The IClient vtable is +4 from the IGameEventListener2 (CBaseClient) vtable due to multiple inheritance.
IGameEventListener2 *pGameClient = (IGameEventListener2 *)((intptr_t)pClient - 4);
// The IClient vtable is +sizeof(void *) from the IGameEventListener2 (CBaseClient) vtable due to multiple inheritance.
IGameEventListener2 *pGameClient = (IGameEventListener2 *)((intptr_t)pClient - sizeof(void *));
pGameClient->FireGameEvent(pInfo->pEvent);
}
@@ -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;
}
}
+6 -2
View File
@@ -71,11 +71,15 @@ 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)
{
return key.hash();
}
};
+2 -2
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
if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue))
return;
# endif
+3 -3
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;
}
};
+183 -58
View File
@@ -45,31 +45,14 @@
#if SOURCE_ENGINE == SE_CSGO
#include <cstrike15_usermessages.pb.h>
#elif SOURCE_ENGINE == SE_BLADE
#include <berimbau_usermessages.pb.h>
#endif
typedef ICommandLine *(*FakeGetCommandLine)();
#if defined _WIN32
#define TIER0_NAME "tier0.dll"
#define VSTDLIB_NAME "vstdlib.dll"
#elif defined __APPLE__
#define TIER0_NAME "libtier0.dylib"
#define VSTDLIB_NAME "libvstdlib.dylib"
#elif defined __linux__
#if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 \
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_LEFT4DEAD2 || SOURCE_ENGINE == SE_NUCLEARDAWN \
|| SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_INSURGENCY || SOURCE_ENGINE == SE_DOI
#define TIER0_NAME "libtier0_srv.so"
#define VSTDLIB_NAME "libvstdlib_srv.so"
#elif SOURCE_ENGINE >= SE_LEFT4DEAD
#define TIER0_NAME "libtier0.so"
#define VSTDLIB_NAME "libvstdlib.so"
#else
#define TIER0_NAME "tier0_i486.so"
#define VSTDLIB_NAME "vstdlib_i486.so"
#endif
#endif
#define TIER0_NAME FORMAT_SOURCE_BIN_NAME("tier0")
#define VSTDLIB_NAME FORMAT_SOURCE_BIN_NAME("vstdlib")
CHalfLife2 g_HL2;
ConVar *sv_lan = NULL;
@@ -77,6 +60,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()
{
@@ -157,9 +141,11 @@ void CHalfLife2::OnSourceModAllInitialized_Post()
m_CSGOBadList.add("m_flFallbackWear");
m_CSGOBadList.add("m_nFallbackStatTrak");
m_CSGOBadList.add("m_iCompetitiveRanking");
m_CSGOBadList.add("m_iCompetitiveRankType");
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 +166,7 @@ ConfigResult CHalfLife2::OnSourceModConfigChanged(const char *key, const char *v
}
else
{
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
return ConfigResult_Reject;
}
#endif
@@ -197,6 +183,7 @@ void CHalfLife2::InitLogicalEntData()
|| SOURCE_ENGINE == SE_CSS \
|| SOURCE_ENGINE == SE_SDK2013 \
|| SOURCE_ENGINE == SE_BMS \
|| SOURCE_ENGINE == SE_BLADE \
|| SOURCE_ENGINE == SE_NUCLEARDAWN
if (g_SMAPI->GetServerFactory(false)("VSERVERTOOLS003", nullptr))
@@ -245,7 +232,12 @@ void CHalfLife2::InitLogicalEntData()
return;
}
#ifdef PLATFORM_X86
g_EntList = *reinterpret_cast<void **>(addr + offset);
#elif defined PLATFORM_X64
int32_t varOffset = *reinterpret_cast<int32_t *>(addr + offset);
g_EntList = reinterpret_cast<void *>(addr + offset + sizeof(int32_t) + varOffset);
#endif
}
}
@@ -329,23 +321,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())
@@ -528,7 +538,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
CCSUsrMsg_SayText *pMsg;
if ((pMsg = (CCSUsrMsg_SayText *)g_UserMsgs.StartProtobufMessage(m_SayTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@@ -555,7 +565,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
CCSUsrMsg_TextMsg *pMsg;
if ((pMsg = (CCSUsrMsg_TextMsg *)g_UserMsgs.StartProtobufMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@@ -592,7 +602,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
CCSUsrMsg_HintText *pMsg;
if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
{
@@ -622,7 +632,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
CCSUsrMsg_HintText *pMsg;
if ((pMsg = (CCSUsrMsg_HintText *)g_UserMsgs.StartProtobufMessage(m_HinTextMsg, players, count, USERMSG_RELIABLE)) == NULL)
{
@@ -657,7 +667,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
CCSUsrMsg_VGUIMenu *pMsg;
if ((pMsg = (CCSUsrMsg_VGUIMenu *)g_UserMsgs.StartProtobufMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
{
@@ -682,7 +692,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
pMsg->set_name(name);
pMsg->set_show(show);
@@ -845,7 +855,7 @@ void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
kick.client = client;
kick.userid = userid;
ke::SafeSprintf(kick.buffer, sizeof(kick.buffer), "%s", msg);
ke::SafeStrcpy(kick.buffer, sizeof(kick.buffer), msg);
m_DelayedKicks.push(kick);
}
@@ -1212,7 +1222,7 @@ bool IsWindowsReservedDeviceName(const char *pMapname)
};
size_t reservedCount = sizeof(reservedDeviceNames) / sizeof(reservedDeviceNames[0]);
for (int i = 0; i < reservedCount; ++i)
for (size_t i = 0; i < reservedCount; ++i)
{
if (CheckReservedFilename(pMapname, reservedDeviceNames[i]))
{
@@ -1224,6 +1234,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 */
@@ -1257,8 +1306,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;
@@ -1270,27 +1324,40 @@ 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_BMS
static char szTemp[PLATFORM_MAX_PATH];
if (pFoundMap == NULL)
#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
static IVEngineServer *engine23 = (IVEngineServer *)(g_SMAPI->GetEngineFactory()("VEngineServer023", nullptr));
if (engine23)
{
ke::SafeStrcpy(szTemp, SM_ARRAYSIZE(szTemp), pMapName);
pFoundMap = szTemp;
nMapNameMax = 0;
}
static char szTemp[PLATFORM_MAX_PATH];
if (pFoundMap == NULL)
{
ke::SafeStrcpy(szTemp, SM_ARRAYSIZE(szTemp), pMapName);
pFoundMap = szTemp;
nMapNameMax = 0;
}
return static_cast<SMFindMapResult>(engine->FindMap(pFoundMap, static_cast<int>(nMapNameMax)));
#elif SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_SDK2013
static IVEngineServer *engine21 = (IVEngineServer *)(g_SMAPI->GetEngineFactory()("VEngineServer021", nullptr));
return engine21->IsMapValid(pMapName) == 0 ? SMFindMapResult::NotFound : SMFindMapResult::Found;
return static_cast<SMFindMapResult>(engine->FindMap(pFoundMap, static_cast<int>(nMapNameMax)));
}
else
{
static IVEngineServer *engine21 = (IVEngineServer *)(g_SMAPI->GetEngineFactory()("VEngineServer021", nullptr));
return engine21->IsMapValid(pMapName) == 0 ? SMFindMapResult::NotFound : SMFindMapResult::Found;
}
#else
return engine->IsMapValid(pMapName) == 0 ? SMFindMapResult::NotFound : SMFindMapResult::Found;
#endif
@@ -1318,11 +1385,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
@@ -1332,32 +1427,62 @@ 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
{
CSteamID sid(GetServerSteamId64());
CSteamID sid((uint64)GetServerSteamId64());
switch (sid.GetEAccountType())
{
+51 -5
View File
@@ -60,6 +60,33 @@ using namespace SourceMod;
#define HUD_PRINTTALK 3
#define HUD_PRINTCENTER 4
#if defined _WIN32
#define SOURCE_BIN_PREFIX ""
#define SOURCE_BIN_SUFFIX ""
#define SOURCE_BIN_EXT ".dll"
#elif defined __APPLE__
#define SOURCE_BIN_PREFIX ""
#define SOURCE_BIN_SUFFIX ""
#define SOURCE_BIN_EXT ".dylib"
#elif defined __linux__
#if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 \
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_LEFT4DEAD2 || SOURCE_ENGINE == SE_NUCLEARDAWN \
|| 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
#define SOURCE_BIN_PREFIX "lib"
#define SOURCE_BIN_SUFFIX ""
#else
#define SOURCE_BIN_PREFIX ""
#define SOURCE_BIN_SUFFIX "_i486"
#endif
#define SOURCE_BIN_EXT ".so"
#endif
#define FORMAT_SOURCE_BIN_NAME(basename) \
(SOURCE_BIN_PREFIX basename SOURCE_BIN_SUFFIX SOURCE_BIN_EXT)
struct DataTableInfo
{
struct SendPropPolicy
@@ -68,13 +95,21 @@ struct DataTableInfo
{
return strcmp(name, info.prop->GetName()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
static inline bool matches(const char *name, const DataTableInfo *info)
{
return strcmp(name, info->sc->GetName()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
DataTableInfo(ServerClass *sc)
: sc(sc)
{
@@ -90,6 +125,10 @@ struct DataMapCachePolicy
{
return strcmp(name, info.prop->fieldName) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
typedef NameHashSet<sm_datatable_info_t, DataMapCachePolicy> DataMapCache;
@@ -140,6 +179,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
@@ -190,10 +235,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:
@@ -232,7 +278,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
};
+3 -3
View File
@@ -308,7 +308,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 +398,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 +420,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))
+103 -42
View File
@@ -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;
@@ -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);
@@ -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,7 +598,7 @@ 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;
@@ -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,84 @@ 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;
}
unsigned int CBaseMenu::GetItemCount()
{
return m_items.length();
return m_items.size();
}
bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
@@ -733,7 +794,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 +805,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 +829,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 +886,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));
}
+22 -10
View File
@@ -32,6 +32,9 @@
#ifndef _INCLUDE_MENUSTYLE_BASE_H
#define _INCLUDE_MENUSTYLE_BASE_H
#include <memory>
#include <utility>
#include <IMenuManager.h>
#include <IPlayerHelpers.h>
#include <am-string.h>
@@ -43,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;
@@ -137,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();
@@ -151,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;
@@ -167,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
+29 -9
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,8 @@
#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>
#endif
extern const char *g_RadioNumTable[];
@@ -59,7 +61,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 +124,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 +182,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
int c = ((CCSUsrMsg_ShowMenu &)msg).display_time();
#else
bf_read br(bf->GetBasePointer(), 3);
@@ -197,7 +205,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
@@ -458,12 +466,20 @@ void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
}
else
{
display_len = ke::SafeSprintf(display_pkt,
display_len = ke::SafeStrcpy(display_pkt,
sizeof(display_pkt),
"%s",
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()
@@ -484,9 +500,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
// 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);
@@ -577,7 +597,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);
+4 -3
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())
{
+7 -15
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)
{
@@ -138,8 +130,8 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const
logger->LogMessage("[SM] Changed map to \"%s\"", newmap);
ke::SafeSprintf(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), newmap);
ke::SafeSprintf(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "Normal level change");
ke::SafeStrcpy(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), newmap);
ke::SafeStrcpy(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "Normal level change");
#if SOURCE_ENGINE != SE_DARKMESSIAH
RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::ChangeLevel, (newmap, unknown));
@@ -184,14 +176,14 @@ void NextMapManager::OnSourceModLevelChange( const char *mapName )
m_tempChangeInfo.m_mapName[0] ='\0';
m_tempChangeInfo.m_changeReason[0] = '\0';
m_tempChangeInfo.startTime = time(NULL);
ke::SafeSprintf(lastMap, sizeof(lastMap), "%s", mapName);
ke::SafeStrcpy(lastMap, sizeof(lastMap), mapName);
}
void NextMapManager::ForceChangeLevel( const char *mapName, const char* changeReason )
{
/* Store the mapname and reason */
ke::SafeSprintf(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), "%s", mapName);
ke::SafeSprintf(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "%s", changeReason);
ke::SafeStrcpy(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), mapName);
ke::SafeStrcpy(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), changeReason);
/* Change level and skip our hook */
g_forcedChange = true;
@@ -221,7 +213,7 @@ void CmdChangeLevelCallback()
if (g_NextMap.m_tempChangeInfo.m_mapName[0] == '\0')
{
ke::SafeSprintf(g_NextMap.m_tempChangeInfo.m_mapName, sizeof(g_NextMap.m_tempChangeInfo.m_mapName), command.Arg(1));
ke::SafeSprintf(g_NextMap.m_tempChangeInfo.m_changeReason, sizeof(g_NextMap.m_tempChangeInfo.m_changeReason), "changelevel Command");
ke::SafeStrcpy(g_NextMap.m_tempChangeInfo.m_mapName, sizeof(g_NextMap.m_tempChangeInfo.m_mapName), command.Arg(1));
ke::SafeStrcpy(g_NextMap.m_tempChangeInfo.m_changeReason, sizeof(g_NextMap.m_tempChangeInfo.m_changeReason), "changelevel Command");
}
}
+231 -76
View File
@@ -30,6 +30,7 @@
*/
#include "PlayerManager.h"
#include "sourcemod.h"
#include "IAdminSystem.h"
#include "ConCmdManager.h"
#include "MenuStyle_Valve.h"
@@ -63,7 +64,7 @@ const unsigned int *g_NumPlayersToAuth = NULL;
int lifestate_offset = -1;
List<ICommandTargetProcessor *> target_processors;
ConVar sm_debug_connect("sm_debug_connect", "0", 0, "Log Debug information about potential connection issues.");
ConVar sm_debug_connect("sm_debug_connect", "1", 0, "Log Debug information about potential connection issues.");
SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char *, const char *, char *, int);
SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, const char *);
@@ -92,6 +93,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;
@@ -102,7 +109,7 @@ class KickPlayerTimer : public ITimedEvent
public:
ResultType OnTimer(ITimer *pTimer, void *pData)
{
int userid = (int)pData;
int userid = (int)(intptr_t)pData;
int client = g_Players.GetClientOfUserId(userid);
if (client)
{
@@ -172,6 +179,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,6 +196,7 @@ 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);
@@ -204,6 +213,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 +237,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,6 +250,7 @@ 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);
@@ -250,6 +264,8 @@ void PlayerManager::OnSourceModShutdown()
{
SH_REMOVE_HOOK(ConCommand, Dispatch, maxplayersCmd, SH_STATIC(CmdMaxplayersCallback), true);
}
gameevents->RemoveListener(this);
}
ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
@@ -272,7 +288,7 @@ ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
} else if (strcasecmp(value, "off") == 0) {
m_QueryLang = false;
} else {
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
return ConfigResult_Reject;
}
return ConfigResult_Accept;
@@ -283,7 +299,7 @@ ConfigResult PlayerManager::OnSourceModConfigChanged(const char *key,
} else if ( strcasecmp(value, "no") == 0) {
m_bAuthstringValidation = false;
} else {
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
return ConfigResult_Reject;
}
return ConfigResult_Accept;
@@ -396,7 +412,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 +519,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
pPlayer->m_LangId = translator->GetServerLanguage();
#else
const char *name;
@@ -511,6 +527,8 @@ 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();
}
@@ -657,6 +675,8 @@ void PlayerManager::OnClientPutInServer(edict_t *pEntity, const char *playername
&& (m_SourceTVUserId == userId
#if SOURCE_ENGINE == SE_CSGO
|| 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)
|| (tv_name && strcmp(playername, tv_name->GetString()) == 0) || (tv_name && tv_name->GetString()[0] == 0 && strcmp(playername, "unnamed") == 0)
@@ -700,19 +720,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
else if(m_QueryLang)
{
// Not a bot
@@ -772,7 +792,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
// These games have the bug fixed where hltv/replay was getting kicked on hibernation
if (pPlayer->IsSourceTV() || pPlayer->IsReplay())
continue;
@@ -846,6 +866,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];
@@ -1065,14 +1167,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 +1193,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);
}
@@ -1242,65 +1348,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 +1424,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 +1450,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 +1596,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 +2035,7 @@ void CmdMaxplayersCallback()
g_Players.MaxPlayersChanged();
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
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 +2043,9 @@ 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;
OnClientLanguageChanged(i, new_langid);
return true;
}
@@ -1928,6 +2055,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 ***
@@ -1966,7 +2114,8 @@ 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
m_pIClient = engine->GetIServer()->GetClient(m_iIndex - 1);
#else
#if SOURCE_ENGINE == SE_SDK2013
@@ -2050,18 +2199,18 @@ void CPlayer::UpdateAuthIds()
}
char szAuthBuffer[64];
snprintf(szAuthBuffer, sizeof(szAuthBuffer), "STEAM_%u:%u:%u", steam2universe, m_SteamId.GetAccountID() & 1, m_SteamId.GetAccountID() >> 1);
ke::SafeSprintf(szAuthBuffer, sizeof(szAuthBuffer), "STEAM_%u:%u:%u", steam2universe, m_SteamId.GetAccountID() & 1, m_SteamId.GetAccountID() >> 1);
m_Steam2Id = szAuthBuffer;
// TODO: make sure all hl2sdks' steamclientpublic.h have k_unSteamUserDesktopInstance.
if (m_SteamId.GetUnAccountInstance() == 1 /* k_unSteamUserDesktopInstance */)
{
snprintf(szAuthBuffer, sizeof(szAuthBuffer), "[U:%u:%u]", m_SteamId.GetEUniverse(), m_SteamId.GetAccountID());
ke::SafeSprintf(szAuthBuffer, sizeof(szAuthBuffer), "[U:%u:%u]", m_SteamId.GetEUniverse(), m_SteamId.GetAccountID());
}
else
{
snprintf(szAuthBuffer, sizeof(szAuthBuffer), "[U:%u:%u:%u]", m_SteamId.GetEUniverse(), m_SteamId.GetAccountID(), m_SteamId.GetUnAccountInstance());
ke::SafeSprintf(szAuthBuffer, sizeof(szAuthBuffer), "[U:%u:%u:%u]", m_SteamId.GetEUniverse(), m_SteamId.GetAccountID(), m_SteamId.GetUnAccountInstance());
}
m_Steam3Id = szAuthBuffer;
@@ -2142,9 +2291,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
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 +2333,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 +2348,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 +2369,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 +2379,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 +2513,7 @@ void CPlayer::Kick(const char *str)
}
else
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
pClient->Disconnect(str);
#else
pClient->Disconnect("%s", str);
@@ -2480,7 +2631,7 @@ void CPlayer::DoBasicAdminChecks()
if (!g_Players.CheckSetAdminName(client, this, id))
{
int userid = engine->GetPlayerUserId(m_pEdict);
g_Timers.CreateTimer(&s_KickPlayerTimer, 0.1f, (void *)userid, 0);
g_Timers.CreateTimer(&s_KickPlayerTimer, 0.1f, (void *)(intptr_t)userid, 0);
}
return;
}
@@ -2495,7 +2646,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))
{
@@ -2511,7 +2662,11 @@ unsigned int CPlayer::GetLanguageId()
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()
+25 -7
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>
@@ -123,6 +124,7 @@ private:
bool IsAuthStringValidated();
bool SetEngineString();
bool SetCSteamID();
void ClearNetchannelQueue(void);
private:
bool m_IsConnected = false;
bool m_IsInGame = false;
@@ -131,9 +133,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;
@@ -149,14 +151,16 @@ private:
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
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 +193,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 +204,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 +215,11 @@ public: //IPlayerManager
int GetClientFromSerial(unsigned int serial);
void ClearAdminId(AdminId id);
void RecheckAnyAdmins();
public: // IGameEventListener2
void FireGameEvent(IGameEvent *pEvent);
#if SOURCE_ENGINE >= SE_ALIENSWARM
virtual int GetEventDebugID( void ) { return 42; }
#endif
public:
inline int MaxClients()
{
@@ -230,7 +243,7 @@ public:
{
return m_bInCCKVHook;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
#endif
private:
@@ -248,6 +261,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 +281,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
+9 -3
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;
}
+96 -5
View File
@@ -118,6 +118,13 @@ public:
return msg;
}
inline bool HasField(const char *pszFieldName)
{
GETCHECK_FIELD();
CHECK_FIELD_NOT_REPEATED();
return msg->GetReflection()->HasField(*msg, field);
}
inline bool GetInt32(const char *pszFieldName, int32 *out)
{
GETCHECK_FIELD();
@@ -341,6 +348,20 @@ public:
return true;
}
inline bool GetInt64OrUnsigned(const char *pszFieldName, int64 *out)
{
GETCHECK_FIELD();
CHECK_FIELD_TYPE2(INT64, UINT64);
CHECK_FIELD_NOT_REPEATED();
if (fieldType == protobuf::FieldDescriptor::CPPTYPE_UINT64)
*out = (int64)msg->GetReflection()->GetUInt64(*msg, field);
else
*out = msg->GetReflection()->GetInt64(*msg, field);
return true;
}
inline bool SetInt32OrUnsignedOrEnum(const char *pszFieldName, int32 value)
{
@@ -367,6 +388,24 @@ public:
return true;
}
inline bool SetInt64OrUnsigned(const char *pszFieldName, int64 value)
{
GETCHECK_FIELD();
CHECK_FIELD_TYPE2(INT64, UINT64);
CHECK_FIELD_NOT_REPEATED();
if (fieldType == protobuf::FieldDescriptor::CPPTYPE_UINT64)
{
msg->GetReflection()->SetUInt64(msg, field, (uint64)value);
}
else
{
msg->GetReflection()->SetInt64(msg, field, value);
}
return true;
}
inline bool GetRepeatedInt32OrUnsignedOrEnum(const char *pszFieldName, int index, int32 *out)
{
@@ -384,6 +423,21 @@ public:
return true;
}
inline bool GetRepeatedInt64OrUnsigned(const char *pszFieldName, int index, int64 *out)
{
GETCHECK_FIELD();
CHECK_FIELD_TYPE2(INT64, UINT64);
CHECK_FIELD_REPEATED();
CHECK_REPEATED_ELEMENT(index);
if (fieldType == protobuf::FieldDescriptor::CPPTYPE_UINT64)
*out = (int64)msg->GetReflection()->GetRepeatedUInt64(*msg, field, index);
else
*out = msg->GetReflection()->GetRepeatedInt64(*msg, field, index);
return true;
}
inline bool SetRepeatedInt32OrUnsignedOrEnum(const char *pszFieldName, int index, int32 value)
{
@@ -411,6 +465,25 @@ public:
return true;
}
inline bool SetRepeatedInt64OrUnsigned(const char *pszFieldName, int index, int64 value)
{
GETCHECK_FIELD();
CHECK_FIELD_TYPE2(INT64, UINT64);
CHECK_FIELD_REPEATED();
CHECK_REPEATED_ELEMENT(index);
if (fieldType == protobuf::FieldDescriptor::CPPTYPE_UINT64)
{
msg->GetReflection()->SetRepeatedUInt64(msg, field, index, (uint64)value);
}
else
{
msg->GetReflection()->SetRepeatedInt64(msg, field, index, value);
}
return true;
}
inline bool AddInt32OrUnsignedOrEnum(const char *pszFieldName, int32 value)
{
@@ -437,6 +510,24 @@ public:
return true;
}
inline bool AddInt64OrUnsigned(const char *pszFieldName, int64 value)
{
GETCHECK_FIELD();
CHECK_FIELD_TYPE2(INT64, UINT64);
CHECK_FIELD_REPEATED();
if (fieldType == protobuf::FieldDescriptor::CPPTYPE_UINT64)
{
msg->GetReflection()->AddUInt64(msg, field, (uint64)value);
}
else
{
msg->GetReflection()->AddInt64(msg, field, value);
}
return true;
}
inline bool GetBool(const char *pszFieldName, bool *out)
{
@@ -725,7 +816,7 @@ public:
inline bool GetColor(const char *pszFieldName, Color *out)
{
#if SOURCE_ENGINE != SE_CSGO
#if SOURCE_ENGINE != SE_CSGO && SOURCE_ENGINE != SE_BLADE
return false;
#else
GETCHECK_FIELD();
@@ -746,7 +837,7 @@ public:
inline bool SetColor(const char *pszFieldName, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
#if SOURCE_ENGINE != SE_CSGO && SOURCE_ENGINE != SE_BLADE
return false;
#else
GETCHECK_FIELD();
@@ -765,7 +856,7 @@ public:
inline bool GetRepeatedColor(const char *pszFieldName, int index, Color *out)
{
#if SOURCE_ENGINE != SE_CSGO
#if SOURCE_ENGINE != SE_CSGO && SOURCE_ENGINE != SE_BLADE
return false;
#else
GETCHECK_FIELD();
@@ -787,7 +878,7 @@ public:
inline bool SetRepeatedColor(const char *pszFieldName, int index, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
#if SOURCE_ENGINE != SE_CSGO && SOURCE_ENGINE != SE_BLADE
return false;
#else
GETCHECK_FIELD();
@@ -807,7 +898,7 @@ public:
inline bool AddColor(const char *pszFieldName, const Color &value)
{
#if SOURCE_ENGINE != SE_CSGO
#if SOURCE_ENGINE != SE_CSGO && SOURCE_ENGINE != SE_BLADE
return false;
#else
GETCHECK_FIELD();
+29 -18
View File
@@ -35,12 +35,14 @@
#if SOURCE_ENGINE == SE_CSGO
#include <cstrike15_usermessage_helpers.h>
#elif SOURCE_ENGINE == SE_BLADE
#include <berimbau_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
SH_DECL_HOOK3_void(IVEngineServer, SendUserMessage, SH_NOATTRIB, 0, IRecipientFilter &, int, const protobuf::Message &);
#else
#if SOURCE_ENGINE >= SE_LEFT4DEAD
@@ -49,7 +51,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
UserMessages::UserMessages()
#ifndef USE_PROTOBUF_USERMESSAGES
@@ -93,7 +95,7 @@ void UserMessages::OnSourceModAllShutdown()
{
if (m_HookCount)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
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 +113,8 @@ 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);
#else
int msgid;
@@ -148,6 +152,8 @@ 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);
#endif
if (!pszName)
return false;
@@ -297,7 +303,7 @@ bool UserMessages::EndMessage()
return false;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
if (m_CurFlags & USERMSG_BLOCKHOOKS)
{
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
@@ -327,7 +333,7 @@ bool UserMessages::EndMessage()
} else {
engine->MessageEnd();
}
#endif // SE_CSGO
#endif // SE_CSGO || SE_BLADE
m_InExec = false;
m_CurFlags = 0;
@@ -412,7 +418,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
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 +444,8 @@ 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);
#endif
}
#endif
@@ -487,7 +495,7 @@ void UserMessages::_DecRefCounter()
{
if (--m_HookCount == 0)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
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 +507,17 @@ void UserMessages::_DecRefCounter()
}
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
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);
#endif
OnStartMessage_Pre(&filter, msg_type, pszName);
if (m_FakeMetaRes == MRES_SUPERCEDE)
{
int size = msg.ByteSize();
@@ -517,9 +530,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 +562,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
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 +602,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
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 +767,7 @@ void UserMessages::OnMessageEnd_Pre()
if (!handled && intercepted)
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
#else
bf_write *engine_bfw;
@@ -768,11 +779,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
}
{
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
int size = m_OrigBuffer->ByteSize();
uint8 *data = (uint8 *)stackalloc(size);
m_OrigBuffer->SerializePartialToArray(data, size);
@@ -802,7 +813,7 @@ void UserMessages::OnMessageEnd_Pre()
iter++;
}
#if SOURCE_ENGINE == SE_CSGO
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
delete pTempMsg;
#endif
}
+3 -3
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
#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
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
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
+22 -14
View File
@@ -36,7 +36,11 @@
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK1_void(ICvar, UnregisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
SH_DECL_HOOK2_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *, bool);
#else
SH_DECL_HOOK1_void(ICvar, RegisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
#endif
#else
SH_DECL_HOOK1_void(ICvar, RegisterConCommandBase, SH_NOATTRIB, 0, ConCommandBase *);
#endif
@@ -78,7 +82,11 @@ public:
#endif
}
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_BLADE
void LinkConCommandBase(ConCommandBase *pBase, bool unknown)
#else
void LinkConCommandBase(ConCommandBase *pBase)
#endif
{
IConCommandLinkListener *listener = IConCommandLinkListener::head;
while (listener)
@@ -100,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)
-551
View File
@@ -1,551 +0,0 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: 2006-08-13 06:34:30 -0500 (Sun, 13 Aug 2006) $
//
//-----------------------------------------------------------------------------
// $NoKeywords: $
//=============================================================================//
#ifndef CONVAR_H
#define CONVAR_H
#if _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#ifdef _WIN32
#define FORCEINLINE_CVAR FORCEINLINE
#elif _LINUX
#define FORCEINLINE_CVAR __inline__ FORCEINLINE
#else
#error "implement me"
#endif
// The default, no flags at all
#define FCVAR_NONE 0
// Command to ConVars and ConCommands
// ConVar Systems
#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc.
#define FCVAR_LAUNCHER (1<<1) // defined by launcher
#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL
#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL
#define FCVAR_MATERIAL_SYSTEM (1<<4) // Defined by the material system.
#define FCVAR_DATACACHE (1<<19) // Defined by the datacache system.
#define FCVAR_STUDIORENDER (1<<15) // Defined by the studiorender system.
#define FCVAR_FILESYSTEM (1<<21) // Defined by the file system.
#define FCVAR_PLUGIN (1<<18) // Defined by a 3rd party plugin.
#define FCVAR_TOOLSYSTEM (1<<20) // Defined by an IToolSystem library
#define FCVAR_SOUNDSYSTEM (1<<23) // Defined by the soundsystem library
#define FCVAR_INPUTSYSTEM (1<<25) // Defined by the inputsystem dll
#define FCVAR_NETWORKSYSTEM (1<<26) // Defined by the network system
// NOTE!! if you add a cvar system, add it here too!!!!
// the engine lacks a cvar flag, but needs it for xbox
// an engine cvar is thus a cvar not marked with any other system
#define FCVAR_NON_ENGINE ((FCVAR_LAUNCHER|FCVAR_GAMEDLL|FCVAR_CLIENTDLL|FCVAR_MATERIAL_SYSTEM|FCVAR_DATACACHE|FCVAR_STUDIORENDER|FCVAR_FILESYSTEM|FCVAR_PLUGIN|FCVAR_TOOLSYSTEM|FCVAR_SOUNDSYSTEM|FCVAR_INPUTSYSTEM|FCVAR_NETWORKSYSTEM))
// ConVar only
#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value
#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server.
#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc
#define FCVAR_NOTIFY (1<<8) // notifies players when changed
#define FCVAR_USERINFO (1<<9) // changes the client's info string
#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats
#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ).
#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log
#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar
// It's a ConVar that's shared between the client and the server.
// At signon, the values of all such ConVars are sent from the server to the client (skipped for local
// client, of course )
// If a change is requested it must come from the console (i.e., no remote client changes)
// If a value is changed while a server is active, it's replicated to all connected clients
#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time
#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file
#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles
#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server
#define FCVAR_ARCHIVE_XBOX (1<<24) // cvar written to config.cfg on the Xbox
// #define FCVAR_AVAILABLE (1<<27)
// #define FCVAR_AVAILABLE (1<<28)
// #define FCVAR_AVAILABLE (1<<29)
// #define FCVAR_AVAILABLE (1<<30)
// #define FCVAR_AVAILABLE (1<<31)
class ConVar;
class ConCommand;
class ConCommandBase;
// Any executable that wants to use ConVars need to implement one of
// these to hook up access to console variables.
class IConCommandBaseAccessor
{
public:
// Flags is a combination of FCVAR flags in cvar.h.
// hOut is filled in with a handle to the variable.
virtual bool RegisterConCommandBase( ConCommandBase *pVar )=0;
};
// You don't have to instantiate one of these, just call its
// OneTimeInit function when your executable is initializing.
class ConCommandBaseMgr
{
public:
// Call this ONCE when the executable starts up.
static void OneTimeInit( IConCommandBaseAccessor *pAccessor );
#ifdef _XBOX
static bool Fixup( ConCommandBase* pConCommandBase );
#ifndef _RETAIL
static void PublishCommands( bool bForce );
#endif
#endif
};
// Called when a ConVar changes value
typedef void ( *FnChangeCallback )( ConVar *var, char const *pOldString );
// Called when a ConCommand needs to execute
typedef void ( *FnCommandCallback )( void );
#define COMMAND_COMPLETION_MAXITEMS 64
#define COMMAND_COMPLETION_ITEM_LENGTH 64
// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
typedef int ( *FnCommandCompletionCallback )( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
//-----------------------------------------------------------------------------
// Purpose: The base console invoked command/cvar interface
//-----------------------------------------------------------------------------
class ConCommandBase
{
friend class ConCommandBaseMgr;
friend class CCvar;
friend class ConVar;
friend class ConCommand;
public:
ConCommandBase( void );
ConCommandBase( char const *pName, char const *pHelpString = 0,
int flags = 0 );
virtual ~ConCommandBase( void );
virtual bool IsCommand( void ) const;
// Check flag
virtual bool IsBitSet( int flag ) const;
// Set flag
virtual void AddFlags( int flags );
// Return name of cvar
virtual char const *GetName( void ) const;
// Return help text for cvar
virtual char const *GetHelpText( void ) const;
// Deal with next pointer
const ConCommandBase *GetNext( void ) const;
void SetNext( ConCommandBase *next );
virtual bool IsRegistered( void ) const;
// Global methods
static ConCommandBase const *GetCommands( void );
static void AddToList( ConCommandBase *var );
static void RemoveFlaggedCommands( int flag );
static void RevertFlaggedCvars( int flag );
static ConCommandBase const *FindCommand( char const *name );
protected:
virtual void Create( char const *pName, char const *pHelpString = 0,
int flags = 0 );
// Used internally by OneTimeInit to initialize.
virtual void Init();
// Internal copy routine ( uses new operator from correct module )
char *CopyString( char const *from );
// Next ConVar in chain
ConCommandBase *m_pNext;
private:
// Has the cvar been added to the global list?
bool m_bRegistered;
// Static data
char const *m_pszName;
char const *m_pszHelpString;
// ConVar flags
int m_nFlags;
protected:
// ConVars add themselves to this list for the executable. Then ConVarMgr::Init() runs through
// all the console variables and registers them.
static ConCommandBase *s_pConCommandBases;
// ConVars in this executable use this 'global' to access values.
static IConCommandBaseAccessor *s_pAccessor;
public: // Hackalicous
inline int GetFlags() const
{
return m_nFlags;
}
inline void SetFlags(int flags)
{
m_nFlags = flags;
}
};
//-----------------------------------------------------------------------------
// Purpose: The console invoked command
//-----------------------------------------------------------------------------
class ConCommand : public ConCommandBase
{
friend class ConCommandBaseMgr;
friend class CCvar;
#ifdef _STATIC_LINKED
friend class G_ConCommand;
friend class C_ConCommand;
friend class M_ConCommand;
friend class S_ConCommand;
friend class D_ConCommand;
#endif
public:
typedef ConCommandBase BaseClass;
ConCommand( void );
ConCommand( char const *pName, FnCommandCallback callback,
char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
virtual ~ConCommand( void );
virtual bool IsCommand( void ) const;
virtual int AutoCompleteSuggest( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
virtual bool CanAutoComplete( void );
// Invoke the function
virtual void Dispatch( void );
private:
virtual void Create( char const *pName, FnCommandCallback callback,
char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
// Call this function when executing the command
FnCommandCallback m_fnCommandCallback;
FnCommandCompletionCallback m_fnCompletionCallback;
bool m_bHasCompletionCallback;
public: // Hackalicous
inline FnCommandCallback GetCallback() const
{
return m_fnCommandCallback;
}
};
//-----------------------------------------------------------------------------
// Purpose: A console variable
//-----------------------------------------------------------------------------
class ConVar : public ConCommandBase
{
friend class ConCommandBaseMgr;
friend class CCvar;
friend class CDefaultCvar;
#ifdef _STATIC_LINKED
friend class G_ConVar;
friend class C_ConVar;
friend class M_ConVar;
friend class S_ConVar;
friend class D_ConVar;
#endif
public:
typedef ConCommandBase BaseClass;
ConVar( char const *pName, char const *pDefaultValue, int flags = 0);
ConVar( char const *pName, char const *pDefaultValue, int flags,
char const *pHelpString );
ConVar( char const *pName, char const *pDefaultValue, int flags,
char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax );
ConVar( char const *pName, char const *pDefaultValue, int flags,
char const *pHelpString, FnChangeCallback callback );
ConVar( char const *pName, char const *pDefaultValue, int flags,
char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax,
FnChangeCallback callback );
virtual ~ConVar( void );
virtual bool IsBitSet( int flag ) const;
virtual char const* GetHelpText( void ) const;
virtual bool IsRegistered( void ) const;
virtual char const *GetName( void ) const;
virtual void AddFlags( int flags );
virtual bool IsCommand( void ) const;
// Install a change callback (there shouldn't already be one....)
void InstallChangeCallback( FnChangeCallback callback );
// Retrieve value
FORCEINLINE_CVAR float GetFloat( void ) const;
FORCEINLINE_CVAR int GetInt( void ) const;
FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); }
FORCEINLINE_CVAR char const *GetString( void ) const;
// Any function that allocates/frees memory needs to be virtual or else you'll have crashes
// from alloc/free across dll/exe boundaries.
// These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue).
virtual void SetValue( char const *value );
virtual void SetValue( float value );
virtual void SetValue( int value );
// Reset to default value
void Revert( void );
// True if it has a min/max setting
bool GetMin( float& minVal ) const;
bool GetMax( float& maxVal ) const;
char const *GetDefault( void ) const;
static void RevertAll( void );
private:
// Called by CCvar when the value of a var is changing.
virtual void InternalSetValue(char const *value);
// For CVARs marked FCVAR_NEVER_AS_STRING
virtual void InternalSetFloatValue( float fNewValue );
virtual void InternalSetIntValue( int nValue );
virtual bool ClampValue( float& value );
virtual void ChangeStringValue( char const *tempVal );
virtual void Create( char const *pName, char const *pDefaultValue, int flags = 0,
char const *pHelpString = 0, bool bMin = false, float fMin = 0.0,
bool bMax = false, float fMax = false, FnChangeCallback callback = 0 );
// Used internally by OneTimeInit to initialize.
virtual void Init();
private:
// This either points to "this" or it points to the original declaration of a ConVar.
// This allows ConVars to exist in separate modules, and they all use the first one to be declared.
// m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar).
ConVar *m_pParent;
// Static data
char const *m_pszDefaultValue;
// Value
// Dynamically allocated
char *m_pszString;
int m_StringLength;
// Values
float m_fValue;
int m_nValue;
// Min/Max values
bool m_bHasMin;
float m_fMinVal;
bool m_bHasMax;
float m_fMaxVal;
// Call this function when ConVar changes
FnChangeCallback m_fnChangeCallback;
public: // Hackalicous
inline FnChangeCallback GetCallback() const
{
return m_fnChangeCallback;
}
inline void SetMin(bool set, float min=0.0)
{
m_bHasMin = set;
m_fMinVal = min;
}
inline void SetMax(bool set, float max=0.0)
{
m_bHasMax = set;
m_fMaxVal = max;
}
};
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a float
// Output : float
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR float ConVar::GetFloat( void ) const
{
return m_pParent->m_fValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
// Output : int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
{
return m_pParent->m_nValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
// Output : char const *
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR char const *ConVar::GetString( void ) const
{
if ( m_nFlags & FCVAR_NEVER_AS_STRING )
{
return "FCVAR_NEVER_AS_STRING";
}
return ( m_pParent->m_pszString ) ? m_pParent->m_pszString : "";
}
#ifdef _STATIC_LINKED
// identifies subsystem via piggybacking constructors with flags
class G_ConCommand : public ConCommand
{
public:
G_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_GAMEDLL, completionFunc) {}
};
class C_ConCommand : public ConCommand
{
public:
C_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_CLIENTDLL, completionFunc) {}
};
class M_ConCommand : public ConCommand
{
public:
M_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_MATERIAL_SYSTEM, completionFunc) {}
};
class S_ConCommand : public ConCommand
{
public:
S_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_STUDIORENDER, completionFunc) {}
};
class D_ConCommand : public ConCommand
{
public:
D_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_DATACACHE, completionFunc) {}
};
typedef void ( *G_FnChangeCallback )( G_ConVar *var, char const *pOldString );
typedef void ( *C_FnChangeCallback )( C_ConVar *var, char const *pOldString );
typedef void ( *M_FnChangeCallback )( M_ConVar *var, char const *pOldString );
typedef void ( *S_FnChangeCallback )( S_ConVar *var, char const *pOldString );
typedef void ( *D_FnChangeCallback )( D_ConVar *var, char const *pOldString );
class G_ConVar : public ConVar
{
public:
G_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL) {}
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString ) {}
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, bMin, fMin, bMax, fMax) {}
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, G_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, (FnChangeCallback)callback ) {}
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, G_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
};
class C_ConVar : public ConVar
{
public:
C_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL) {}
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString ) {}
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, bMin, fMin, bMax, fMax) {}
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, C_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, (FnChangeCallback)callback ) {}
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, C_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
};
class M_ConVar : public ConVar
{
public:
M_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM) {}
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString ) {}
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, bMin, fMin, bMax, fMax) {}
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, (FnChangeCallback)callback ) {}
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
};
class S_ConVar : public ConVar
{
public:
S_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER) {}
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString ) {}
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, bMin, fMin, bMax, fMax) {}
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, (FnChangeCallback)callback ) {}
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, S_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
};
class D_ConVar : public ConVar
{
public:
D_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE) {}
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString ) {}
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, bMin, fMin, bMax, fMax) {}
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, (FnChangeCallback)callback ) {}
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, D_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
};
// redirect these declarations to their specific subsystem
#ifdef GAME_DLL
#define ConCommand G_ConCommand
#define ConVar G_ConVar
#endif
#ifdef CLIENT_DLL
#define ConCommand C_ConCommand
#define ConVar C_ConVar
#endif
#ifdef MATERIALSYSTEM_DLL
#define ConCommand M_ConCommand
#define ConVar M_ConVar
#endif
#ifdef STUDIORENDER_DLL
#define ConCommand S_ConCommand
#define ConVar S_ConVar
#endif
#ifdef DATACACHE_DLL
#define ConCommand D_ConCommand
#define ConVar D_ConVar
#endif
#endif // _STATIC_LINKED
//-----------------------------------------------------------------------------
// Purpose: Utility to quicky generate a simple console command
//-----------------------------------------------------------------------------
#define CON_COMMAND( name, description ) \
static void name(); \
static ConCommand name##_command( #name, name, description ); \
static void name()
//-----------------------------------------------------------------------------
// Purpose: Utility to quicky generate a simple console command
//-----------------------------------------------------------------------------
#define CON_COMMAND_F( name, description, flags ) \
static void name(); \
static ConCommand name##_command( #name, name, description, flags ); \
static void name()
#endif // CONVAR_H
-718
View File
@@ -1,718 +0,0 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $NoKeywords: $
//===========================================================================//
#ifndef CONVAR_H
#define CONVAR_H
#if _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#include "tier1/iconvar.h"
#include "tier1/utlvector.h"
#include "tier1/utlstring.h"
#include "icvar.h"
#ifdef _WIN32
#define FORCEINLINE_CVAR FORCEINLINE
#elif defined _LINUX || defined __APPLE__
#define FORCEINLINE_CVAR inline
#else
#error "implement me"
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class ConVar;
class CCommand;
class ConCommand;
class ConCommandBase;
struct characterset_t;
//-----------------------------------------------------------------------------
// Any executable that wants to use ConVars need to implement one of
// these to hook up access to console variables.
//-----------------------------------------------------------------------------
class IConCommandBaseAccessor
{
public:
// Flags is a combination of FCVAR flags in cvar.h.
// hOut is filled in with a handle to the variable.
virtual bool RegisterConCommandBase( ConCommandBase *pVar ) = 0;
};
//-----------------------------------------------------------------------------
// Helper method for console development
//-----------------------------------------------------------------------------
#if defined( _X360 ) && !defined( _RETAIL )
void ConVar_PublishToVXConsole();
#endif
//-----------------------------------------------------------------------------
// Called when a ConCommand needs to execute
//-----------------------------------------------------------------------------
typedef void ( *FnCommandCallbackV1_t )( void );
typedef void ( *FnCommandCallback_t )( const CCommand &command );
#define COMMAND_COMPLETION_MAXITEMS 64
#define COMMAND_COMPLETION_ITEM_LENGTH 64
//-----------------------------------------------------------------------------
// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
//-----------------------------------------------------------------------------
typedef int ( *FnCommandCompletionCallback )( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
//-----------------------------------------------------------------------------
// Interface version
//-----------------------------------------------------------------------------
class ICommandCallback
{
public:
virtual void CommandCallback( const CCommand &command ) = 0;
};
class ICommandCompletionCallback
{
public:
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) = 0;
};
//-----------------------------------------------------------------------------
// Purpose: The base console invoked command/cvar interface
//-----------------------------------------------------------------------------
class ConCommandBase
{
friend class CCvar;
friend class ConVar;
friend class ConCommand;
friend void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor );
friend void ConVar_PublishToVXConsole();
// FIXME: Remove when ConVar changes are done
friend class CDefaultCvar;
public:
ConCommandBase( void );
ConCommandBase( const char *pName, const char *pHelpString = 0,
int flags = 0 );
virtual ~ConCommandBase( void );
virtual bool IsCommand( void ) const;
// Check flag
virtual bool IsFlagSet( int flag ) const;
// Set flag
virtual void AddFlags( int flags );
// Remove flag
virtual void RemoveFlags( int flags );
// Get flags
virtual int GetFlags( void ) const;
// Return name of cvar
virtual const char *GetName( void ) const;
// Return help text for cvar
virtual const char *GetHelpText( void ) const;
// Deal with next pointer
const ConCommandBase *GetNext( void ) const;
ConCommandBase *GetNext( void );
inline void SetNext(ConCommandBase *pBase)
{
m_pNext = pBase;
}
virtual bool IsRegistered( void ) const;
// Returns the DLL identifier
virtual CVarDLLIdentifier_t GetDLLIdentifier() const;
protected:
virtual void Create( const char *pName, const char *pHelpString = 0,
int flags = 0 );
// Used internally by OneTimeInit to initialize/shutdown
virtual void Init();
void Shutdown();
// Internal copy routine ( uses new operator from correct module )
char *CopyString( const char *from );
private:
// Next ConVar in chain
// Prior to register, it points to the next convar in the DLL.
// Once registered, though, m_pNext is reset to point to the next
// convar in the global list
ConCommandBase *m_pNext;
// Has the cvar been added to the global list?
bool m_bRegistered;
// Static data
const char *m_pszName;
const char *m_pszHelpString;
// ConVar flags
int m_nFlags;
protected:
// ConVars add themselves to this list for the executable.
// Then ConVar_Register runs through all the console variables
// and registers them into a global list stored in vstdlib.dll
static ConCommandBase *s_pConCommandBases;
// ConVars in this executable use this 'global' to access values.
static IConCommandBaseAccessor *s_pAccessor;
public:
inline void SetFlags(int flags)
{
m_nFlags = flags;
}
};
//-----------------------------------------------------------------------------
// Command tokenizer
//-----------------------------------------------------------------------------
class CCommand
{
public:
CCommand();
CCommand( int nArgC, const char **ppArgV );
bool Tokenize( const char *pCommand, characterset_t *pBreakSet = NULL );
void Reset();
int ArgC() const;
const char **ArgV() const;
const char *ArgS() const; // All args that occur after the 0th arg, in string form
const char *GetCommandString() const; // The entire command in string form, including the 0th arg
const char *operator[]( int nIndex ) const; // Gets at arguments
const char *Arg( int nIndex ) const; // Gets at arguments
// Helper functions to parse arguments to commands.
const char* FindArg( const char *pName ) const;
int FindArgInt( const char *pName, int nDefaultVal ) const;
static int MaxCommandLength();
static characterset_t* DefaultBreakSet();
private:
enum
{
COMMAND_MAX_ARGC = 64,
COMMAND_MAX_LENGTH = 512,
};
int m_nArgc;
int m_nArgv0Size;
char m_pArgSBuffer[ COMMAND_MAX_LENGTH ];
char m_pArgvBuffer[ COMMAND_MAX_LENGTH ];
const char* m_ppArgv[ COMMAND_MAX_ARGC ];
};
inline int CCommand::MaxCommandLength()
{
return COMMAND_MAX_LENGTH - 1;
}
inline int CCommand::ArgC() const
{
return m_nArgc;
}
inline const char **CCommand::ArgV() const
{
return m_nArgc ? (const char**)m_ppArgv : NULL;
}
inline const char *CCommand::ArgS() const
{
return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : "";
}
inline const char *CCommand::GetCommandString() const
{
return m_nArgc ? m_pArgSBuffer : "";
}
inline const char *CCommand::Arg( int nIndex ) const
{
// FIXME: Many command handlers appear to not be particularly careful
// about checking for valid argc range. For now, we're going to
// do the extra check and return an empty string if it's out of range
if ( nIndex < 0 || nIndex >= m_nArgc )
return "";
return m_ppArgv[nIndex];
}
inline const char *CCommand::operator[]( int nIndex ) const
{
return Arg( nIndex );
}
//-----------------------------------------------------------------------------
// Purpose: The console invoked command
//-----------------------------------------------------------------------------
class ConCommand : public ConCommandBase
{
friend class CCvar;
public:
typedef ConCommandBase BaseClass;
ConCommand( const char *pName, FnCommandCallbackV1_t callback,
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
ConCommand( const char *pName, FnCommandCallback_t callback,
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
ConCommand( const char *pName, ICommandCallback *pCallback,
const char *pHelpString = 0, int flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 );
virtual ~ConCommand( void );
virtual bool IsCommand( void ) const;
virtual int AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands );
virtual bool CanAutoComplete( void );
// Invoke the function
virtual void Dispatch( const CCommand &command );
private:
// NOTE: To maintain backward compat, we have to be very careful:
// All public virtual methods must appear in the same order always
// since engine code will be calling into this code, which *does not match*
// in the mod code; it's using slightly different, but compatible versions
// of this class. Also: Be very careful about adding new fields to this class.
// Those fields will not exist in the version of this class that is instanced
// in mod code.
// Call this function when executing the command
union
{
FnCommandCallbackV1_t m_fnCommandCallbackV1;
FnCommandCallback_t m_fnCommandCallback;
ICommandCallback *m_pCommandCallback;
};
union
{
FnCommandCompletionCallback m_fnCompletionCallback;
ICommandCompletionCallback *m_pCommandCompletionCallback;
};
bool m_bHasCompletionCallback : 1;
bool m_bUsingNewCommandCallback : 1;
bool m_bUsingCommandCallbackInterface : 1;
public:
inline FnCommandCallback_t GetCallback() const
{
return m_fnCommandCallback;
}
};
//-----------------------------------------------------------------------------
// Purpose: A console variable
//-----------------------------------------------------------------------------
class ConVar : public ConCommandBase, public IConVar
{
friend class CCvar;
friend class ConVarRef;
public:
typedef ConCommandBase BaseClass;
ConVar( const char *pName, const char *pDefaultValue, int flags = 0);
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, FnChangeCallback_t callback );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax,
FnChangeCallback_t callback );
virtual ~ConVar( void );
virtual bool IsCommand( void ) const;
virtual bool IsFlagSet( int flag ) const;
virtual void AddFlags( int flags );
virtual int GetFlags( void ) const;
virtual const char *GetName( void ) const;
virtual const char* GetHelpText( void ) const;
virtual bool IsRegistered( void ) const;
// Install a change callback (there shouldn't already be one....)
void InstallChangeCallback( FnChangeCallback_t callback );
// Retrieve value
FORCEINLINE_CVAR float GetFloat( void ) const;
FORCEINLINE_CVAR int GetInt( void ) const;
FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); }
FORCEINLINE_CVAR char const *GetString( void ) const;
// Used internally by OneTimeInit to initialize.
virtual void Init();
virtual const char *GetBaseName( void ) const;
virtual int GetSplitScreenPlayerSlot ( void ) const;
// Any function that allocates/frees memory needs to be virtual or else you'll have crashes
// from alloc/free across dll/exe boundaries.
// These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue).
virtual void SetValue( const char *value );
virtual void SetValue( float value );
virtual void SetValue( int value );
#if SOURCE_ENGINE >= SE_NUCLEARDAWN
virtual void SetValue( Color value );
#endif
// Reset to default value
void Revert( void );
// True if it has a min/max setting
bool GetMin( float& minVal ) const;
bool GetMax( float& maxVal ) const;
const char *GetDefault( void ) const;
private:
// Called by CCvar when the value of a var is changing.
virtual void InternalSetValue(const char *value);
// For CVARs marked FCVAR_NEVER_AS_STRING
virtual void InternalSetFloatValue( float fNewValue );
virtual void InternalSetIntValue( int nValue );
#if SOURCE_ENGINE >= SE_LEFT4DEAD2
virtual void InternalSetColorValue( Color value );
#endif
virtual bool ClampValue( float& value );
virtual void ChangeStringValue( const char *tempVal, float flOldValue );
virtual void Create( const char *pName, const char *pDefaultValue, int flags = 0,
const char *pHelpString = 0, bool bMin = false, float fMin = 0.0,
bool bMax = false, float fMax = false, FnChangeCallback_t callback = 0 );
private:
// This either points to "this" or it points to the original declaration of a ConVar.
// This allows ConVars to exist in separate modules, and they all use the first one to be declared.
// m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar).
ConVar *m_pParent;
// Static data
const char *m_pszDefaultValue;
// Value
// Dynamically allocated
char *m_pszString;
int m_StringLength;
// Values
float m_fValue;
int m_nValue;
// Min/Max values
bool m_bHasMin;
float m_fMinVal;
bool m_bHasMax;
float m_fMaxVal;
// Call this function when ConVar changes
FnChangeCallback_t m_fnChangeCallback;
public:
inline FnChangeCallback_t GetCallback() const
{
return m_fnChangeCallback;
}
inline void SetMin(bool set, float min=0.0)
{
m_bHasMin = set;
m_fMinVal = min;
}
inline void SetMax(bool set, float max=0.0)
{
m_bHasMax = set;
m_fMaxVal = max;
}
};
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a float
// Output : float
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR float ConVar::GetFloat( void ) const
{
return m_pParent->m_fValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
// Output : int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
{
return m_pParent->m_nValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
// Output : const char *
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR const char *ConVar::GetString( void ) const
{
if ( m_nFlags & FCVAR_NEVER_AS_STRING )
return "FCVAR_NEVER_AS_STRING";
return ( m_pParent->m_pszString ) ? m_pParent->m_pszString : "";
}
//-----------------------------------------------------------------------------
// Used to read/write convars that already exist (replaces the FindVar method)
//-----------------------------------------------------------------------------
class ConVarRef
{
public:
ConVarRef( const char *pName );
ConVarRef( const char *pName, bool bIgnoreMissing );
ConVarRef( IConVar *pConVar );
void Init( const char *pName, bool bIgnoreMissing );
bool IsValid() const;
bool IsFlagSet( int nFlags ) const;
IConVar *GetLinkedConVar();
// Get/Set value
float GetFloat( void ) const;
int GetInt( void ) const;
bool GetBool() const { return !!GetInt(); }
const char *GetString( void ) const;
void SetValue( const char *pValue );
void SetValue( float flValue );
void SetValue( int nValue );
void SetValue( bool bValue );
const char *GetName() const;
const char *GetDefault() const;
private:
// High-speed method to read convar data
IConVar *m_pConVar;
ConVar *m_pConVarState;
};
//-----------------------------------------------------------------------------
// Did we find an existing convar of that name?
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR bool ConVarRef::IsFlagSet( int nFlags ) const
{
return ( m_pConVar->IsFlagSet( nFlags ) != 0 );
}
FORCEINLINE_CVAR IConVar *ConVarRef::GetLinkedConVar()
{
return m_pConVar;
}
FORCEINLINE_CVAR const char *ConVarRef::GetName() const
{
return m_pConVar->GetName();
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a float
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR float ConVarRef::GetFloat( void ) const
{
return m_pConVarState->m_fValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const
{
return m_pConVarState->m_nValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR const char *ConVarRef::GetString( void ) const
{
Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) );
return m_pConVarState->m_pszString;
}
FORCEINLINE_CVAR void ConVarRef::SetValue( const char *pValue )
{
m_pConVar->SetValue( pValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( float flValue )
{
m_pConVar->SetValue( flValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( int nValue )
{
m_pConVar->SetValue( nValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( bool bValue )
{
m_pConVar->SetValue( bValue ? 1 : 0 );
}
FORCEINLINE_CVAR const char *ConVarRef::GetDefault() const
{
return m_pConVarState->m_pszDefaultValue;
}
//-----------------------------------------------------------------------------
// Called by the framework to register ConCommands with the ICVar
//-----------------------------------------------------------------------------
void ConVar_Register( int nCVarFlag = 0, IConCommandBaseAccessor *pAccessor = NULL );
void ConVar_Unregister( );
//-----------------------------------------------------------------------------
// Utility methods
//-----------------------------------------------------------------------------
void ConVar_PrintFlags( const ConCommandBase *var );
void ConVar_PrintDescription( const ConCommandBase *pVar );
//-----------------------------------------------------------------------------
// Purpose: Utility class to quickly allow ConCommands to call member methods
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning (disable : 4355 )
#endif
template< class T >
class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, public ICommandCompletionCallback
{
typedef ConCommand BaseClass;
typedef void ( T::*FnMemberCommandCallback_t )( const CCommand &command );
typedef int ( T::*FnMemberCommandCompletionCallback_t )( const char *pPartial, CUtlVector< CUtlString > &commands );
public:
CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString = 0,
int flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) :
BaseClass( pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL )
{
m_pOwner = pOwner;
m_Func = callback;
m_CompletionFunc = completionFunc;
}
~CConCommandMemberAccessor()
{
Shutdown();
}
void SetOwner( T* pOwner )
{
m_pOwner = pOwner;
}
virtual void CommandCallback( const CCommand &command )
{
Assert( m_pOwner && m_Func );
(m_pOwner->*m_Func)( command );
}
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands )
{
Assert( m_pOwner && m_CompletionFunc );
return (m_pOwner->*m_CompletionFunc)( pPartial, commands );
}
private:
T* m_pOwner;
FnMemberCommandCallback_t m_Func;
FnMemberCommandCompletionCallback_t m_CompletionFunc;
};
#ifdef _MSC_VER
#pragma warning ( default : 4355 )
#endif
//-----------------------------------------------------------------------------
// Purpose: Utility macros to quicky generate a simple console command
//-----------------------------------------------------------------------------
#define CON_COMMAND( name, description ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description ); \
static void name( const CCommand &args )
#define CON_COMMAND_F( name, description, flags ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description, flags ); \
static void name( const CCommand &args )
#define CON_COMMAND_F_COMPLETION( name, description, flags, completion ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description, flags, completion ); \
static void name( const CCommand &args )
#define CON_COMMAND_EXTERN( name, _funcname, description ) \
void _funcname( const CCommand &args ); \
static ConCommand name##_command( #name, _funcname, description ); \
void _funcname( const CCommand &args )
#define CON_COMMAND_EXTERN_F( name, _funcname, description, flags ) \
void _funcname( const CCommand &args ); \
static ConCommand name##_command( #name, _funcname, description, flags ); \
void _funcname( const CCommand &args )
#define CON_COMMAND_MEMBER_F( _thisclass, name, _funcname, description, flags ) \
void _funcname( const CCommand &args ); \
friend class CCommandMemberInitializer_##_funcname; \
class CCommandMemberInitializer_##_funcname \
{ \
public: \
CCommandMemberInitializer_##_funcname() : m_ConCommandAccessor( NULL, name, &_thisclass::_funcname, description, flags ) \
{ \
m_ConCommandAccessor.SetOwner( GET_OUTER( _thisclass, m_##_funcname##_register ) ); \
} \
private: \
CConCommandMemberAccessor< _thisclass > m_ConCommandAccessor; \
}; \
\
CCommandMemberInitializer_##_funcname m_##_funcname##_register; \
#endif // CONVAR_H
-709
View File
@@ -1,709 +0,0 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $NoKeywords: $
//===========================================================================//
#ifndef CONVAR_H
#define CONVAR_H
#if _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#include "tier1/iconvar.h"
#include "tier1/utlvector.h"
#include "tier1/utlstring.h"
#include "icvar.h"
#ifdef _WIN32
#define FORCEINLINE_CVAR FORCEINLINE
#elif defined _LINUX || defined __APPLE__
#define FORCEINLINE_CVAR inline
#else
#error "implement me"
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class ConVar;
class CCommand;
class ConCommand;
class ConCommandBase;
struct characterset_t;
//-----------------------------------------------------------------------------
// Any executable that wants to use ConVars need to implement one of
// these to hook up access to console variables.
//-----------------------------------------------------------------------------
class IConCommandBaseAccessor
{
public:
// Flags is a combination of FCVAR flags in cvar.h.
// hOut is filled in with a handle to the variable.
virtual bool RegisterConCommandBase( ConCommandBase *pVar ) = 0;
};
//-----------------------------------------------------------------------------
// Helper method for console development
//-----------------------------------------------------------------------------
#if defined( _X360 ) && !defined( _RETAIL )
void ConVar_PublishToVXConsole();
#endif
//-----------------------------------------------------------------------------
// Called when a ConCommand needs to execute
//-----------------------------------------------------------------------------
typedef void ( *FnCommandCallbackV1_t )( void );
typedef void ( *FnCommandCallback_t )( const CCommand &command );
#define COMMAND_COMPLETION_MAXITEMS 64
#define COMMAND_COMPLETION_ITEM_LENGTH 64
//-----------------------------------------------------------------------------
// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
//-----------------------------------------------------------------------------
typedef int ( *FnCommandCompletionCallback )( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
//-----------------------------------------------------------------------------
// Interface version
//-----------------------------------------------------------------------------
class ICommandCallback
{
public:
virtual void CommandCallback( const CCommand &command ) = 0;
};
class ICommandCompletionCallback
{
public:
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) = 0;
};
//-----------------------------------------------------------------------------
// Purpose: The base console invoked command/cvar interface
//-----------------------------------------------------------------------------
class ConCommandBase
{
friend class CCvar;
friend class ConVar;
friend class ConCommand;
friend void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor );
friend void ConVar_PublishToVXConsole();
// FIXME: Remove when ConVar changes are done
friend class CDefaultCvar;
public:
ConCommandBase( void );
ConCommandBase( const char *pName, const char *pHelpString = 0,
int flags = 0 );
virtual ~ConCommandBase( void );
virtual bool IsCommand( void ) const;
// Check flag
virtual bool IsFlagSet( int flag ) const;
// Set flag
virtual void AddFlags( int flags );
// Return name of cvar
virtual const char *GetName( void ) const;
// Return help text for cvar
virtual const char *GetHelpText( void ) const;
// Deal with next pointer
const ConCommandBase *GetNext( void ) const;
ConCommandBase *GetNext( void );
void SetNext(ConCommandBase *pBase)
{
m_pNext = pBase;
}
virtual bool IsRegistered( void ) const;
// Returns the DLL identifier
virtual CVarDLLIdentifier_t GetDLLIdentifier() const;
protected:
virtual void Create( const char *pName, const char *pHelpString = 0,
int flags = 0 );
// Used internally by OneTimeInit to initialize/shutdown
virtual void Init();
void Shutdown();
// Internal copy routine ( uses new operator from correct module )
char *CopyString( const char *from );
private:
// Next ConVar in chain
// Prior to register, it points to the next convar in the DLL.
// Once registered, though, m_pNext is reset to point to the next
// convar in the global list
ConCommandBase *m_pNext;
// Has the cvar been added to the global list?
bool m_bRegistered;
// Static data
const char *m_pszName;
const char *m_pszHelpString;
// ConVar flags
int m_nFlags;
protected:
// ConVars add themselves to this list for the executable.
// Then ConVar_Register runs through all the console variables
// and registers them into a global list stored in vstdlib.dll
static ConCommandBase *s_pConCommandBases;
// ConVars in this executable use this 'global' to access values.
static IConCommandBaseAccessor *s_pAccessor;
public: // Hackalicous
inline int GetFlags() const
{
return m_nFlags;
}
inline void SetFlags(int flags)
{
m_nFlags = flags;
}
};
//-----------------------------------------------------------------------------
// Command tokenizer
//-----------------------------------------------------------------------------
class CCommand
{
public:
CCommand();
CCommand( int nArgC, const char **ppArgV );
bool Tokenize( const char *pCommand, characterset_t *pBreakSet = NULL );
void Reset();
int ArgC() const;
const char **ArgV() const;
const char *ArgS() const; // All args that occur after the 0th arg, in string form
const char *GetCommandString() const; // The entire command in string form, including the 0th arg
const char *operator[]( int nIndex ) const; // Gets at arguments
const char *Arg( int nIndex ) const; // Gets at arguments
// Helper functions to parse arguments to commands.
const char* FindArg( const char *pName ) const;
int FindArgInt( const char *pName, int nDefaultVal ) const;
static int MaxCommandLength();
static characterset_t* DefaultBreakSet();
private:
enum
{
COMMAND_MAX_ARGC = 64,
COMMAND_MAX_LENGTH = 512,
};
int m_nArgc;
int m_nArgv0Size;
char m_pArgSBuffer[ COMMAND_MAX_LENGTH ];
char m_pArgvBuffer[ COMMAND_MAX_LENGTH ];
const char* m_ppArgv[ COMMAND_MAX_ARGC ];
};
inline int CCommand::MaxCommandLength()
{
return COMMAND_MAX_LENGTH - 1;
}
inline int CCommand::ArgC() const
{
return m_nArgc;
}
inline const char **CCommand::ArgV() const
{
return m_nArgc ? (const char**)m_ppArgv : NULL;
}
inline const char *CCommand::ArgS() const
{
return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : "";
}
inline const char *CCommand::GetCommandString() const
{
return m_nArgc ? m_pArgSBuffer : "";
}
inline const char *CCommand::Arg( int nIndex ) const
{
// FIXME: Many command handlers appear to not be particularly careful
// about checking for valid argc range. For now, we're going to
// do the extra check and return an empty string if it's out of range
if ( nIndex < 0 || nIndex >= m_nArgc )
return "";
return m_ppArgv[nIndex];
}
inline const char *CCommand::operator[]( int nIndex ) const
{
return Arg( nIndex );
}
//-----------------------------------------------------------------------------
// Purpose: The console invoked command
//-----------------------------------------------------------------------------
class ConCommand : public ConCommandBase
{
friend class CCvar;
public:
typedef ConCommandBase BaseClass;
ConCommand( const char *pName, FnCommandCallbackV1_t callback,
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
ConCommand( const char *pName, FnCommandCallback_t callback,
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
ConCommand( const char *pName, ICommandCallback *pCallback,
const char *pHelpString = 0, int flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 );
virtual ~ConCommand( void );
virtual bool IsCommand( void ) const;
virtual int AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands );
virtual bool CanAutoComplete( void );
// Invoke the function
virtual void Dispatch( const CCommand &command );
private:
// NOTE: To maintain backward compat, we have to be very careful:
// All public virtual methods must appear in the same order always
// since engine code will be calling into this code, which *does not match*
// in the mod code; it's using slightly different, but compatible versions
// of this class. Also: Be very careful about adding new fields to this class.
// Those fields will not exist in the version of this class that is instanced
// in mod code.
// Call this function when executing the command
union
{
FnCommandCallbackV1_t m_fnCommandCallbackV1;
FnCommandCallback_t m_fnCommandCallback;
ICommandCallback *m_pCommandCallback;
};
union
{
FnCommandCompletionCallback m_fnCompletionCallback;
ICommandCompletionCallback *m_pCommandCompletionCallback;
};
bool m_bHasCompletionCallback : 1;
bool m_bUsingNewCommandCallback : 1;
bool m_bUsingCommandCallbackInterface : 1;
public: // Hackalicous
inline FnCommandCallback_t GetCallback() const
{
return m_fnCommandCallback;
}
};
//-----------------------------------------------------------------------------
// Purpose: A console variable
//-----------------------------------------------------------------------------
class ConVar : public ConCommandBase, public IConVar
{
friend class CCvar;
friend class ConVarRef;
public:
typedef ConCommandBase BaseClass;
ConVar( const char *pName, const char *pDefaultValue, int flags = 0);
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, FnChangeCallback_t callback );
ConVar( const char *pName, const char *pDefaultValue, int flags,
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax,
FnChangeCallback_t callback );
virtual ~ConVar( void );
virtual bool IsFlagSet( int flag ) const;
virtual const char* GetHelpText( void ) const;
virtual bool IsRegistered( void ) const;
virtual const char *GetName( void ) const;
virtual void AddFlags( int flags );
virtual bool IsCommand( void ) const;
// Install a change callback (there shouldn't already be one....)
void InstallChangeCallback( FnChangeCallback_t callback );
// Retrieve value
FORCEINLINE_CVAR float GetFloat( void ) const;
FORCEINLINE_CVAR int GetInt( void ) const;
FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); }
FORCEINLINE_CVAR char const *GetString( void ) const;
// Any function that allocates/frees memory needs to be virtual or else you'll have crashes
// from alloc/free across dll/exe boundaries.
// These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue).
virtual void SetValue( const char *value );
virtual void SetValue( float value );
virtual void SetValue( int value );
// Reset to default value
void Revert( void );
// True if it has a min/max setting
bool GetMin( float& minVal ) const;
bool GetMax( float& maxVal ) const;
const char *GetDefault( void ) const;
private:
// Called by CCvar when the value of a var is changing.
virtual void InternalSetValue(const char *value);
// For CVARs marked FCVAR_NEVER_AS_STRING
virtual void InternalSetFloatValue( float fNewValue );
virtual void InternalSetIntValue( int nValue );
virtual bool ClampValue( float& value );
virtual void ChangeStringValue( const char *tempVal, float flOldValue );
virtual void Create( const char *pName, const char *pDefaultValue, int flags = 0,
const char *pHelpString = 0, bool bMin = false, float fMin = 0.0,
bool bMax = false, float fMax = false, FnChangeCallback_t callback = 0 );
// Used internally by OneTimeInit to initialize.
virtual void Init();
private:
// This either points to "this" or it points to the original declaration of a ConVar.
// This allows ConVars to exist in separate modules, and they all use the first one to be declared.
// m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar).
ConVar *m_pParent;
// Static data
const char *m_pszDefaultValue;
// Value
// Dynamically allocated
char *m_pszString;
int m_StringLength;
// Values
float m_fValue;
int m_nValue;
// Min/Max values
bool m_bHasMin;
float m_fMinVal;
bool m_bHasMax;
float m_fMaxVal;
// Call this function when ConVar changes
FnChangeCallback_t m_fnChangeCallback;
public: // Hackalicous
inline FnChangeCallback_t GetCallback() const
{
return m_fnChangeCallback;
}
inline void SetMin(bool set, float min=0.0)
{
m_bHasMin = set;
m_fMinVal = min;
}
inline void SetMax(bool set, float max=0.0)
{
m_bHasMax = set;
m_fMaxVal = max;
}
};
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a float
// Output : float
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR float ConVar::GetFloat( void ) const
{
return m_pParent->m_fValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
// Output : int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
{
return m_pParent->m_nValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
// Output : const char *
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR const char *ConVar::GetString( void ) const
{
if ( m_nFlags & FCVAR_NEVER_AS_STRING )
return "FCVAR_NEVER_AS_STRING";
return ( m_pParent->m_pszString ) ? m_pParent->m_pszString : "";
}
//-----------------------------------------------------------------------------
// Used to read/write convars that already exist (replaces the FindVar method)
//-----------------------------------------------------------------------------
class ConVarRef
{
public:
ConVarRef( const char *pName );
ConVarRef( const char *pName, bool bIgnoreMissing );
ConVarRef( IConVar *pConVar );
void Init( const char *pName, bool bIgnoreMissing );
bool IsValid() const;
bool IsFlagSet( int nFlags ) const;
IConVar *GetLinkedConVar();
// Get/Set value
float GetFloat( void ) const;
int GetInt( void ) const;
bool GetBool() const { return !!GetInt(); }
const char *GetString( void ) const;
void SetValue( const char *pValue );
void SetValue( float flValue );
void SetValue( int nValue );
void SetValue( bool bValue );
const char *GetName() const;
const char *GetDefault() const;
private:
// High-speed method to read convar data
IConVar *m_pConVar;
ConVar *m_pConVarState;
};
//-----------------------------------------------------------------------------
// Did we find an existing convar of that name?
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR bool ConVarRef::IsFlagSet( int nFlags ) const
{
return ( m_pConVar->IsFlagSet( nFlags ) != 0 );
}
FORCEINLINE_CVAR IConVar *ConVarRef::GetLinkedConVar()
{
return m_pConVar;
}
FORCEINLINE_CVAR const char *ConVarRef::GetName() const
{
return m_pConVar->GetName();
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a float
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR float ConVarRef::GetFloat( void ) const
{
return m_pConVarState->m_fValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as an int
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const
{
return m_pConVarState->m_nValue;
}
//-----------------------------------------------------------------------------
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
//-----------------------------------------------------------------------------
FORCEINLINE_CVAR const char *ConVarRef::GetString( void ) const
{
Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) );
return m_pConVarState->m_pszString;
}
FORCEINLINE_CVAR void ConVarRef::SetValue( const char *pValue )
{
m_pConVar->SetValue( pValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( float flValue )
{
m_pConVar->SetValue( flValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( int nValue )
{
m_pConVar->SetValue( nValue );
}
FORCEINLINE_CVAR void ConVarRef::SetValue( bool bValue )
{
m_pConVar->SetValue( bValue ? 1 : 0 );
}
FORCEINLINE_CVAR const char *ConVarRef::GetDefault() const
{
return m_pConVarState->m_pszDefaultValue;
}
//-----------------------------------------------------------------------------
// Called by the framework to register ConCommands with the ICVar
//-----------------------------------------------------------------------------
void ConVar_Register( int nCVarFlag = 0, IConCommandBaseAccessor *pAccessor = NULL );
void ConVar_Unregister( );
//-----------------------------------------------------------------------------
// Utility methods
//-----------------------------------------------------------------------------
void ConVar_PrintFlags( const ConCommandBase *var );
void ConVar_PrintDescription( const ConCommandBase *pVar );
//-----------------------------------------------------------------------------
// Purpose: Utility class to quickly allow ConCommands to call member methods
//-----------------------------------------------------------------------------
#if defined _MSC_VER
#pragma warning (disable : 4355 )
#endif
template< class T >
class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, public ICommandCompletionCallback
{
typedef ConCommand BaseClass;
typedef void ( T::*FnMemberCommandCallback_t )( const CCommand &command );
typedef int ( T::*FnMemberCommandCompletionCallback_t )( const char *pPartial, CUtlVector< CUtlString > &commands );
public:
CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString = 0,
int flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) :
BaseClass( pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL )
{
m_pOwner = pOwner;
m_Func = callback;
m_CompletionFunc = completionFunc;
}
~CConCommandMemberAccessor()
{
Shutdown();
}
void SetOwner( T* pOwner )
{
m_pOwner = pOwner;
}
virtual void CommandCallback( const CCommand &command )
{
Assert( m_pOwner && m_Func );
(m_pOwner->*m_Func)( command );
}
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands )
{
Assert( m_pOwner && m_CompletionFunc );
return (m_pOwner->*m_CompletionFunc)( pPartial, commands );
}
private:
T* m_pOwner;
FnMemberCommandCallback_t m_Func;
FnMemberCommandCompletionCallback_t m_CompletionFunc;
};
#if defined _MSC_VER
#pragma warning ( default : 4355 )
#endif
//-----------------------------------------------------------------------------
// Purpose: Utility macros to quicky generate a simple console command
//-----------------------------------------------------------------------------
#define CON_COMMAND( name, description ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description ); \
static void name( const CCommand &args )
#define CON_COMMAND_F( name, description, flags ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description, flags ); \
static void name( const CCommand &args )
#define CON_COMMAND_F_COMPLETION( name, description, flags, completion ) \
static void name( const CCommand &args ); \
static ConCommand name##_command( #name, name, description, flags, completion ); \
static void name( const CCommand &args )
#define CON_COMMAND_EXTERN( name, _funcname, description ) \
void _funcname( const CCommand &args ); \
static ConCommand name##_command( #name, _funcname, description ); \
void _funcname( const CCommand &args )
#define CON_COMMAND_EXTERN_F( name, _funcname, description, flags ) \
void _funcname( const CCommand &args ); \
static ConCommand name##_command( #name, _funcname, description, flags ); \
void _funcname( const CCommand &args )
#define CON_COMMAND_MEMBER_F( _thisclass, name, _funcname, description, flags ) \
void _funcname( const CCommand &args ); \
friend class CCommandMemberInitializer_##_funcname; \
class CCommandMemberInitializer_##_funcname \
{ \
public: \
CCommandMemberInitializer_##_funcname() : m_ConCommandAccessor( NULL, name, &_thisclass::_funcname, description, flags ) \
{ \
m_ConCommandAccessor.SetOwner( GET_OUTER( _thisclass, m_##_funcname##_register ) ); \
} \
private: \
CConCommandMemberAccessor< _thisclass > m_ConCommandAccessor; \
}; \
\
CCommandMemberInitializer_##_funcname m_##_funcname##_register; \
#endif // CONVAR_H
File diff suppressed because it is too large Load Diff
+88 -85
View File
@@ -1,92 +1,95 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
import os
binary = SM.Library(builder, 'sourcemod.logic')
binary.compiler.cxxincludes += [
builder.sourcePath,
os.path.join(builder.sourcePath, 'core', 'logic'),
os.path.join(builder.sourcePath, 'public'),
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
os.path.join(builder.sourcePath, 'public', 'amtl', 'amtl'),
os.path.join(builder.sourcePath, 'public', 'amtl'),
os.path.join(SM.mms_root, 'core', 'sourcehook')
]
binary.compiler.defines += [
'SM_DEFAULT_THREADER',
'SM_LOGIC'
]
for cxx in builder.targets:
binary = SM.Library(builder, cxx, 'sourcemod.logic')
binary.compiler.cxxincludes += [
builder.sourcePath,
os.path.join(builder.sourcePath, 'core', 'logic'),
os.path.join(builder.sourcePath, 'public'),
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
os.path.join(builder.sourcePath, 'public', 'amtl', 'amtl'),
os.path.join(builder.sourcePath, 'public', 'amtl'),
os.path.join(SM.mms_root, 'core', 'sourcehook')
]
binary.compiler.defines += [
'SM_DEFAULT_THREADER',
'SM_LOGIC'
]
if builder.target.platform == 'linux':
binary.compiler.postlink += ['-lpthread', '-lrt']
elif builder.target.platform == 'mac':
binary.compiler.cflags += ['-Wno-deprecated-declarations']
binary.compiler.postlink += ['-framework', 'CoreServices']
if binary.compiler.target.platform == 'linux':
binary.compiler.postlink += ['-lpthread', '-lrt']
elif binary.compiler.target.platform == 'mac':
binary.compiler.cflags += ['-Wno-deprecated-declarations']
binary.compiler.postlink += ['-framework', 'CoreServices']
if binary.compiler.family == 'gcc' or binary.compiler.family == 'clang':
binary.compiler.cxxflags += ['-fno-rtti']
elif binary.compiler.family == 'msvc':
binary.compiler.cxxflags += ['/GR-']
if binary.compiler.family == 'gcc' or binary.compiler.family == 'clang':
binary.compiler.cxxflags += ['-fno-rtti']
elif binary.compiler.family == 'msvc':
binary.compiler.cxxflags += ['/GR-']
binary.sources += [
'common_logic.cpp',
'smn_adt_array.cpp',
'smn_sorting.cpp',
'smn_maplists.cpp',
'ADTFactory.cpp',
'smn_adt_stack.cpp',
'thread/ThreadWorker.cpp',
'thread/BaseWorker.cpp',
'ThreadSupport.cpp',
'smn_float.cpp',
'TextParsers.cpp',
'smn_textparse.cpp',
'smn_adt_trie.cpp',
'smn_functions.cpp',
'smn_timers.cpp',
'smn_players.cpp',
'MemoryUtils.cpp',
'smn_admin.cpp',
'smn_banning.cpp',
'smn_filesystem.cpp',
'stringutil.cpp',
'Translator.cpp',
'PhraseCollection.cpp',
'smn_lang.cpp',
'smn_string.cpp',
'smn_handles.cpp',
'smn_datapacks.cpp',
'smn_gameconfigs.cpp',
'smn_fakenatives.cpp',
'GameConfigs.cpp',
'sm_crc32.cpp',
'smn_profiler.cpp',
'ShareSys.cpp',
'PluginSys.cpp',
'HandleSys.cpp',
'NativeOwner.cpp',
'ExtensionSys.cpp',
'DebugReporter.cpp',
'Database.cpp',
'smn_database.cpp',
'ForwardSys.cpp',
'AdminCache.cpp',
'sm_trie.cpp',
'smn_console.cpp',
'ProfileTools.cpp',
'Logger.cpp',
'smn_core.cpp',
'smn_menus.cpp',
'sprintf.cpp',
'LibrarySys.cpp',
'RootConsoleMenu.cpp',
'CDataPack.cpp',
'frame_tasks.cpp',
'smn_halflife.cpp',
'FrameIterator.cpp',
]
if builder.target.platform == 'windows':
binary.sources += ['thread/WinThreads.cpp']
else:
binary.sources += ['thread/PosixThreads.cpp']
binary.sources += [
'common_logic.cpp',
'smn_adt_array.cpp',
'smn_sorting.cpp',
'smn_maplists.cpp',
'ADTFactory.cpp',
'smn_adt_stack.cpp',
'BaseWorker.cpp',
'ThreadSupport.cpp',
'smn_float.cpp',
'TextParsers.cpp',
'smn_textparse.cpp',
'smn_adt_trie.cpp',
'smn_functions.cpp',
'smn_timers.cpp',
'smn_players.cpp',
'MemoryUtils.cpp',
'smn_admin.cpp',
'smn_banning.cpp',
'smn_filesystem.cpp',
'stringutil.cpp',
'Translator.cpp',
'PhraseCollection.cpp',
'smn_lang.cpp',
'smn_string.cpp',
'smn_handles.cpp',
'smn_datapacks.cpp',
'smn_gameconfigs.cpp',
'smn_fakenatives.cpp',
'GameConfigs.cpp',
'sm_crc32.cpp',
'smn_profiler.cpp',
'ShareSys.cpp',
'PluginSys.cpp',
'HandleSys.cpp',
'NativeOwner.cpp',
'ExtensionSys.cpp',
'DebugReporter.cpp',
'Database.cpp',
'smn_database.cpp',
'ForwardSys.cpp',
'AdminCache.cpp',
'sm_trie.cpp',
'smn_console.cpp',
'ProfileTools.cpp',
'Logger.cpp',
'smn_core.cpp',
'smn_menus.cpp',
'sprintf.cpp',
'LibrarySys.cpp',
'RootConsoleMenu.cpp',
'CDataPack.cpp',
'frame_tasks.cpp',
'smn_halflife.cpp',
'FrameIterator.cpp',
'DatabaseConfBuilder.cpp',
'LumpManager.cpp',
'smn_entitylump.cpp',
'NativeInvoker.cpp',
]
SM.binaries += [builder.Add(binary)]
if binary.compiler.target.arch == 'x86_64':
binary.sources += ['PseudoAddrManager.cpp']
SM.binaries += [builder.Add(binary)]
+9 -9
View File
@@ -1090,14 +1090,14 @@ bool AdminCache::GetUnifiedSteamIdentity(const char *ident, char *out, size_t ma
else if (len >= 11 && !strncmp(ident, "STEAM_", 6) && ident[8] != '_')
{
// non-bot/lan Steam2 Id, strip off the STEAM_* part
snprintf(out, maxlen, "%s", &ident[8]);
ke::SafeStrcpy(out, maxlen, &ident[8]);
return true;
}
else if (len >= 7 && !strncmp(ident, "[U:", 3) && ident[len-1] == ']')
{
// Steam3 Id, replicate the Steam2 Post-"STEAM_" part
uint32_t accountId = strtoul(&ident[5], nullptr, 10);
snprintf(out, maxlen, "%u:%u", accountId & 1, accountId >> 1);
ke::SafeSprintf(out, maxlen, "%u:%u", accountId & 1, accountId >> 1);
return true;
}
else
@@ -1124,7 +1124,7 @@ bool AdminCache::GetUnifiedSteamIdentity(const char *ident, char *out, size_t ma
&& accountType == k_EAccountTypeIndividual && accountInstance <= k_unSteamUserWebInstance
)
{
snprintf(out, maxlen, "%u:%u", accountId & 1, accountId >> 1);
ke::SafeSprintf(out, maxlen, "%u:%u", accountId & 1, accountId >> 1);
return true;
}
}
@@ -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);
+4
View File
@@ -82,6 +82,10 @@ struct AuthMethod
{
return strcmp(name, method->name.c_str()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
struct UserAuth
@@ -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())
@@ -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();
+205 -258
View File
@@ -31,327 +31,274 @@
#include <stdlib.h>
#include <string.h>
#include <memory>
#include "CDataPack.h"
#include <amtl/am-autoptr.h>
#include <amtl/am-vector.h>
using namespace ke;
#define DATAPACK_INITIAL_SIZE 64
CDataPack::CDataPack()
{
m_pBase = (char *)malloc(DATAPACK_INITIAL_SIZE);
m_capacity = DATAPACK_INITIAL_SIZE;
Initialize();
}
CDataPack::~CDataPack()
{
free(m_pBase);
}
static Vector<AutoPtr<CDataPack>> sDataPackCache;
IDataPack * CDataPack::New()
{
if (sDataPackCache.empty())
return new CDataPack();
CDataPack *pack = sDataPackCache.back().take();
sDataPackCache.pop();
pack->Initialize();
return pack;
}
void
CDataPack::Free(IDataPack *pack)
{
sDataPackCache.append(static_cast<CDataPack *>(pack));
Initialize();
}
void CDataPack::Initialize()
{
m_curptr = m_pBase;
m_size = 0;
}
void CDataPack::CheckSize(size_t typesize)
{
if (m_curptr - m_pBase + typesize <= m_capacity)
{
return;
}
size_t pos = m_curptr - m_pBase;
position = 0;
do
{
m_capacity *= 2;
} while (pos + typesize > m_capacity);
m_pBase = (char *)realloc(m_pBase, m_capacity);
m_curptr = m_pBase + pos;
} while (this->RemoveItem());
elements.clear();
}
void CDataPack::ResetSize()
{
m_size = 0;
Initialize();
}
size_t CDataPack::CreateMemory(size_t size, void **addr)
{
CheckSize(sizeof(char) + sizeof(size_t) + size);
size_t pos = m_curptr - m_pBase;
InternalPack val;
val.type = CDataPackType::Raw;
val.pData.vval = new uint8_t[size + sizeof(size)];
reinterpret_cast<size_t *>(val.pData.vval)[0] = size;
elements.emplace(elements.begin() + position, val);
*(char *)m_curptr = Raw;
m_curptr += sizeof(char);
*(size_t *)m_curptr = size;
m_curptr += sizeof(size_t);
if (addr)
{
*addr = m_curptr;
}
m_curptr += size;
m_size += sizeof(char) + sizeof(size_t) + size;
return pos;
return position++;
}
void CDataPack::PackCell(cell_t cell)
{
CheckSize(sizeof(char) + sizeof(size_t) + sizeof(cell_t));
*(char *)m_curptr = Cell;
m_curptr += sizeof(char);
*(size_t *)m_curptr = sizeof(cell_t);
m_curptr += sizeof(size_t);
*(cell_t *)m_curptr = cell;
m_curptr += sizeof(cell_t);
m_size += sizeof(char) + sizeof(size_t) + sizeof(cell_t);
InternalPack val;
val.type = CDataPackType::Cell;
val.pData.cval = cell;
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackFloat(float val)
void CDataPack::PackFunction(cell_t function)
{
CheckSize(sizeof(char) + sizeof(size_t) + sizeof(float));
InternalPack val;
val.type = CDataPackType::Function;
val.pData.cval = function;
elements.emplace(elements.begin() + position, val);
position++;
}
*(char *)m_curptr = Float;
m_curptr += sizeof(char);
*(size_t *)m_curptr = sizeof(float);
m_curptr += sizeof(size_t);
*(float *)m_curptr = val;
m_curptr += sizeof(float);
m_size += sizeof(char) + sizeof(size_t) + sizeof(float);
void CDataPack::PackFloat(float floatval)
{
InternalPack val;
val.type = CDataPackType::Float;
val.pData.fval = floatval;
elements.emplace(elements.begin() + position, val);
position++;
}
void CDataPack::PackString(const char *string)
{
size_t len = strlen(string);
size_t maxsize = sizeof(char) + sizeof(size_t) + len + 1;
CheckSize(maxsize);
InternalPack val;
val.type = CDataPackType::String;
std::string *sval = new std::string(string);
val.pData.sval = sval;
elements.emplace(elements.begin() + position, val);
position++;
}
*(char *)m_curptr = String;
m_curptr += sizeof(char);
void CDataPack::PackCellArray(cell_t const *vals, cell_t count)
{
InternalPack val;
val.type = CDataPackType::CellArray;
// Pack the string length first for buffer overrun checking.
*(size_t *)m_curptr = len;
m_curptr += sizeof(size_t);
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++;
}
// Now pack the string.
memcpy(m_curptr, string, len);
m_curptr[len] = '\0';
m_curptr += len + 1;
void CDataPack::PackFloatArray(cell_t const *vals, cell_t count)
{
InternalPack val;
val.type = CDataPackType::FloatArray;
m_size += maxsize;
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
{
m_curptr = m_pBase;
position = 0;
}
size_t CDataPack::GetPosition() const
{
return static_cast<size_t>(m_curptr - m_pBase);
return position;
}
bool CDataPack::SetPosition(size_t pos) const
{
if (pos > m_size)
{
if (pos > elements.size())
return false;
}
m_curptr = m_pBase + pos;
position = pos;
return true;
}
cell_t CDataPack::ReadCell() const
{
if (!IsReadable(sizeof(char) + sizeof(size_t) + sizeof(cell_t)))
{
if (!IsReadable() || elements[position].type != CDataPackType::Cell)
return 0;
}
if (*reinterpret_cast<char *>(m_curptr) != Cell)
{
return 0;
}
m_curptr += sizeof(char);
if (*reinterpret_cast<size_t *>(m_curptr) != sizeof(cell_t))
{
return 0;
}
m_curptr += sizeof(size_t);
cell_t val = *reinterpret_cast<cell_t *>(m_curptr);
m_curptr += sizeof(cell_t);
return val;
}
float CDataPack::ReadFloat() const
{
if (!IsReadable(sizeof(char) + sizeof(size_t) + sizeof(float)))
{
return 0;
}
if (*reinterpret_cast<char *>(m_curptr) != Float)
{
return 0;
}
m_curptr += sizeof(char);
if (*reinterpret_cast<size_t *>(m_curptr) != sizeof(float))
{
return 0;
}
m_curptr += sizeof(size_t);
float val = *reinterpret_cast<float *>(m_curptr);
m_curptr += sizeof(float);
return val;
}
bool CDataPack::IsReadable(size_t bytes) const
{
return (bytes + (m_curptr - m_pBase) > m_size) ? false : true;
}
const char *CDataPack::ReadString(size_t *len) const
{
if (!IsReadable(sizeof(char) + sizeof(size_t)))
{
return NULL;
}
if (*reinterpret_cast<char *>(m_curptr) != String)
{
return NULL;
}
m_curptr += sizeof(char);
size_t real_len = *(size_t *)m_curptr;
m_curptr += sizeof(size_t);
char *str = (char *)m_curptr;
if ((strlen(str) != real_len) || !(IsReadable(real_len+1)))
{
return NULL;
}
if (len)
{
*len = real_len;
}
m_curptr += real_len + 1;
return str;
}
void *CDataPack::GetMemory() const
{
return m_curptr;
}
void *CDataPack::ReadMemory(size_t *size) const
{
if (!IsReadable(sizeof(size_t)))
{
return NULL;
}
if (*reinterpret_cast<char *>(m_curptr) != Raw)
{
return NULL;
}
m_curptr += sizeof(char);
size_t bytecount = *(size_t *)m_curptr;
m_curptr += sizeof(size_t);
if (!IsReadable(bytecount))
{
return NULL;
}
void *ptr = m_curptr;
if (size)
{
*size = bytecount;
}
m_curptr += bytecount;
return ptr;
}
void CDataPack::PackFunction(cell_t function)
{
CheckSize(sizeof(char) + sizeof(size_t) + sizeof(cell_t));
*(char *)m_curptr = Function;
m_curptr += sizeof(char);
*(size_t *)m_curptr = sizeof(cell_t);
m_curptr += sizeof(size_t);
*(cell_t *)m_curptr = function;
m_curptr += sizeof(cell_t);
m_size += sizeof(char) + sizeof(size_t) + sizeof(cell_t);
return elements[position++].pData.cval;
}
cell_t CDataPack::ReadFunction() const
{
if (!IsReadable(sizeof(char) + sizeof(size_t) + sizeof(cell_t)))
{
if (!IsReadable() || elements[position].type != CDataPackType::Function)
return 0;
}
if (*reinterpret_cast<char *>(m_curptr) != Function)
{
return 0;
}
m_curptr += sizeof(char);
if (*reinterpret_cast<size_t *>(m_curptr) != sizeof(cell_t))
{
return 0;
}
m_curptr += sizeof(size_t);
cell_t val = *reinterpret_cast<cell_t *>(m_curptr);
m_curptr += sizeof(cell_t);
return val;
return elements[position++].pData.cval;
}
float CDataPack::ReadFloat() const
{
if (!IsReadable() || elements[position].type != CDataPackType::Float)
return 0;
return elements[position++].pData.fval;
}
bool CDataPack::IsReadable(size_t bytes) const
{
return (position < elements.size());
}
const char *CDataPack::ReadString(size_t *len) const
{
if (!IsReadable() || elements[position].type != CDataPackType::String)
{
if (len)
*len = 0;
return nullptr;
}
const std::string &val = *elements[position++].pData.sval;
if (len)
*len = val.size();
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
{
void *ptr = nullptr;
if (!IsReadable() || elements[position].type != CDataPackType::Raw)
return ptr;
size_t *val = reinterpret_cast<size_t *>(elements[position].pData.vval);
ptr = &(val[1]);
++position;
if (size)
*size = val[0]; /* Egor!!!! */
return ptr;
}
bool CDataPack::RemoveItem(size_t pos)
{
if (!elements.size())
{
return false;
}
if (pos == static_cast<size_t>(-1))
{
pos = position;
}
if (pos >= elements.size())
{
return false;
}
if (pos < position) // we're deleting under us, step back
{
--position;
}
switch (elements[pos].type)
{
case CDataPackType::Raw:
{
delete [] elements[pos].pData.vval;
break;
}
case CDataPackType::String:
{
delete elements[pos].pData.sval;
break;
}
case CDataPackType::CellArray:
case CDataPackType::FloatArray:
{
delete [] elements[pos].pData.aval;
break;
}
}
elements.erase(elements.begin() + pos);
return true;
}
+164 -23
View File
@@ -32,54 +32,195 @@
#ifndef _INCLUDE_SOURCEMOD_CDATAPACK_H_
#define _INCLUDE_SOURCEMOD_CDATAPACK_H_
#include <IDataPack.h>
#include <ISourceMod.h>
#include <amtl/am-vector.h>
#include <amtl/am-string.h>
using namespace SourceMod;
class CDataPack : public IDataPack
enum CDataPackType {
Raw,
Cell,
Float,
String,
Function,
CellArray,
FloatArray,
};
class CDataPack
{
public:
CDataPack();
~CDataPack();
static IDataPack *New();
static void Free(IDataPack *pack);
public: //IDataReader
public: // Originally IDataReader
/**
* @brief Resets the position in the data stream to the beginning.
*/
void Reset() const;
/**
* @brief Retrieves the current stream position.
*
* @return Index into the stream.
*/
size_t GetPosition() const;
/**
* @brief Sets the current stream position.
*
* @param pos Index to set the stream at.
* @return True if succeeded, false if out of bounds.
*/
bool SetPosition(size_t pos) const;
/**
* @brief Reads one cell from the data stream.
*
* @return A cell read from the current position.
*/
cell_t ReadCell() const;
/**
* @brief Reads one float from the data stream.
*
* @return A float read from the current position.
*/
float ReadFloat() const;
bool IsReadable(size_t bytes) 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.
*
* @param bytes Number of bytes to simulate reading.
* @return True if can be read, false otherwise.
*/
bool IsReadable(size_t bytes = 0) const;
/**
* @brief Reads a string from the data stream.
*
* @param len Optional pointer to store the string length.
* @return Pointer to the string, or NULL if out of bounds.
*/
const char *ReadString(size_t *len) const;
void *GetMemory() const;
/**
* @brief Reads the current position as a generic data type.
*
* @param size Optional pointer to store the size of the data type.
* @return Pointer to the data, or NULL if out of bounds.
*/
void *ReadMemory(size_t *size) const;
/**
* @brief Reads a function pointer from the data stream.
*
* @return A function pointer read from the current position.
*/
cell_t ReadFunction() const;
public: //IDataPack
public: // Originally IDataPack
/**
* @brief Resets the used size of the stream back to zero.
*/
void ResetSize();
/**
* @brief Packs one cell into the data stream.
*
* @param cell Cell value to write.
*/
void PackCell(cell_t cell);
/**
* @brief Packs one float into the data stream.
*
* @param val Float value to write.
*/
void PackFloat(float val);
/**
* @brief Packs one string into the data stream.
* The length is recorded as well for buffer overrun protection.
*
* @param string String to write.
*/
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.
*
* Note that the pointer it returns can be invalidated on further
* writing, since the stream size may grow. You may need to double back
* and fetch the pointer again.
*
* @param size Size of the memory to create in the stream.
* @param addr Optional pointer to store the relocated memory address.
* @return Current position of the stream beforehand.
*/
size_t CreateMemory(size_t size, void **addr);
/**
* @brief Packs one function pointer into the data stream.
*
* @param function The function pointer to write.
*/
void PackFunction(cell_t function);
public:
void Initialize();
inline size_t GetCapacity() const { return m_capacity; }
private:
void CheckSize(size_t sizetype);
private:
char *m_pBase;
mutable char *m_curptr;
size_t m_capacity;
size_t m_size;
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);
enum DataPackType {
Raw,
Cell,
Float,
String,
Function
};
private:
typedef union {
cell_t cval;
float fval;
uint8_t *vval;
std::string *sval;
cell_t *aval;
} InternalPackValue;
typedef struct {
InternalPackValue pData;
CDataPackType type;
} InternalPack;
std::vector<InternalPack> elements;
mutable size_t position;
};
#endif //_INCLUDE_SOURCEMOD_CDATAPACK_H_
+20 -9
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,24 +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;
if (!ke::IsUintPtrMultiplySafe(newAllocSize, 2))
{
return false;
}
newAllocSize *= 2;
}
/* finally, allocate the new block */
if (m_Data)
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_Data = (cell_t *)realloc(m_Data, sizeof(cell_t) * m_BlockSize * m_AllocSize);
} else {
m_Data = (cell_t *)malloc(sizeof(cell_t) * m_BlockSize * m_AllocSize);
m_AllocSize = newAllocSize;
m_Data = data;
}
return (m_Data != NULL);
return (data != nullptr);
}
private:
cell_t *m_Data;
+87 -250
View File
@@ -34,21 +34,23 @@
#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),
m_ParseLevel(0),
m_ParseState(0),
m_pDefault(NULL)
{
}
@@ -65,38 +67,29 @@ void DBManager::OnSourceModAllInitialized()
g_HandleSys.InitAccessDefaults(NULL, &sec);
sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_ShareSys.AddInterface(NULL, this);
g_pSM->BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
g_PluginSys.AddPluginsListener(this);
m_Builder.SetPath(m_Filename);
g_PluginSys.AddPluginsListener(this);
g_pSM->AddGameFrameHook(&FrameHook);
auto sm_reload_databases = [this] (int client, const ICommandArgs *args) -> bool {
m_Builder.StartParse();
return true;
};
bridge->DefineCommand("sm_reload_databases", "Reparse database configurations file", sm_reload_databases);
}
void DBManager::OnSourceModLevelChange(const char *mapName)
{
SMCError err;
SMCStates states = {0, 0};
/* We lock and don't give up the lock until we're done.
* This way the thread's search won't be searching through a
* potentially empty/corrupt list, which would be very bad.
*/
ke::AutoLock lock(&m_ConfigLock);
if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
{
logger->LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
if (err != SMCError_Custom)
{
const char *txt = textparsers->GetSMCErrorString(err);
logger->LogError("[SM] Line %d: %s", states.line, txt);
}
}
m_Builder.StartParse();
}
void DBManager::OnSourceModShutdown()
@@ -106,7 +99,6 @@ void DBManager::OnSourceModShutdown()
g_PluginSys.RemovePluginsListener(this);
g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
ClearConfigs();
}
unsigned int DBManager::GetInterfaceVersion()
@@ -134,136 +126,10 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
}
}
void DBManager::ReadSMC_ParseStart()
{
ClearConfigs();
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_DefDriver.clear();
}
void DBManager::ClearConfigs()
{
List<ConfDbInfo *>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
delete (*iter);
m_confs.clear();
}
ConfDbInfo s_CurInfo;
SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (m_ParseLevel)
{
m_ParseLevel++;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_NONE)
{
if (strcmp(name, "Databases") == 0)
{
m_ParseState = DBPARSE_LEVEL_MAIN;
} else {
m_ParseLevel++;
}
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
s_CurInfo = ConfDbInfo();
s_CurInfo.name = name;
m_ParseState = DBPARSE_LEVEL_DATABASE;
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
m_ParseLevel++;
}
return SMCResult_Continue;
}
SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (m_ParseLevel)
{
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_MAIN)
{
if (strcmp(key, "driver_default") == 0)
{
m_DefDriver.assign(value);
}
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
if (strcmp(key, "driver") == 0)
{
if (strcmp(value, "default") != 0)
{
s_CurInfo.driver = value;
}
} else if (strcmp(key, "database") == 0) {
s_CurInfo.database = value;
} else if (strcmp(key, "host") == 0) {
s_CurInfo.host = value;
} else if (strcmp(key, "user") == 0) {
s_CurInfo.user = value;
} else if (strcmp(key, "pass") == 0) {
s_CurInfo.pass = value;
} else if (strcmp(key, "timeout") == 0) {
s_CurInfo.info.maxTimeout = atoi(value);
} else if (strcmp(key, "port") == 0) {
s_CurInfo.info.port = atoi(value);
}
}
return SMCResult_Continue;
}
SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_ParseLevel)
{
m_ParseLevel--;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
{
ConfDbInfo *cdb = new ConfDbInfo();
cdb->name = s_CurInfo.name;
cdb->driver = s_CurInfo.driver;
cdb->host = s_CurInfo.host;
cdb->user = s_CurInfo.user;
cdb->pass = s_CurInfo.pass;
cdb->database = s_CurInfo.database;
cdb->realDriver = s_CurInfo.realDriver;
cdb->info.maxTimeout = s_CurInfo.info.maxTimeout;
cdb->info.port = s_CurInfo.info.port;
cdb->info.driver = cdb->driver.c_str();
cdb->info.database = cdb->database.c_str();
cdb->info.host = cdb->host.c_str();
cdb->info.user = cdb->user.c_str();
cdb->info.pass = cdb->pass.c_str();
/* Save it.. */
m_confs.push_back(cdb);
/* Go up one level */
m_ParseState = DBPARSE_LEVEL_MAIN;
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseState = DBPARSE_LEVEL_NONE;
return SMCResult_Halt;
}
return SMCResult_Continue;
}
void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
{
}
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
{
ConfDbInfo *pInfo = GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> pInfo = list.GetDatabaseConf(name);
if (!pInfo)
{
@@ -282,11 +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')
{
if (!m_pDefault && m_DefDriver.size() > 0)
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
}
dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
dname = defaultDriver.length() ? defaultDriver.c_str() : "default";
pInfo->realDriver = m_pDefault;
} else {
pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
@@ -310,7 +177,6 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
*pdb = NULL;
g_pSM->Format(error, maxlength, "Driver \"%s\" not found", dname);
return false;
}
@@ -323,7 +189,7 @@ void DBManager::AddDriver(IDBDriver *pDriver)
*/
KillWorkerThread();
m_drivers.push_back(pDriver);
m_drivers.push_back(pDriver);
}
void DBManager::RemoveDriver(IDBDriver *pDriver)
@@ -343,14 +209,11 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
}
}
/* Make sure NOTHING references this! */
List<ConfDbInfo *>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{
ConfDbInfo &db = *(*iter);
if (db.realDriver == pDriver)
ConfDbInfoList &list = m_Builder.GetConfigList();
for (auto conf : list) {
if (conf->realDriver == pDriver)
{
db.realDriver = NULL;
conf->realDriver = NULL;
}
}
@@ -389,9 +252,11 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
IDBDriver *DBManager::GetDefaultDriver()
{
if (!m_pDefault && m_DefDriver.size() > 0)
ConfDbInfoList &list = m_Builder.GetConfigList();
std::string defaultDriver = list.GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
m_pDefault = FindOrLoadDriver(defaultDriver.c_str());
}
return m_pDefault;
@@ -454,29 +319,25 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
{
ConfDbInfo *info = GetDatabaseConf(name);
ConfDbInfoList &list = m_Builder.GetConfigList();
ke::RefPtr<ConfDbInfo> info = list.GetDatabaseConf(name);
if (!info)
{
return NULL;
// couldn't find requested conf, return default if exists
info = list.GetDefaultConfiguration();
if (!info)
{
return NULL;
}
}
return &info->info;
}
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
ke::RefPtr<ConfDbInfo> DBManager::GetDatabaseConf(const char *name)
{
List<ConfDbInfo *>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{
ConfDbInfo &conf = *(*iter);
if (conf.name == name)
{
return &conf;
}
}
return NULL;
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDatabaseConf(name);
}
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
@@ -515,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;
}
}
@@ -537,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;
@@ -588,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
@@ -596,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();
}
}
@@ -650,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();
}
@@ -729,23 +576,13 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
}
}
void DBManager::LockConfig()
std::string DBManager::GetDefaultDriverName()
{
m_ConfigLock.Lock();
}
void DBManager::UnlockConfig()
{
m_ConfigLock.Unlock();
}
const char *DBManager::GetDefaultDriverName()
{
return m_DefDriver.c_str();
ConfDbInfoList &list = m_Builder.GetConfigList();
return list.GetDefaultDriver();
}
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
{
g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
}
+14 -37
View File
@@ -32,39 +32,27 @@
#ifndef _INCLUDE_DATABASE_MANAGER_H_
#define _INCLUDE_DATABASE_MANAGER_H_
#include <IDBDriver.h>
#include "common_logic.h"
#include <sh_vector.h>
#include <sh_string.h>
#include <am-string.h>
#include <sh_list.h>
#include <ITextParsers.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"
using namespace SourceHook;
struct ConfDbInfo
{
ConfDbInfo() : realDriver(NULL)
{
}
String name;
String driver;
String host;
String user;
String pass;
String database;
IDBDriver *realDriver;
DatabaseInfo info;
};
class DBManager :
public IDBManager,
public SMGlobalClass,
public IHandleTypeDispatch,
public ITextListener_SMC,
public IPluginsListener
{
public:
@@ -83,6 +71,7 @@ public: //IDBManager
void AddDriver(IDBDriver *pDrivera);
void RemoveDriver(IDBDriver *pDriver);
const DatabaseInfo *FindDatabaseConf(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);
@@ -90,25 +79,16 @@ public: //IDBManager
HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr);
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
void AddDependency(IExtension *myself, IDBDriver *driver);
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseEnd(bool halted, bool failed);
public: //ke::IRunnable
void Run();
void ThreadMain();
public: //IPluginsListener
void OnPluginWillUnload(IPlugin *plugin);
public:
ConfDbInfo *GetDatabaseConf(const char *name);
IDBDriver *FindOrLoadDriver(const char *name);
IDBDriver *GetDefaultDriver();
const char *GetDefaultDriverName();
std::string GetDefaultDriverName();
bool AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio);
void LockConfig();
void UnlockConfig();
void RunFrame();
inline HandleType_t GetDatabaseType()
{
@@ -124,19 +104,16 @@ 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_ConfigLock;
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;
List<ConfDbInfo *> m_confs;
DatabaseConfBuilder m_Builder;
HandleType_t m_DriverType;
HandleType_t m_DatabaseType;
String m_DefDriver;
char m_Filename[PLATFORM_MAX_PATH];
unsigned int m_ParseLevel;
unsigned int m_ParseState;
IDBDriver *m_pDefault;
};
+182
View File
@@ -0,0 +1,182 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2018 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 "DatabaseConfBuilder.h"
#include <bridge/include/ILogger.h>
#define DBPARSE_LEVEL_NONE 0
#define DBPARSE_LEVEL_MAIN 1
#define DBPARSE_LEVEL_DATABASE 2
DatabaseConfBuilder::DatabaseConfBuilder()
: m_ParseList(),
m_InfoList()
{
}
void DatabaseConfBuilder::SetPath(char *str)
{
m_Filename = str;
}
DatabaseConfBuilder::~DatabaseConfBuilder()
{
}
ConfDbInfoList &DatabaseConfBuilder::GetConfigList()
{
return m_InfoList;
}
void DatabaseConfBuilder::StartParse()
{
SMCError err;
SMCStates states = {0, 0};
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.c_str());
if (err != SMCError_Custom)
{
const char *txt = textparsers->GetSMCErrorString(err);
logger->LogError("[SM] Line %d: %s", states.line, txt);
}
}
}
void DatabaseConfBuilder::ReadSMC_ParseStart()
{
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_ParseList.clear();
}
SMCResult DatabaseConfBuilder::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (m_ParseLevel)
{
m_ParseLevel++;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_NONE)
{
if (strcmp(name, "Databases") == 0)
{
m_ParseState = DBPARSE_LEVEL_MAIN;
} else {
m_ParseLevel++;
}
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseCurrent = new ConfDbInfo();
m_ParseCurrent->name = name;
m_ParseState = DBPARSE_LEVEL_DATABASE;
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
m_ParseLevel++;
}
return SMCResult_Continue;
}
SMCResult DatabaseConfBuilder::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (m_ParseLevel)
{
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_MAIN)
{
if (strcmp(key, "driver_default") == 0)
{
m_ParseList.SetDefaultDriver(value);
}
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
if (strcmp(key, "driver") == 0)
{
if (strcmp(value, "default") != 0)
{
m_ParseCurrent->driver = value;
}
} else if (strcmp(key, "database") == 0) {
m_ParseCurrent->database = value;
} else if (strcmp(key, "host") == 0) {
m_ParseCurrent->host = value;
} else if (strcmp(key, "user") == 0) {
m_ParseCurrent->user = value;
} else if (strcmp(key, "pass") == 0) {
m_ParseCurrent->pass = value;
} else if (strcmp(key, "timeout") == 0) {
m_ParseCurrent->info.maxTimeout = atoi(value);
} else if (strcmp(key, "port") == 0) {
m_ParseCurrent->info.port = atoi(value);
}
}
return SMCResult_Continue;
}
SMCResult DatabaseConfBuilder::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_ParseLevel)
{
m_ParseLevel--;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
{
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.push_back(m_ParseCurrent);
m_ParseCurrent = nullptr;
/* Go up one level */
m_ParseState = DBPARSE_LEVEL_MAIN;
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseState = DBPARSE_LEVEL_NONE;
return SMCResult_Halt;
}
return SMCResult_Continue;
}
void DatabaseConfBuilder::ReadSMC_ParseEnd(bool halted, bool failed)
{
m_InfoList.clear();
m_InfoList = m_ParseList;
m_ParseList.clear();
}
+125
View File
@@ -0,0 +1,125 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2018 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_DATABASE_CONF_BUILDER_H_
#define _INCLUDE_DATABASE_CONF_BUILDER_H_
#include <IDBDriver.h>
#include <ITextParsers.h>
#include "common_logic.h"
#include <am-vector.h>
#include <am-string.h>
#include <am-refcounting.h>
#include <am-refcounting-threadsafe.h>
class ConfDbInfo : public ke::RefcountedThreadsafe<ConfDbInfo>
{
public:
ConfDbInfo() : realDriver(NULL)
{
}
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 std::vector<ke::RefPtr<ConfDbInfo>>
{
/* Allow internal usage of ConfDbInfoList */
friend class DBManager;
friend class DatabaseConfBuilder;
private:
std::string& GetDefaultDriver() {
return m_DefDriver;
}
ke::RefPtr<ConfDbInfo> GetDatabaseConf(const char *name) {
for (size_t i = 0; i < this->size(); 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.c_str(), "default") == 0)
{
m_DefaultConfig = current;
}
if (strcmp(current->name.c_str(), name) == 0)
{
return current;
}
}
return nullptr;
}
ke::RefPtr<ConfDbInfo> GetDefaultConfiguration() {
return m_DefaultConfig;
}
void SetDefaultDriver(const char *input) {
m_DefDriver = std::string(input);
}
private:
ke::RefPtr<ConfDbInfo> m_DefaultConfig;
std::string m_DefDriver;
};
class DatabaseConfBuilder : public ITextListener_SMC
{
public:
DatabaseConfBuilder();
~DatabaseConfBuilder();
void StartParse();
void SetPath(char* path);
ConfDbInfoList &GetConfigList();
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
void ReadSMC_ParseEnd(bool halted, bool failed);
private:
unsigned int m_ParseLevel;
unsigned int m_ParseState;
ConfDbInfo *m_ParseCurrent;
ConfDbInfoList m_ParseList;
private:
std::string m_Filename;
ConfDbInfoList m_InfoList;
};
#endif //_INCLUDE_DATABASE_CONF_BUILDER_H_
+35 -17
View File
@@ -29,11 +29,11 @@
* Version: $Id$
*/
#include <ISourceMod.h>
#include <IPluginSys.h>
#include <stdarg.h>
#include "DebugReporter.h"
#include "Logger.h"
#include <am-string.h>
DebugReport g_DbgReporter;
@@ -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,35 +194,53 @@ void DebugReport::ReportError(const IErrorReport &report, IFrameIterator &iter)
g_Logger.LogError("[SM] Blaming: %s", blame);
}
if (!iter.Done())
std::vector<std::string> arr = GetStackTrace(&iter);
for (size_t i = 0; i < arr.size(); i++)
{
g_Logger.LogError("[SM] Call stack trace:");
g_Logger.LogError("%s", arr[i].c_str());
}
}
for (int index = 0; !iter.Done(); iter.Next(), index++)
std::vector<std::string> DebugReport::GetStackTrace(IFrameIterator *iter)
{
char temp[3072];
std::vector<std::string> trace;
iter->Reset();
if (!iter->Done())
{
trace.push_back("[SM] Call stack trace:");
for (int index = 0; !iter->Done(); iter->Next(), index++)
{
const char *fn = iter.FunctionName();
const char *fn = iter->FunctionName();
if (!fn)
{
fn = "<unknown function>";
}
if (iter.IsNativeFrame())
if (iter->IsNativeFrame())
{
g_Logger.LogError("[SM] [%d] %s", index, fn);
g_pSM->Format(temp, sizeof(temp), "[SM] [%d] %s", index, fn);
trace.push_back(temp);
continue;
}
if (iter.IsScriptedFrame())
if (iter->IsScriptedFrame())
{
const char *file = iter.FilePath();
const char *file = iter->FilePath();
if (!file)
{
file = "<unknown>";
}
g_Logger.LogError("[SM] [%d] Line %d, %s::%s",
g_pSM->Format(temp, sizeof(temp), "[SM] [%d] Line %d, %s::%s",
index,
iter.LineNumber(),
iter->LineNumber(),
file,
fn);
trace.push_back(temp);
}
}
}
return trace;
}
+5
View File
@@ -34,6 +34,8 @@
#include "sp_vm_api.h"
#include "common_logic.h"
#include <am-vector.h>
#include <am-string.h>
class DebugReport :
public SMGlobalClass,
@@ -45,9 +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, ...);
std::vector<std::string> GetStackTrace(IFrameIterator *iter);
private:
int _GetPluginIndex(IPluginContext *ctx);
};
+90 -162
View File
@@ -30,6 +30,9 @@
*/
#include <stdlib.h>
#include <memory>
#include "ExtensionSys.h"
#include <ILibrarySys.h>
#include <ISourceMod.h>
@@ -72,74 +75,69 @@ CLocalExtension::CLocalExtension(const char *filename, bool bRequired)
/* Special case for new bintools binary */
if (strcmp(filename, "bintools.ext") == 0)
{
goto normal;
}
/* Zeroth, see if there is an engine specific build in the new place. */
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
if (libsys->IsPathFile(path))
{
goto found;
}
/* COMPAT HACK: One-halfth, if ep2v, see if there is an engine specific build in the new place with old naming */
if (strcmp(bridge->gamesuffix, "2.tf2") == 0
|| strcmp(bridge->gamesuffix, "2.dods") == 0
|| strcmp(bridge->gamesuffix, "2.hl2dm") == 0
)
{
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.2.ep2v." PLATFORM_LIB_EXT,
"extensions/" PLATFORM_ARCH_FOLDER "%s." PLATFORM_LIB_EXT,
filename);
}
else
{
/* Zeroth, see if there is an engine specific build in the new place. */
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/" PLATFORM_ARCH_FOLDER "%s.%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
if (libsys->IsPathFile(path))
if (!libsys->IsPathFile(path))
{
goto found;
/* COMPAT HACK: One-halfth, if ep2v, see if there is an engine specific build in the new place with old naming */
if (strcmp(bridge->gamesuffix, "2.tf2") == 0
|| strcmp(bridge->gamesuffix, "2.dods") == 0
|| strcmp(bridge->gamesuffix, "2.hl2dm") == 0
)
{
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/" PLATFORM_ARCH_FOLDER "%s.2.ep2v." PLATFORM_LIB_EXT,
filename);
}
else if (strcmp(bridge->gamesuffix, "2.nd") == 0)
{
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/" PLATFORM_ARCH_FOLDER "%s.2.l4d2." PLATFORM_LIB_EXT,
filename);
}
//Try further
if (!libsys->IsPathFile(path))
{
/* First see if there is an engine specific build! */
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/" PLATFORM_ARCH_FOLDER "auto.%s/%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
/* Try the "normal" version */
if (!libsys->IsPathFile(path))
{
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/" PLATFORM_ARCH_FOLDER "%s." PLATFORM_LIB_EXT,
filename);
}
}
}
}
else if (strcmp(bridge->gamesuffix, "2.nd") == 0)
{
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s.2.l4d2." PLATFORM_LIB_EXT,
filename);
if (libsys->IsPathFile(path))
{
goto found;
}
}
/* First see if there is an engine specific build! */
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/auto.%s/%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
/* Try the "normal" version */
if (!libsys->IsPathFile(path))
{
normal:
g_pSM->BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s." PLATFORM_LIB_EXT,
filename);
}
found:
Initialize(filename, path, bRequired);
}
@@ -176,7 +174,7 @@ bool CLocalExtension::Load(char *error, size_t maxlength)
{
m_pLib->CloseLibrary();
m_pLib = NULL;
snprintf(error, maxlength, "Unable to find extension entry point");
ke::SafeStrcpy(error, maxlength, "Unable to find extension entry point");
return false;
}
@@ -242,11 +240,13 @@ void CLocalExtension::Unload()
m_pLib->CloseLibrary();
m_pLib = NULL;
}
m_bFullyLoaded = false;
}
bool CRemoteExtension::Reload(char *error, size_t maxlength)
{
snprintf(error, maxlength, "Remote extensions do not support reloading");
ke::SafeStrcpy(error, maxlength, "Remote extensions do not support reloading");
return false;
}
@@ -280,13 +280,13 @@ bool CExtension::PerformAPICheck(char *error, size_t maxlength)
{
if (!m_pAPI)
{
snprintf(error, maxlength, "No IExtensionInterface instance provided");
ke::SafeStrcpy(error, maxlength, "No IExtensionInterface instance provided");
return false;
}
if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
{
snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
ke::SafeSprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
return false;
}
@@ -306,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;
@@ -421,65 +421,20 @@ void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
m_ChildDeps.push_back(info);
}
// note: dependency iteration deprecated since 1.10
ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
{
List<IfaceInfo>::iterator iter = m_Deps.begin();
if (iter == m_Deps.end())
{
return NULL;
}
if (pOwner)
{
*pOwner = (*iter).owner;
}
if (pInterface)
{
*pInterface = (*iter).iface;
}
List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
return (ITERATOR *)pIter;
return nullptr;
}
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
{
List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
List<IfaceInfo>::iterator _iter;
if (_iter == m_Deps.end())
{
return false;
}
_iter++;
if (pOwner)
{
*pOwner = (*_iter).owner;
}
if (pInterface)
{
*pInterface = (*_iter).iface;
}
*pIter = _iter;
if (_iter == m_Deps.end())
{
return false;
}
return true;
return false;
}
void CExtension::FreeDependencyIterator(ITERATOR *iter)
{
List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
delete pIter;
}
void CExtension::AddInterface(SMInterface *pInterface)
@@ -493,7 +448,7 @@ bool CExtension::IsRunning(char *error, size_t maxlength)
{
if (error)
{
snprintf(error, maxlength, "%s", m_Error.c_str());
ke::SafeStrcpy(error, maxlength, m_Error.c_str());
}
return false;
}
@@ -546,7 +501,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;
@@ -575,7 +530,7 @@ void CExtensionManager::TryAutoload()
}
char file[PLATFORM_MAX_PATH];
len = ke::SafeSprintf(file, sizeof(file), "%s", lfile);
len = ke::SafeStrcpy(file, sizeof(file), lfile);
strcpy(&file[len - 9], ".ext");
LoadAutoExtension(file);
@@ -591,7 +546,7 @@ IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOn
if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
{
char path2[PLATFORM_MAX_PATH];
ke::SafeSprintf(path2, sizeof(path2), "%s", path);
ke::SafeStrcpy(path2, sizeof(path2), path);
path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
return LoadAutoExtension(path2, bErrorOnMissing);
}
@@ -678,12 +633,18 @@ 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::SafeSprintf(path2, sizeof(path2), "%s", file);
ke::SafeStrcpy(path2, sizeof(path2), file);
path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
return LoadExtension(path2, error, maxlength);
}
@@ -948,20 +909,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
CExtension *pExt;
unsigned int num = 1;
List<CExtension *> required; // List of loaded and required extensions
List<CExtension *> optional; // List of non loaded optional extensions
for (iter = m_Libs.begin(); iter != m_Libs.end(); iter++)
{
pExt = (*iter);
if (pExt->IsLoaded() || pExt->IsRequired())
required.push_back(pExt);
else if (!pExt->IsLoaded() && !pExt->IsRequired())
optional.push_back(pExt);
}
switch (required.size())
switch (m_Libs.size())
{
case 1:
{
@@ -975,11 +923,11 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
}
default:
{
rootmenu->ConsolePrint("[SM] Displaying %d extensions:", required.size());
rootmenu->ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
break;
}
}
for (iter = required.begin(); iter != required.end(); iter++,num++)
for (iter = m_Libs.begin(); iter != m_Libs.end(); iter++,num++)
{
pExt = (*iter);
if (pExt->IsLoaded())
@@ -998,32 +946,13 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
rootmenu->ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
}
}
else
else if(pExt->IsRequired() || libsys->PathExists(pExt->GetPath()))
{
rootmenu->ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
}
}
if (optional.size())
{
num = 1;
switch (optional.size())
else
{
case 1:
{
rootmenu->ConsolePrint("\n[SM] Displaying 1 optional extension not found:");
break;
}
default:
{
rootmenu->ConsolePrint("\n[SM] Displaying %d optional extensions not found:", optional.size());
break;
}
}
for (iter = optional.begin(); iter != optional.end(); iter++,num++)
{
pExt = (*iter);
rootmenu->ConsolePrint("[%02d] \"%s\"", num, pExt->GetFilename());
rootmenu->ConsolePrint("[%02d] <OPTIONAL> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
}
}
return;
@@ -1168,7 +1097,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
if (pExt->unload_code == (unsigned)atoi(unload))
{
char filename[PLATFORM_MAX_PATH];
snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename());
UnloadExtension(pExt);
rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename);
}
@@ -1183,7 +1112,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
{
char filename[PLATFORM_MAX_PATH];
snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename());
UnloadExtension(pExt);
rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename);
return;
@@ -1253,7 +1182,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);
@@ -1284,7 +1212,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
char filename[PLATFORM_MAX_PATH];
char error[255];
snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename());
if (pExt->Reload(error, sizeof(error)))
{
@@ -1446,7 +1374,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;
}
+5 -5
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()
+6 -11
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,18 +46,13 @@ SafeFrameIterator::SafeFrameIterator(IFrameIterator *it)
bool SafeFrameIterator::Done() const
{
return current == frames.length();
return current >= frames.size();
}
bool SafeFrameIterator::Next()
{
if (!this->Done())
{
current++;
return true;
}
return false;
current++;
return !this->Done();
}
void SafeFrameIterator::Reset()
@@ -82,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
@@ -92,5 +87,5 @@ const char *SafeFrameIterator::FilePath() const
return NULL;
}
return frames[current].FilePath.chars();
return frames[current].FilePath.c_str();
}
+8 -6
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;
};
+117 -61
View File
@@ -66,27 +66,31 @@ static const char *g_pParseEngine = NULL;
#define PSTATE_GAMEDEFS_OFFSETS 3
#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
#define PSTATE_GAMEDEFS_KEYS 5
#define PSTATE_GAMEDEFS_SUPPORTED 6
#define PSTATE_GAMEDEFS_SIGNATURES 7
#define PSTATE_GAMEDEFS_SIGNATURES_SIG 8
#define PSTATE_GAMEDEFS_CRC 9
#define PSTATE_GAMEDEFS_CRC_BINARY 10
#define PSTATE_GAMEDEFS_CUSTOM 11
#define PSTATE_GAMEDEFS_ADDRESSES 12
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS 13
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ 14
#define PSTATE_GAMEDEFS_KEYS_PLATFORM 6
#define PSTATE_GAMEDEFS_SUPPORTED 7
#define PSTATE_GAMEDEFS_SIGNATURES 8
#define PSTATE_GAMEDEFS_SIGNATURES_SIG 9
#define PSTATE_GAMEDEFS_CRC 10
#define PSTATE_GAMEDEFS_CRC_BINARY 11
#define PSTATE_GAMEDEFS_CUSTOM 12
#define PSTATE_GAMEDEFS_ADDRESSES 13
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS 14
#define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ 15
#if defined PLATFORM_X86
#define PLATFORM_ARCH_SUFFIX ""
#elif defined PLATFORM_X64
#define PLATFORM_ARCH_SUFFIX "64"
#endif
#if defined PLATFORM_WINDOWS
#define PLATFORM_NAME "windows"
#define PLATFORM_SERVER_BINARY "server.dll"
#define PLATFORM_NAME "windows" PLATFORM_ARCH_SUFFIX
#elif defined PLATFORM_LINUX
#define PLATFORM_NAME "linux"
#define PLATFORM_COMPAT_ALT "mac" /* Alternate platform name if game data is missing for primary one */
#define PLATFORM_SERVER_BINARY "server_i486.so"
#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 */
#elif defined PLATFORM_APPLE
#define PLATFORM_NAME "mac"
#define PLATFORM_COMPAT_ALT "linux"
#define PLATFORM_SERVER_BINARY "server.dylib"
#define PLATFORM_NAME "mac" PLATFORM_ARCH_SUFFIX
#define PLATFORM_COMPAT_ALT "linux" PLATFORM_ARCH_SUFFIX
#endif
struct TempSigInfo
@@ -146,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));
@@ -153,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
@@ -199,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++;
}
@@ -215,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. */
@@ -252,18 +275,25 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
}
break;
}
case PSTATE_GAMEDEFS_KEYS:
{
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;
@@ -282,7 +312,10 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
FILE *fp;
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
char binName[64];
bridge->FormatSourceBinaryName(name, binName, sizeof(binName));
g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/%s", binName);
if ((fp = fopen(path, "rb")) == NULL)
{
ke::SafeSprintf(error, sizeof(error), "Could not open binary: %s", path);
@@ -305,7 +338,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
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;
@@ -320,12 +353,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;
@@ -338,9 +371,10 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
}
else
{
if (strcmp(name, "linux") != 0 && strcmp(name, "windows") != 0 && strcmp(name, "mac") != 0)
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;
@@ -349,7 +383,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
}
/* No sub-sections allowed:
case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
case PSTATE_GAMEDEFS_KEYS:
case PSTATE_GAMEDEFS_KEYS_PLATFORM:
case PSTATE_GAMEDEFS_SUPPORTED:
case PSTATE_GAMEDEFS_SIGNATURES_SIG:
case PSTATE_GAMEDEFS_CRC_BINARY:
@@ -377,15 +411,22 @@ 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))
{
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)
{
@@ -435,7 +476,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)
{
@@ -443,15 +484,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);
@@ -499,28 +540,32 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
m_ParseState = PSTATE_GAMEDEFS;
break;
}
case PSTATE_GAMEDEFS_KEYS_PLATFORM:
{
m_ParseState = PSTATE_GAMEDEFS_KEYS;
break;
}
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());
}
}
}
@@ -628,7 +673,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;
@@ -644,10 +689,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;
@@ -765,7 +810,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;
}
@@ -974,10 +1022,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
@@ -995,7 +1043,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;
@@ -1028,11 +1076,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);
@@ -1105,9 +1153,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);
+15 -9
View File
@@ -72,20 +72,25 @@ public: //NameHashSet
{
return strcmp(key, value->m_File) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
private:
char m_File[PLATFORM_MAX_PATH];
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];
std::string m_Class;
std::string m_Prop;
std::string m_offset;
std::string m_Game;
std::string m_Key;
bool bShouldBeReadingDefault;
bool had_game;
bool matched_game;
@@ -100,24 +105,25 @@ 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;
};
class GameConfigManager :
+104 -15
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);
}
+19 -8
View File
@@ -32,21 +32,27 @@
#ifndef _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_
#define _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_
#include <IHandleSys.h>
#include <stdio.h>
#include <sm_namehashset.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
@@ -87,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 */
@@ -104,15 +111,19 @@ 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)
{
return type->name && type->name->compare(key) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
typedef ke::Lambda<void(const char *)> HandleReporter;
typedef ke::Function<void(const char *)> HandleReporter;
class HandleSystem :
public IHandleSys
+11 -7
View File
@@ -66,7 +66,7 @@ CDirectory::CDirectory(const char *path)
{
#if defined PLATFORM_WINDOWS
char newpath[PLATFORM_MAX_PATH];
snprintf(newpath, sizeof(newpath), "%s\\*.*", path);
ke::SafeSprintf(newpath, sizeof(newpath), "%s\\*.*", path);
m_dir = FindFirstFileA(newpath, &m_fd);
if (!IsValid())
{
@@ -78,7 +78,7 @@ CDirectory::CDirectory(const char *path)
{
/* :TODO: we need to read past "." and ".."! */
ep = readdir(m_dir);
snprintf(m_origpath, PLATFORM_MAX_PATH, "%s", path);
ke::SafeStrcpy(m_origpath, PLATFORM_MAX_PATH, path);
} else {
ep = NULL;
}
@@ -125,7 +125,9 @@ bool CDirectory::IsEntryDirectory()
return ((m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY);
#elif defined PLATFORM_POSIX
char temppath[PLATFORM_MAX_PATH];
snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
int ret = ke::SafeSprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
if (static_cast<size_t>(ret) >= sizeof(temppath))
return false;
return ke::file::IsDirectory(temppath);
#endif
}
@@ -136,7 +138,9 @@ bool CDirectory::IsEntryFile()
return !(m_fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE));
#elif defined PLATFORM_POSIX
char temppath[PLATFORM_MAX_PATH];
snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
int ret = ke::SafeSprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
if (static_cast<size_t>(ret) >= sizeof(temppath))
return false;
return ke::file::IsFile(temppath);
#endif
}
@@ -226,7 +230,7 @@ void LibrarySystem::GetPlatformErrorEx(int code, char *error, size_t maxlength)
const char *ae = strerror_r(code, error, maxlength);
if (ae != error)
{
ke::SafeSprintf(error, maxlength, "%s", ae);
ke::SafeStrcpy(error, maxlength, ae);
}
#elif defined PLATFORM_POSIX
strerror_r(code, error, maxlength);
@@ -305,12 +309,12 @@ size_t LibrarySystem::GetFileFromPath(char *buffer, size_t maxlength, const char
#endif
)
{
return ke::SafeSprintf(buffer, maxlength, "%s", &path[i+1]);
return ke::SafeStrcpy(buffer, maxlength, &path[i+1]);
}
}
/* We scanned and found no path separator */
return ke::SafeSprintf(buffer, maxlength, "%s", path);
return ke::SafeStrcpy(buffer, maxlength, path);
}
bool LibrarySystem::FileTime(const char *path, FileTimeType type, time_t *pTime)
+188 -288
View File
@@ -40,10 +40,6 @@
Logger g_Logger;
/**
* :TODO: This should be creating the log folder if it doesn't exist
*/
ConfigResult Logger::OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
@@ -60,7 +56,7 @@ ConfigResult Logger::OnSourceModConfigChanged(const char *key,
} else if (strcasecmp(value, "off") == 0) {
state = false;
} else {
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
return ConfigResult_Reject;
}
@@ -68,7 +64,7 @@ ConfigResult Logger::OnSourceModConfigChanged(const char *key,
{
state ? EnableLogging() : DisableLogging();
} else {
m_InitialState = state;
m_Active = state;
}
return ConfigResult_Accept;
@@ -81,7 +77,7 @@ ConfigResult Logger::OnSourceModConfigChanged(const char *key,
} else if (strcasecmp(value, "game") == 0) {
m_Mode = LoggingMode_Game;
} else {
ke::SafeSprintf(error, maxlength, "Invalid value: must be [daily|map|game]");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be [daily|map|game]");
return ConfigResult_Reject;
}
@@ -93,7 +89,12 @@ ConfigResult Logger::OnSourceModConfigChanged(const char *key,
void Logger::OnSourceModStartup(bool late)
{
InitLogger(m_Mode);
char buff[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs");
if (!libsys->IsPathDirectory(buff))
{
libsys->CreateFolder(buff);
}
}
void Logger::OnSourceModAllShutdown()
@@ -103,127 +104,7 @@ void Logger::OnSourceModAllShutdown()
void Logger::OnSourceModLevelChange(const char *mapName)
{
MapChange(mapName);
}
void Logger::_NewMapFile()
{
if (!m_Active)
{
return;
}
/* Append "Log file closed" to previous log file */
_CloseFile();
char _filename[256];
int i = 0;
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
while (true)
{
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
FILE *fp = fopen(_filename, "r");
if (!fp)
{
break;
}
fclose(fp);
i++;
}
m_NrmFileName.assign(_filename);
FILE *fp = fopen(m_NrmFileName.c_str(), "w");
if (!fp)
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
return;
} else {
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: SourceMod log file started (file \"L%02d%02d%03d.log\") (Version \"%s\")\n", date, curtime->tm_mon + 1, curtime->tm_mday, i, SOURCEMOD_VERSION);
fclose(fp);
}
}
void Logger::_CloseFile()
{
if (!m_Active)
{
return;
}
FILE *fp = NULL;
if (!m_NrmFileName.empty())
{
fp = fopen(m_NrmFileName.c_str(), "r+");
if (fp)
{
fseek(fp, 0, SEEK_END);
LogMessage("Log file closed.");
fclose(fp);
}
m_NrmFileName.clear();
}
if (!m_ErrMapStart)
{
return;
}
fp = fopen(m_ErrFileName.c_str(), "r+");
if (fp)
{
fseek(fp, 0, SEEK_END);
LogError("Error log file session closed.");
fclose(fp);
}
m_ErrFileName.clear();
}
void Logger::InitLogger(LoggingMode mode)
{
m_Mode = mode;
m_Active = m_InitialState;
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
m_NrmCurDay = curtime->tm_mday;
m_ErrCurDay = curtime->tm_mday;
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_ErrFileName.assign(_filename);
switch (m_Mode)
{
case LoggingMode_PerMap:
{
if (!m_Active)
{
m_DelayedStart = true;
}
break;
}
case LoggingMode_Daily:
{
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_NrmFileName.assign(_filename);
m_DailyPrintHdr = true;
break;
}
default:
{
/* do nothing... */
break;
}
}
_MapChange(mapName);
}
void Logger::CloseLogger()
@@ -231,6 +112,13 @@ void Logger::CloseLogger()
_CloseFile();
}
void Logger::_CloseFile()
{
_CloseNormal();
_CloseError();
_CloseFatal();
}
void Logger::LogToOpenFile(FILE *fp, const char *msg, ...)
{
if (!m_Active)
@@ -259,11 +147,6 @@ void Logger::LogToFileOnly(FILE *fp, const char *msg, ...)
void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
static ConVar *sv_logecho = bridge->FindConVar("sv_logecho");
char buffer[3072];
@@ -282,15 +165,12 @@ void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
ke::SafeSprintf(conBuffer, sizeof(conBuffer), "L %s: %s\n", date, buffer);
bridge->ConPrint(conBuffer);
}
fflush(fp);
}
void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
char buffer[3072];
ke::SafeVsprintf(buffer, sizeof(buffer), msg, ap);
@@ -298,8 +178,8 @@ void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: %s\n", date, buffer);
fflush(fp);
}
@@ -324,63 +204,14 @@ void Logger::LogMessageEx(const char *vafmt, va_list ap)
return;
}
if (m_DelayedStart)
FILE *pFile = _OpenNormal();
if (!pFile)
{
m_DelayedStart = false;
_NewMapFile();
return;
}
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
FILE *fp = NULL;
if (m_Mode == LoggingMode_PerMap)
{
fp = fopen(m_NrmFileName.c_str(), "a+");
if (!fp)
{
_NewMapFile();
fp = fopen(m_NrmFileName.c_str(), "a+");
if (!fp)
{
goto print_error;
}
}
} else {
if (m_NrmCurDay != curtime->tm_mday)
{
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_NrmFileName.assign(_filename);
m_NrmCurDay = curtime->tm_mday;
m_DailyPrintHdr = true;
}
fp = fopen(m_NrmFileName.c_str(), "a+");
}
if (fp)
{
if (m_DailyPrintHdr)
{
char date[32];
m_DailyPrintHdr = false;
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: SourceMod log file session started (file \"L%04d%02d%02d.log\") (Version \"%s\")\n", date, curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday, SOURCEMOD_VERSION);
}
LogToOpenFileEx(fp, vafmt, ap);
fclose(fp);
} else {
goto print_error;
}
return;
print_error:
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
LogToOpenFileEx(pFile, vafmt, ap);
fclose(pFile);
}
void Logger::LogError(const char *vafmt, ...)
@@ -398,72 +229,20 @@ void Logger::LogErrorEx(const char *vafmt, va_list ap)
return;
}
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
if (curtime->tm_mday != m_ErrCurDay)
FILE *pFile = _OpenError();
if (!pFile)
{
char _filename[256];
g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_ErrFileName.assign(_filename);
m_ErrCurDay = curtime->tm_mday;
m_ErrMapStart = false;
}
FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
if (fp)
{
if (!m_ErrMapStart)
{
char date[32];
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: SourceMod error session started\n", date);
fprintf(fp, "L %s: Info (map \"%s\") (file \"errors_%04d%02d%02d.log\")\n", date, m_CurMapName.c_str(), curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
m_ErrMapStart = true;
}
LogToOpenFileEx(fp, vafmt, ap);
fclose(fp);
}
else
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
LogFatal("[SM] Logging has been disabled.");
m_Active = false;
return;
}
LogToOpenFileEx(pFile, vafmt, ap);
fclose(pFile);
}
void Logger::MapChange(const char *mapname)
void Logger::_MapChange(const char *mapname)
{
m_CurMapName.assign(mapname);
switch (m_Mode)
{
case LoggingMode_Daily:
{
LogMessage("-------- Mapchange to %s --------", mapname);
break;
}
case LoggingMode_PerMap:
{
_NewMapFile();
break;
}
default:
{
/* Do nothing... */
break;
}
}
if (m_ErrMapStart)
{
LogError("Error log file session closed.");
}
m_ErrMapStart = false;
m_CurrentMapName = mapname;
_UpdateFiles(true);
}
void Logger::_PrintToGameLog(const char *fmt, va_list ap)
@@ -480,30 +259,6 @@ void Logger::_PrintToGameLog(const char *fmt, va_list ap)
bridge->LogToGame(msg);
}
const char *Logger::GetLogFileName(LogType type) const
{
switch (type)
{
case LogType_Normal:
{
return m_NrmFileName.c_str();
}
case LogType_Error:
{
return m_ErrFileName.c_str();
}
default:
{
return "";
}
}
}
LoggingMode Logger::GetLoggingMode() const
{
return m_Mode;
}
void Logger::EnableLogging()
{
if (m_Active)
@@ -539,16 +294,161 @@ void Logger::LogFatalEx(const char *msg, va_list ap)
* It's already implemented twice which is bad.
*/
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
FILE *fp = fopen(path, "at");
if (fp)
FILE *pFile = _OpenFatal();
if (!pFile)
{
m_Active = true;
LogToOpenFileEx(fp, msg, ap);
m_Active = false;
fclose(fp);
return;
}
LogToOpenFileEx(pFile, msg, ap);
fclose(pFile);
}
void Logger::_UpdateFiles(bool bLevelChange)
{
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
if (!bLevelChange && curtime->tm_mday == m_Day)
{
return;
}
m_Day = curtime->tm_mday;
char buff[PLATFORM_MAX_PATH];
ke::SafeSprintf(buff, sizeof(buff), "%04d%02d%02d", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
std::string currentDate(buff);
if (m_Mode == LoggingMode_PerMap)
{
if (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.c_str(), iter);
if (!libsys->IsPathFile(buff))
{
break;
}
}
}
else
{
ke::SafeStrcpy(buff, sizeof(buff), m_NormalFileName.c_str());
}
}
else
{
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/L%s.log", currentDate.c_str());
}
if (m_NormalFileName.compare(buff))
{
_CloseNormal();
m_NormalFileName = buff;
}
else
{
if (bLevelChange)
{
LogMessage("-------- Mapchange to %s --------", m_CurrentMapName.c_str());
}
}
g_pSM->BuildPath(Path_SM, buff, sizeof(buff), "logs/errors_%s.log", currentDate.c_str());
if (bLevelChange || m_ErrorFileName.compare(buff))
{
_CloseError();
m_ErrorFileName = buff;
}
}
FILE *Logger::_OpenNormal()
{
_UpdateFiles();
FILE *pFile = fopen(m_NormalFileName.c_str(), "a+");
if (pFile == NULL)
{
_LogFatalOpen(m_NormalFileName);
return pFile;
}
if (!m_DamagedNormalFile)
{
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
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.c_str(), SOURCEMOD_VERSION);
m_DamagedNormalFile = true;
}
return pFile;
}
FILE *Logger::_OpenError()
{
_UpdateFiles();
FILE *pFile = fopen(m_ErrorFileName.c_str(), "a+");
if (pFile == NULL)
{
_LogFatalOpen(m_ErrorFileName);
return pFile;
}
if (!m_DamagedErrorFile)
{
time_t t = g_pSM->GetAdjustedTime();
tm *curtime = localtime(&t);
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.c_str(), m_ErrorFileName.c_str());
m_DamagedErrorFile = true;
}
return pFile;
}
FILE *Logger::_OpenFatal()
{
char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
return fopen(path, "at");
}
void Logger::_LogFatalOpen(std::string &str)
{
char error[255];
libsys->GetPlatformError(error, sizeof(error));
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", str.c_str());
LogFatal("[SM] Platform returned error: \"%s\"", error);
}
void Logger::_CloseNormal()
{
if (m_DamagedNormalFile)
{
LogMessage("Log file closed.");
m_DamagedNormalFile = false;
}
}
void Logger::_CloseError()
{
if (m_DamagedErrorFile)
{
LogError("Error log file session closed.");
m_DamagedErrorFile = false;
}
}
void Logger::_CloseFatal()
{
}
+22 -20
View File
@@ -34,11 +34,9 @@
#include "common_logic.h"
#include <stdio.h>
#include <sh_string.h>
#include <amtl/am-string.h>
#include <bridge/include/ILogger.h>
using namespace SourceHook;
enum LogType
{
LogType_Normal,
@@ -55,9 +53,7 @@ enum LoggingMode
class Logger : public SMGlobalClass, public ILogger
{
public:
Logger() : m_Mode(LoggingMode_Daily), m_ErrMapStart(false),
m_Active(false), m_DelayedStart(false), m_DailyPrintHdr(false),
m_InitialState(true)
Logger() : m_Day(-1), m_Mode(LoggingMode_Daily), m_Active(true), m_DamagedNormalFile(false), m_DamagedErrorFile(false)
{
}
public: //SMGlobalClass
@@ -70,7 +66,6 @@ public: //SMGlobalClass
void OnSourceModAllShutdown();
void OnSourceModLevelChange(const char *mapName);
public:
void InitLogger(LoggingMode mode);
void CloseLogger();
void EnableLogging();
void DisableLogging();
@@ -85,25 +80,32 @@ public:
/* This version does not print to console, and is thus thread-safe */
void LogToFileOnly(FILE *fp, const char *msg, ...);
void LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap);
void MapChange(const char *mapname);
const char *GetLogFileName(LogType type) const;
LoggingMode GetLoggingMode() const;
private:
void _MapChange(const char *mapname);
void _CloseFile();
void _NewMapFile();
void _CloseNormal();
void _CloseError();
void _CloseFatal();
FILE *_OpenNormal();
FILE *_OpenError();
FILE *_OpenFatal();
void _LogFatalOpen(std::string &str);
void _PrintToGameLog(const char *fmt, va_list ap);
void _UpdateFiles(bool bLevelChange = false);
private:
String m_NrmFileName;
String m_ErrFileName;
String m_CurMapName;
std::string m_NormalFileName;
std::string m_ErrorFileName;
std::string m_CurrentMapName;
int m_Day;
LoggingMode m_Mode;
int m_NrmCurDay;
int m_ErrCurDay;
bool m_ErrMapStart;
bool m_Active;
bool m_DelayedStart;
bool m_DailyPrintHdr;
bool m_InitialState;
bool m_DamagedNormalFile;
bool m_DamagedErrorFile;
};
extern Logger g_Logger;
+131
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
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_
+165 -104
View File
@@ -43,45 +43,19 @@
#include <mach-o/dyld_images.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
/* Define things from 10.6 SDK for older SDKs */
#ifndef MAC_OS_X_VERSION_10_6
struct task_dyld_info
{
mach_vm_address_t all_image_info_addr;
mach_vm_size_t all_image_info_size;
};
typedef struct task_dyld_info task_dyld_info_data_t;
#define TASK_DYLD_INFO 17
#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
#endif // MAC_OS_X_VERSION_10_6
#endif // PLATFORM_APPLE
MemoryUtils g_MemUtils;
MemoryUtils::MemoryUtils()
{
m_InfoMap.init();
#ifdef PLATFORM_APPLE
Gestalt(gestaltSystemVersionMajor, &m_OSXMajor);
Gestalt(gestaltSystemVersionMinor, &m_OSXMinor);
/* Get pointer to struct that describes all loaded mach-o images in process */
if ((m_OSXMajor == 10 && m_OSXMinor >= 6) || m_OSXMajor > 10)
{
task_dyld_info_data_t dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
m_ImageList = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr;
}
else
{
struct nlist list[2];
memset(list, 0, sizeof(list));
list[0].n_un.n_name = (char *)"_dyld_all_image_infos";
nlist("/usr/lib/dyld", list);
m_ImageList = (struct dyld_all_image_infos *)list[0].n_value;
}
task_dyld_info_data_t dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
m_ImageList = (struct dyld_all_image_infos *)dyld_info.all_image_info_addr;
#endif
}
@@ -104,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;
@@ -130,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++;
}
@@ -143,17 +117,31 @@ 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
#ifdef PLATFORM_X86
typedef Elf32_Ehdr ElfHeader;
typedef Elf32_Shdr ElfSHeader;
typedef Elf32_Sym ElfSymbol;
#define ELF_SYM_TYPE ELF32_ST_TYPE
#else
typedef Elf64_Ehdr ElfHeader;
typedef Elf64_Shdr ElfSHeader;
typedef Elf64_Sym ElfSymbol;
#define ELF_SYM_TYPE ELF64_ST_TYPE
#endif
struct link_map *dlmap;
struct stat dlstat;
int dlfile;
uintptr_t map_base;
Elf32_Ehdr *file_hdr;
Elf32_Shdr *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
Elf32_Sym *symtab;
ElfHeader *file_hdr;
ElfSHeader *sections, *shstrtab_hdr, *symtab_hdr, *strtab_hdr;
ElfSymbol *symtab;
const char *shstrtab, *strtab;
uint16_t section_count;
uint32_t symbol_count;
@@ -177,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)
{
@@ -204,7 +195,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
/* Map library file into memory */
file_hdr = (Elf32_Ehdr *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
file_hdr = (ElfHeader *)mmap(NULL, dlstat.st_size, PROT_READ, MAP_PRIVATE, dlfile, 0);
map_base = (uintptr_t)file_hdr;
if (file_hdr == MAP_FAILED)
{
@@ -219,7 +210,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
return NULL;
}
sections = (Elf32_Shdr *)(map_base + file_hdr->e_shoff);
sections = (ElfSHeader *)(map_base + file_hdr->e_shoff);
section_count = file_hdr->e_shnum;
/* Get ELF section header string table */
shstrtab_hdr = &sections[file_hdr->e_shstrndx];
@@ -228,7 +219,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
/* Iterate sections while looking for ELF symbol table and string table */
for (uint16_t i = 0; i < section_count; i++)
{
Elf32_Shdr &hdr = sections[i];
ElfSHeader &hdr = sections[i];
const char *section_name = shstrtab + hdr.sh_name;
if (strcmp(section_name, ".symtab") == 0)
@@ -248,15 +239,15 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
return NULL;
}
symtab = (Elf32_Sym *)(map_base + symtab_hdr->sh_offset);
symtab = (ElfSymbol *)(map_base + symtab_hdr->sh_offset);
strtab = (const char *)(map_base + strtab_hdr->sh_offset);
symbol_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
/* Iterate symbol table starting from the position we were at last time */
for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
{
Elf32_Sym &sym = symtab[i];
unsigned char sym_type = ELF32_ST_TYPE(sym.st_info);
ElfSymbol &sym = symtab[i];
unsigned char sym_type = ELF_SYM_TYPE(sym.st_info);
const char *sym_name = strtab + sym.st_name;
Symbol *cur_sym;
@@ -280,14 +271,29 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
return symbol_entry ? symbol_entry->address : NULL;
#elif defined PLATFORM_APPLE
#ifdef PLATFORM_X86
typedef struct mach_header MachHeader;
typedef struct segment_command MachSegment;
typedef struct nlist MachSymbol;
const uint32_t MACH_LOADCMD_SEGMENT = LC_SEGMENT;
#else
typedef struct mach_header_64 MachHeader;
typedef struct segment_command_64 MachSegment;
typedef struct nlist_64 MachSymbol;
const uint32_t MACH_LOADCMD_SEGMENT = LC_SEGMENT_64;
#endif
typedef struct load_command MachLoadCmd;
typedef struct symtab_command MachSymHeader;
uintptr_t dlbase, linkedit_addr;
uint32_t image_count;
struct mach_header *file_hdr;
struct load_command *loadcmds;
struct segment_command *linkedit_hdr;
struct symtab_command *symtab_hdr;
struct nlist *symtab;
MachHeader *file_hdr;
MachLoadCmd *loadcmds;
MachSegment *linkedit_hdr;
MachSymHeader *symtab_hdr;
MachSymbol *symtab;
const char *strtab;
uint32_t loadcmd_count;
uint32_t symbol_count;
@@ -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++)
@@ -357,16 +366,16 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
/* If symbol isn't in our table, then we have to locate it in memory */
file_hdr = (struct mach_header *)dlbase;
loadcmds = (struct load_command *)(dlbase + sizeof(struct mach_header));
file_hdr = (MachHeader *)dlbase;
loadcmds = (MachLoadCmd *)(dlbase + sizeof(MachHeader));
loadcmd_count = file_hdr->ncmds;
/* Loop through load commands until we find the ones for the symbol table */
for (uint32_t i = 0; i < loadcmd_count; i++)
{
if (loadcmds->cmd == LC_SEGMENT && !linkedit_hdr)
if (loadcmds->cmd == MACH_LOADCMD_SEGMENT && !linkedit_hdr)
{
struct segment_command *seg = (struct segment_command *)loadcmds;
MachSegment *seg = (MachSegment *)loadcmds;
if (strcmp(seg->segname, "__LINKEDIT") == 0)
{
linkedit_hdr = seg;
@@ -378,7 +387,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
else if (loadcmds->cmd == LC_SYMTAB)
{
symtab_hdr = (struct symtab_command *)loadcmds;
symtab_hdr = (MachSymHeader *)loadcmds;
if (linkedit_hdr)
{
break;
@@ -386,7 +395,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
/* Load commands are not of a fixed size which is why we add the size */
loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize);
loadcmds = (MachLoadCmd *)((uintptr_t)loadcmds + loadcmds->cmdsize);
}
if (!linkedit_hdr || !symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff)
@@ -396,14 +405,14 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
}
linkedit_addr = dlbase + linkedit_hdr->vmaddr;
symtab = (struct nlist *)(linkedit_addr + symtab_hdr->symoff - linkedit_hdr->fileoff);
symtab = (MachSymbol *)(linkedit_addr + symtab_hdr->symoff - linkedit_hdr->fileoff);
strtab = (const char *)(linkedit_addr + symtab_hdr->stroff - linkedit_hdr->fileoff);
symbol_count = symtab_hdr->nsyms;
/* Iterate symbol table starting from the position we were at last time */
for (uint32_t i = libtable->last_pos; i < symbol_count; i++)
{
struct nlist &sym = symtab[i];
MachSymbol &sym = symtab[i];
/* Ignore the prepended underscore on all symbols, so +1 here */
const char *sym_name = strtab + sym.n_un.n_strx + 1;
Symbol *cur_sym;
@@ -429,17 +438,27 @@ 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;
IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS *pe;
@@ -448,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);
@@ -460,23 +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, which is 32-bit/x86 right now
* Should change this for 64-bit if Valve gets their act together
*/
if (file->Machine != IMAGE_FILE_MACHINE_I386)
/* 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 */
@@ -484,57 +501,73 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
#elif defined PLATFORM_LINUX
#ifdef PLATFORM_X86
typedef Elf32_Ehdr ElfHeader;
typedef Elf32_Phdr ElfPHeader;
const unsigned char ELF_CLASS = ELFCLASS32;
const uint16_t ELF_MACHINE = EM_386;
#else
typedef Elf64_Ehdr ElfHeader;
typedef Elf64_Phdr ElfPHeader;
const unsigned char ELF_CLASS = ELFCLASS64;
const uint16_t ELF_MACHINE = EM_X86_64;
#endif
Dl_info info;
Elf32_Ehdr *file;
Elf32_Phdr *phdr;
ElfHeader *file;
ElfPHeader *phdr;
uint16_t phdrCount;
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 */
baseAddr = reinterpret_cast<uintptr_t>(info.dli_fbase);
file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
file = reinterpret_cast<ElfHeader *>(baseAddr);
/* 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 nullptr;
}
/* Check ELF architecture, which is 32-bit/x86 right now
* Should change this for 64-bit if Valve gets their act together
*/
if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
/* 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;
phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
phdr = reinterpret_cast<ElfPHeader *>(baseAddr + file->e_phoff);
for (uint16_t i = 0; i < phdrCount; i++)
{
Elf32_Phdr &hdr = phdr[i];
ElfPHeader &hdr = phdr[i];
/* We only really care about the segment with executable code */
if (hdr.p_type == PT_LOAD && hdr.p_flags == (PF_X|PF_R))
@@ -553,60 +586,88 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
#elif defined PLATFORM_APPLE
#ifdef PLATFORM_X86
typedef struct mach_header MachHeader;
typedef struct segment_command MachSegment;
const uint32_t MACH_MAGIC = MH_MAGIC;
const uint32_t MACH_LOADCMD_SEGMENT = LC_SEGMENT;
const cpu_type_t MACH_CPU_TYPE = CPU_TYPE_I386;
const cpu_subtype_t MACH_CPU_SUBTYPE = CPU_SUBTYPE_I386_ALL;
#else
typedef struct mach_header_64 MachHeader;
typedef struct segment_command_64 MachSegment;
const uint32_t MACH_MAGIC = MH_MAGIC_64;
const uint32_t MACH_LOADCMD_SEGMENT = LC_SEGMENT_64;
const cpu_type_t MACH_CPU_TYPE = CPU_TYPE_X86_64;
const cpu_subtype_t MACH_CPU_SUBTYPE = CPU_SUBTYPE_X86_64_ALL;
#endif
Dl_info info;
struct mach_header *file;
struct segment_command *seg;
MachHeader *file;
MachSegment *seg;
uint32_t cmd_count;
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 */
baseAddr = (uintptr_t)info.dli_fbase;
file = (struct mach_header *)baseAddr;
file = (MachHeader *)baseAddr;
/* Check Mach-O magic */
if (file->magic != MH_MAGIC)
if (file->magic != MACH_MAGIC)
{
return false;
return nullptr;
}
/* Check architecture (32-bit/x86) */
if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL)
/* 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;
seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header));
seg = (MachSegment *)(baseAddr + sizeof(MachHeader));
/* Add up memory sizes of mapped segments */
for (uint32_t i = 0; i < cmd_count; i++)
{
if (seg->cmd == LC_SEGMENT)
if (seg->cmd == MACH_LOADCMD_SEGMENT)
{
lib.memorySize += seg->vmsize;
}
seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
seg = (MachSegment *)((uintptr_t)seg + seg->cmdsize);
}
#endif
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;
}
+6 -1
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;
+14 -17
View File
@@ -36,6 +36,7 @@
#include <am-string.h>
#include <am-utility.h>
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include "common_logic.h"
class CNativeOwner;
@@ -45,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>
@@ -62,37 +61,35 @@ 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)
{
return strcmp(name, entry->name()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
+324
View File
@@ -0,0 +1,324 @@
// 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)
{
if(native_->native)
*result = native_->native->func(context_, _temp_params);
else
*result = native_->fake->wrapper->Invoke(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; }
+78
View File
@@ -0,0 +1,78 @@
// 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-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_
+3 -3
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();
}
+2 -3
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;
+71 -79
View File
@@ -31,7 +31,6 @@
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include "PluginSys.h"
#include "ShareSys.h"
#include <ILibrarySys.h>
@@ -46,8 +45,6 @@
#include "Logger.h"
#include "frame_tasks.h"
#include <amtl/am-string.h>
#include <amtl/am-linkedlist.h>
#include <amtl/am-uniqueptr.h>
#include <bridge/include/IVEngineServerBridge.h>
#include <bridge/include/CoreProvider.h>
@@ -77,11 +74,11 @@ CPlugin::CPlugin(const char *file)
m_serial = ++MySerial;
m_errormsg[0] = '\0';
m_DateTime[0] = '\0';
ke::SafeSprintf(m_filename, sizeof(m_filename), "%s", file);
ke::SafeStrcpy(m_filename, sizeof(m_filename), file);
memset(&m_info, 0, sizeof(m_info));
m_pPhrases = g_Translator.CreatePhraseCollection();
m_pPhrases.reset(g_Translator.CreatePhraseCollection());
}
CPlugin::~CPlugin()
@@ -229,7 +226,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, ...)
@@ -278,7 +275,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;
@@ -485,7 +482,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;
@@ -526,11 +523,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;
}
@@ -660,7 +657,7 @@ time_t CPlugin::GetFileTimeStamp()
IPhraseCollection *CPlugin::GetPhrases()
{
return m_pPhrases;
return m_pPhrases.get();
}
void CPlugin::DependencyDropped(CPlugin *pOwner)
@@ -676,7 +673,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]);
@@ -775,13 +772,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]);
}
@@ -792,7 +789,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);
}
@@ -849,11 +846,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)
@@ -907,7 +900,7 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa
if (localpath == NULL)
{
/* If no path yet, don't add a former slash */
ke::SafeSprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
ke::SafeStrcpy(new_local, sizeof(new_local), dir->GetEntryName());
} else {
libsys->PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
}
@@ -922,7 +915,7 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa
char plugin[PLATFORM_MAX_PATH];
if (localpath == NULL)
{
ke::SafeSprintf(plugin, sizeof(plugin), "%s", name);
ke::SafeStrcpy(plugin, sizeof(plugin), name);
} else {
libsys->PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
}
@@ -934,38 +927,16 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa
libsys->CloseDirectory(dir);
}
#if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
char *strdup_tolower(const char *input)
{
char *str = strdup(input);
for (char *c = str; *c; c++)
{
*c = tolower((unsigned char)*c);
}
return str;
}
#endif
LoadRes CPluginManager::LoadPlugin(CPlugin **aResult, const char *path, bool debug, PluginType type)
{
if (m_LoadingLocked)
return LoadRes_NeverLoad;
/* 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::UniquePtr<char> finalPath = ke::UniquePtr<char>(strdup_tolower(path));
#else
ke::UniquePtr<char> finalPath = ke::UniquePtr<char>(strdup(path));
#endif
/**
* Does this plugin already exist?
*/
CPlugin *pPlugin;
if (m_LoadLookup.retrieve(finalPath.get(), &pPlugin))
if (m_LoadLookup.retrieve(path, &pPlugin))
{
/* Check to see if we should try reloading it */
if (pPlugin->GetStatus() == Plugin_BadLoad
@@ -978,12 +949,11 @@ LoadRes CPluginManager::LoadPlugin(CPlugin **aResult, const char *path, bool deb
{
if (aResult)
*aResult = pPlugin;
return LoadRes_AlreadyLoaded;
}
}
CPlugin *plugin = CompileAndPrep(finalPath.get());
CPlugin *plugin = CompileAndPrep(path);
// Assign our outparam so we can return early. It must be set.
*aResult = plugin;
@@ -1004,6 +974,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());
@@ -1019,9 +1003,9 @@ IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType typ
if (res == LoadRes_NeverLoad) {
if (m_LoadingLocked)
ke::SafeSprintf(error, maxlength, "There is a global plugin loading lock in effect");
ke::SafeStrcpy(error, maxlength, "There is a global plugin loading lock in effect");
else
ke::SafeSprintf(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
ke::SafeStrcpy(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
return NULL;
}
@@ -1058,7 +1042,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();
@@ -1190,7 +1174,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());
@@ -1202,7 +1186,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()))
@@ -1236,7 +1220,7 @@ void CPluginManager::LoadExtensions(CPlugin *pPlugin)
}
return true;
};
pPlugin->ForEachExtVar(ke::Move(callback));
pPlugin->ForEachExtVar(std::move(callback));
}
bool CPluginManager::RequireExtensions(CPlugin *pPlugin)
@@ -1272,7 +1256,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)
@@ -1302,7 +1286,7 @@ bool CPluginManager::MalwareCheckPass(CPlugin *pPlugin)
unsigned char *pCodeHash = pPlugin->GetRuntime()->GetCodeHash();
char codeHashBuf[40];
ke::SafeSprintf(codeHashBuf, 40, "plugin_");
ke::SafeStrcpy(codeHashBuf, sizeof(codeHashBuf), "plugin_");
for (int i = 0; i < 16; i++)
ke::SafeSprintf(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]);
@@ -1515,6 +1499,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())
@@ -1561,12 +1548,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)
@@ -1603,6 +1590,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()
@@ -1617,6 +1605,7 @@ void CPluginManager::OnSourceModShutdown()
forwardsys->ReleaseForward(m_pOnLibraryAdded);
forwardsys->ReleaseForward(m_pOnLibraryRemoved);
forwardsys->ReleaseForward(m_pOnNotifyPluginUnloaded);
}
ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
@@ -1632,7 +1621,7 @@ ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
} else if (strcasecmp(value, "no") == 0) {
m_bBlockBadPlugins = false;
} else {
ke::SafeSprintf(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
ke::SafeStrcpy(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
return ConfigResult_Reject;
}
return ConfigResult_Accept;
@@ -1726,6 +1715,9 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
char buffer[256];
unsigned int id = 1;
int plnum = GetPluginCount();
char plstr[10];
ke::SafeSprintf(plstr, sizeof(plstr), "%d", plnum);
int plpadding = strlen(plstr);
if (!plnum)
{
@@ -1734,10 +1726,10 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
}
else
{
rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
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);
@@ -1746,19 +1738,19 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
const sm_plugininfo_t *info = pl->GetPublicInfo();
if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
{
len += ke::SafeSprintf(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetDisplayStatus()));
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
{
len += ke::SafeSprintf(buffer, sizeof(buffer), " %02d", id);
len += ke::SafeSprintf(buffer, sizeof(buffer), " %0*d", plpadding, id);
}
if (pl->GetStatus() < Plugin_Created || pl->GetStatus() == Plugin_Evicted)
{
if (pl->IsSilentlyFailed())
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " Disabled:");
len += ke::SafeStrcpy(&buffer[len], sizeof(buffer)-len, " Disabled:");
len += ke::SafeSprintf(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
if (IS_STR_FILLED(info->version))
{
@@ -1867,11 +1859,11 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
if (pl->GetStatus() < Plugin_Created)
{
const sm_plugininfo_t *info = pl->GetPublicInfo();
ke::SafeSprintf(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
ke::SafeStrcpy(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
}
else
{
ke::SafeSprintf(name, sizeof(name), "%s", pl->GetFilename());
ke::SafeStrcpy(name, sizeof(name), pl->GetFilename());
}
if (UnloadPlugin(pl))
@@ -1983,11 +1975,11 @@ void CPluginManager::OnRootConsoleCommand(const char *cmdname, const ICommandArg
if (IPluginRuntime *runtime = pl->GetRuntime()) {
unsigned char *pCodeHash = runtime->GetCodeHash();
unsigned char *pDataHash = runtime->GetDataHash();
char combinedHash[33];
for (int i = 0; i < 16; i++)
ke::SafeSprintf(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]);
rootmenu->ConsolePrint(" Hash: %s", combinedHash);
}
} else {
@@ -2055,7 +2047,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);
@@ -2085,7 +2078,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;
@@ -2100,13 +2093,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;
}
@@ -2115,7 +2108,7 @@ void CPluginManager::ReloadPluginImpl(int id, const char filename[], PluginType
char error[128];
bool wasloaded;
IPlugin *newpl = LoadPlugin(filename, true, ptype, error, sizeof(error), &wasloaded);
if (!newpl)
if (!newpl)
{
rootmenu->ConsolePrint("[SM] Plugin %s failed to reload: %s.", filename, error);
return;
@@ -2230,7 +2223,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)
@@ -2295,7 +2287,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);
@@ -2373,7 +2365,7 @@ public:
{
ke::RefPtr<PluginsListenerV1Wrapper> wrapper = new PluginsListenerV1Wrapper(listener);
v1_wrappers_.append(wrapper);
v1_wrappers_.push_back(wrapper);
g_PluginSys.AddPluginsListener(wrapper);
}
+48 -22
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.
@@ -143,11 +146,6 @@ public:
*/
static CPlugin *Create(const char *file);
static inline bool matches(const char *file, const CPlugin *plugin)
{
return strcmp(plugin->m_filename, file) == 0;
}
public:
// Evicts the plugin from memory and sets an error state.
void EvictWithError(PluginStatus status, const char *error_fmt, ...);
@@ -220,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;
@@ -229,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
@@ -272,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;
@@ -291,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 :
@@ -322,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
@@ -437,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);
@@ -478,12 +476,39 @@ 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;
NameHashSet<CPlugin *> m_LoadLookup;
struct CPluginPolicy
{
static inline uint32_t hash(const detail::CharsAndLength &key)
{
/* For windows & mac, we convert the path to lower-case in order to avoid duplicate plugin loading */
#if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
std::string lower = ke::Lowercase(key.c_str());
return detail::CharsAndLength(lower.c_str()).hash();
#else
return key.hash();
#endif
}
static inline bool matches(const char *file, const CPlugin *plugin)
{
const char *pluginFileChars = const_cast<CPlugin*>(plugin)->GetFilename();
#if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
std::string pluginFile = ke::Lowercase(pluginFileChars);
std::string input = ke::Lowercase(file);
return pluginFile == input;
#else
return strcmp(pluginFileChars, file) == 0;
#endif
}
};
NameHashSet<CPlugin *, CPluginPolicy> m_LoadLookup;
bool m_AllPluginsLoaded;
IdentityToken_t *m_MyIdent;
@@ -498,6 +523,7 @@ private:
// Forwards
IForward *m_pOnLibraryAdded;
IForward *m_pOnLibraryRemoved;
IForward *m_pOnNotifyPluginUnloaded;
};
extern CPluginManager g_PluginSys;
+4 -4
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.");
+2 -2
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_;
+169
View File
@@ -0,0 +1,169 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2017 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>.
*/
#include "PseudoAddrManager.h"
#ifdef PLATFORM_APPLE
#include <mach/mach.h>
#include <mach/vm_region.h>
#endif
#ifdef PLATFORM_LINUX
#include <inttypes.h>
#endif
PseudoAddressManager::PseudoAddressManager() : m_NumEntries(0)
{
}
// A pseudo address consists of a table index in the upper 6 bits and an offset in the
// lower 26 bits. The table consists of memory allocation base addresses.
void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr)
{
#ifdef PLATFORM_X64
uint8_t index = paddr >> PSEUDO_OFFSET_BITS;
uint32_t offset = paddr & ((1 << PSEUDO_OFFSET_BITS) - 1);
if (index >= m_NumEntries)
return nullptr;
return reinterpret_cast<void *>(uintptr_t(m_AllocBases[index]) + offset);
#else
return nullptr;
#endif
}
uint32_t PseudoAddressManager::ToPseudoAddress(void *addr)
{
#ifdef PLATFORM_X64
uint8_t index = 0;
uint32_t offset = 0;
bool hasEntry = false;
void *base = GetAllocationBase(addr);
if (base) {
for (int i = 0; i < m_NumEntries; i++) {
if (m_AllocBases[i] == base) {
index = i;
hasEntry = true;
break;
}
}
} else {
return 0;
}
if (!hasEntry) {
index = m_NumEntries;
if (m_NumEntries < SM_ARRAYSIZE(m_AllocBases))
m_AllocBases[m_NumEntries++] = base;
else
return 0; // Table is full
}
ptrdiff_t diff = uintptr_t(addr) - uintptr_t(base);
// Ensure difference fits in 26 bits
if (diff > (UINT32_MAX >> PSEUDO_INDEX_BITS))
return 0;
return (index << PSEUDO_OFFSET_BITS) | diff;
#else
return 0;
#endif
}
void *PseudoAddressManager::GetAllocationBase(void *ptr)
{
#if defined PLATFORM_WINDOWS
MEMORY_BASIC_INFORMATION info;
if (!VirtualQuery(ptr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
return nullptr;
return info.AllocationBase;
#elif defined PLATFORM_APPLE
#ifdef PLATFORM_X86
typedef vm_region_info_t mach_vm_region_info_t;
typedef vm_region_basic_info_data_t mach_vm_region_basic_info_data_t;
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO;
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT;
#define mach_vm_region vm_region
#elif defined PLATFORM_X64
typedef vm_region_info_64_t mach_vm_region_info_t ;
typedef vm_region_basic_info_data_64_t mach_vm_region_basic_info_data_t;
const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO_64;
const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT_64;
#define mach_vm_region vm_region_64
#endif
vm_size_t size;
vm_address_t vmaddr = reinterpret_cast<vm_address_t>(ptr);
mach_vm_region_basic_info_data_t info;
memory_object_name_t obj;
vm_region_flavor_t flavor = MACH_VM_REGION_BASIC_INFO;
mach_msg_type_number_t count = MACH_VM_REGION_BASIC_INFO_COUNT;
kern_return_t kr = mach_vm_region(mach_task_self(), &vmaddr, &size, flavor,
reinterpret_cast<mach_vm_region_info_t>(&info),
&count, &obj);
if (kr != KERN_SUCCESS)
return nullptr;
return reinterpret_cast<void *>(vmaddr);
#elif defined PLATFORM_LINUX
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
// Format:
// lower upper prot stuff path
// 08048000-0804c000 r-xp 00000000 03:03 1010107 /bin/cat
FILE *fp = fopen("/proc/self/maps", "r");
if (fp) {
uintptr_t lower, upper;
while (fscanf(fp, "%" PRIxPTR "-%" PRIxPTR, &lower, &upper) != EOF) {
if (addr >= lower && addr <= upper) {
fclose(fp);
return reinterpret_cast<void *>(lower);
}
// Read to end of line
int c;
while ((c = fgetc(fp)) != '\n') {
if (c == EOF)
break;
}
if (c == EOF)
break;
}
fclose(fp);
}
return nullptr;
#endif
}
+51
View File
@@ -0,0 +1,51 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2017 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>.
*/
#ifndef _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_
#define _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_
#include "common_logic.h"
class PseudoAddressManager
{
public:
PseudoAddressManager();
public:
void *FromPseudoAddress(uint32_t paddr);
uint32_t ToPseudoAddress(void *addr);
private:
void *GetAllocationBase(void *ptr);
private:
static constexpr uint8_t PSEUDO_OFFSET_BITS = 26;
static constexpr uint8_t PSEUDO_INDEX_BITS = sizeof(uint32_t) * 8 - PSEUDO_OFFSET_BITS;
void *m_AllocBases[1 << PSEUDO_INDEX_BITS];
uint8_t m_NumEntries;
};
#endif // _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_
+7 -3
View File
@@ -166,7 +166,7 @@ void RootConsoleMenu::DrawGenericOption(const char *cmd, const char *text)
{
buffer[len++] = ' ';
}
len += snprintf(&buffer[len], sizeof(buffer) - len, " - %s", text);
len += ke::SafeSprintf(&buffer[len], sizeof(buffer) - len, " - %s", text);
ConsolePrint("%s", buffer);
}
}
@@ -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)
{
@@ -256,7 +260,7 @@ static bool sm_dump_handles(int client, const ICommandArgs *args)
auto write_handles_to_game = [] (const char *str) -> void
{
char buffer[1024];
size_t len = ke::SafeSprintf(buffer, sizeof(buffer)-2, "%s", str);
size_t len = ke::SafeStrcpy(buffer, sizeof(buffer)-2, str);
buffer[len] = '\n';
buffer[len+1] = '\0';
+4
View File
@@ -46,6 +46,10 @@ struct ConsoleEntry
{
return strcmp(name, entry->command.c_str()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
};
class RootConsoleMenu :
+42 -23
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;
@@ -162,18 +165,16 @@ bool ShareSystem::RequestInterface(const char *iface_name,
SMInterface **pIface)
{
/* See if the interface exists */
List<IfaceInfo>::iterator iter;
SMInterface *iface;
IExtension *iface_owner;
IExtension *iface_owner = nullptr;
bool found = false;
for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++)
for (auto iter = m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++)
{
IfaceInfo &info = (*iter);
IfaceInfo &info = *iter;
iface = info.iface;
if (strcmp(iface->GetInterfaceName(), iface_name) == 0)
{
if (iface->GetInterfaceVersion() == iface_vers
|| iface->IsVersionCompatible(iface_vers))
if (iface->GetInterfaceVersion() == iface_vers || iface->IsVersionCompatible(iface_vers))
{
iface_owner = info.owner;
found = true;
@@ -363,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)
@@ -381,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);
@@ -402,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();
+5 -3
View File
@@ -49,9 +49,11 @@ 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;
};
};
+2 -2
View File
@@ -144,7 +144,7 @@ SMCError TextParsers::ParseSMCFile(const char *file,
fclose(fp);
errstr = GetSMCErrorString(result);
ke::SafeSprintf(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error");
ke::SafeStrcpy(buffer, maxsize, errstr != NULL ? errstr : "Unknown error");
return result;
}
@@ -195,7 +195,7 @@ SMCError TextParsers::ParseSMCStream(const char *stream,
result = ParseStream_SMC(&rs, RawStreamReader, smc_listener, states);
const char *errstr = GetSMCErrorString(result);
ke::SafeSprintf(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error");
ke::SafeStrcpy(buffer, maxsize, errstr != NULL ? errstr : "Unknown error");
return result;
}
+509 -8
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
{
+2 -38
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

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