Compare commits

..

177 Commits

Author SHA1 Message Date
David Anderson
0c16c6ebe9 Bump version from 1.3.4-dev to release. 2010-07-17 22:42:57 -07:00
David Anderson
1b9ce5f484 Updated changelog for 1.3.4. 2010-07-17 22:41:35 -07:00
David Anderson
8a0a911d43 Fixed bogus assert about name lengths (bug 4486, r=fyren). 2010-07-02 18:12:31 -07:00
Scott Ehlert
fae6673957 Added MSVC10 project file for sample extension (bug 4521, r+a13=dvander). 2010-07-15 15:21:43 -05:00
Scott Ehlert
63f50b1e73 Oh, fixed Linux build. 2010-07-15 01:04:52 -05:00
Nicholas Hastings
f53746f720 Added forward/detour for TF2's GetHoliday function (bug 4462, r=pred, a13=dvander). 2010-07-15 00:58:42 -05:00
Scott Ehlert
5dd18eeaad Appended OS names to package archives (bug 4395, r+a13=dvander). 2010-07-14 23:45:01 -05:00
Scott Ehlert
7316b6255c Updated docs on replicate and notify params for SetConVar* and ResetConVar (bug 4517, r=pred, a13=dvander). 2010-07-14 20:29:53 -05:00
Scott Ehlert
eb2605f1a3 Fixed replicate and notify params on SetConVar* and ResetConVar which didn't work on old engine games (bug 4516, r=pred, a13=dvander).
This also fixes an issue where clients could be notified of convar changes twice on newer engines (Orange Box and later).
2010-07-14 20:29:45 -05:00
Scott Ehlert
01a3a09d96 Made name param of GetConVarName() non-const (bug 4515, r=pred, a13=dvander). 2010-07-14 20:29:29 -05:00
Scott Ehlert
01e7fb463b Triggering a build to compile against L4D2 SDK changes (bug 4509). 2010-07-11 03:06:00 -05:00
Nicholas Hastings
c4390478fc Fixed TF2 sigs that broke with recent update (bug 4506, r=fyren) 2010-07-08 21:11:39 -04:00
Scott Ehlert
7d97de8a10 Bleh, fixed compiler error in console.inc caused by previous changeset. 2010-07-07 02:29:23 -05:00
Scott Ehlert
51bee0f420 Added GetConVarDefault() native (bug 4502, r+a13=dvander). 2010-07-07 02:10:57 -05:00
Nicholas Hastings
04642a7cc0 Gmod gamedata update (bug 4489, r=fyren) 2010-07-06 07:31:58 -04:00
Nicholas Hastings
f8f6f2cf6c Merge. 2010-06-27 19:24:28 -04:00
_KaszpiR_
99ae90ecc5 Added FindFlagChar native. (bug 3776, r=dvander) 2010-06-27 19:23:50 -04:00
David Anderson
d64125ddd4 Added tag sourcemod-1.3.3 for changeset c5062ca82830 2010-06-24 01:42:39 -07:00
David Anderson
7fb3632fd7 Bumped version to 1.3.4-dev. 2010-06-24 01:25:25 -07:00
David Anderson
49e2303a5b Bumped version to 1.3.3 for release. 2010-06-23 23:44:14 -07:00
David Anderson
fe0b9f7320 Updated changelog. 2010-06-23 23:43:09 -07:00
David Anderson
b16c758f88 Removed code now dead from CS:S Orange Box move (bug 4399, r+a13=ds). 2010-06-23 23:35:28 -07:00
Nicholas Hastings
da729c3c08 Removed cstrike ep1 and cstrike_beta builds and references. (bug 4399, r=dvander) 2010-06-23 23:53:16 -04:00
Erik Minekus
f1bcb2ad3e Fixed lack of error checking on sm_motd. (bug 4460, r=dvander) 2010-06-23 23:42:22 -04:00
Nicholas Hastings
9270399eb1 Forcing (re)build(?) since Windows had a strange fail. 2010-06-23 10:33:51 -04:00
Nicholas Hastings
76e6edbad4 Cstrike ext now recognizes and loads on cstrike_beta (bug 4399, r=fyren) 2010-06-23 10:12:55 -04:00
devicenull
58ca1508bc Fixed inaccuracy in CommandListener documentation 2010-06-23 09:23:23 -04:00
Greyscale
eed1df28ec Removed unnecessary tag from return value of SetArrayCell 2010-06-23 09:17:53 -04:00
Nicholas Hastings
a602d7d107 Ep2v cstrike ext now gets added to package. 2010-06-23 09:02:04 -04:00
Nicholas Hastings
9a08986fd1 Silly AMBuild 2010-06-23 08:32:25 -04:00
Nicholas Hastings
5d1820853a Adding building of cstrike ext for ep2v (bug 4399, r=fyren) 2010-06-23 08:29:15 -04:00
Nicholas Hastings
753aced2a3 Added missing TF2 critcola condflag define (bug 4464, r=fyren) 2010-06-22 19:31:33 -04:00
Nicholas Hastings
df47457321 Updated TF2 'Burn' signature (bug 4463, r=fyren) 2010-06-22 19:26:07 -04:00
Fyren
d9e818e5d9 Possible dynamic array dimension fix (bug 4428, r=dvander) 2010-06-19 15:17:10 -07:00
Nicholas Hastings
47c840c9b4 Fixed outputname null pointer crash in sdktools (bug 4422, r=fyren) 2010-06-14 09:32:50 -04:00
David Anderson
2bfc5d3e1e Fixed switch case comparisons failing with large numbers (bug 4457, r=pred). 2010-06-13 20:26:58 -07:00
Nicholas Hastings
4307c57697 Added missing FireOutput and PlayerRunCmd gamedata for zombie_master (bug 4452, r=fyren) 2010-06-07 19:39:15 -04:00
Nicholas Hastings
bff0faff52 Added new player condition (TFCond_CritCola) to tf2.inc. (bug 4447, r=fyren) 2010-06-06 20:55:25 -04:00
Matt Woodrow
749966ae66 Fixed clientprefs deleting cookies twice in rare cases (bug 4412, r=Fyren) 2010-06-07 10:30:33 +12:00
Nicholas Hastings
9f11132fb5 Fixed OnClientConnected forward not firing for bots (bug 4443, r=fyren) 2010-06-06 17:53:38 -04:00
Fyren
2dfbd1fc1f Fix sdktools voice memory corruption (bug 4415, r=dvander) 2010-06-05 23:21:33 -07:00
Nicholas Hastings
7c659837bf Updated Obsidian Conflict gamedata (bug 4439, r=fyren) 2010-06-02 07:54:15 -04:00
Nicholas Hastings
7cf342a1a7 Fixed tf2.inc incorrectly attempting to load tf2 ext on all games (bug 4197, r=fyren) 2010-05-31 16:36:10 -04:00
Scott Ehlert
e8d6aab989 Triggering build. 2010-05-29 00:55:06 -05:00
Nicholas Hastings
0819278465 Updated/fixed SourceForts gamedata (bug 4429, r=fyren) 2010-05-28 13:29:13 -04:00
Nicholas Hastings
d89c57ba79 Fixed SDKTools sm_dump_datamaps crash on improperly created entities (bug 4424, r=pred) 2010-05-26 08:31:48 -04:00
Nicholas Hastings
23b10fc559 Added gamedata for Resistance and Liberation (bug 4418, r=fyren) 2010-05-21 14:04:19 -04:00
Nicholas Hastings
36e8e881e8 Empires gamedata fixes (bug 4417, r=fyren). 2010-05-21 13:55:29 -04:00
Nicholas Hastings
67011834df Updated gamedata for ep2v cstrike(_beta) (bug 4399, r=fyren) 2010-05-11 20:40:14 -04:00
Scott Ehlert
ed9f68814f Added missing -dev back to product.version. 2010-05-13 09:35:30 -05:00
Nicholas Hastings
79a8008280 Fixed TF2 natives not being marked optional when ext not required (bug 4389, r=fyren) 2010-05-10 18:18:34 -04:00
Nicholas Hastings
643dce8cb1 Updated Empires gamedata (bug 4390, r=fyren) 2010-05-08 21:39:50 -04:00
Scott Ehlert
3669b98986 Fixed SendConVarValue() on TF2, DOD:S, L4D, and L4D2 (bug 4273, r=dvander, a=blocking). 2010-05-04 22:49:46 -04:00
David Anderson
e075b7b9b2 Bumped version to 1.3.3-dev. 2010-05-02 14:41:51 -07:00
David Anderson
b95fda8342 Added tag sourcemod-1.3.2 for changeset a3e8f7a7fdf7 2010-05-02 14:39:43 -07:00
David Anderson
5abec70579 Bumped for release. 2010-05-02 14:16:34 -07:00
Nicholas Hastings
8800d5311c Added TF2_StunPlayer native and TF2_GetPlayerConditionFlags stock to TF2 ext. (bug 4331, r=pred) 2010-03-31 08:51:25 -04:00
Nicholas Hastings
3705210879 Added 4 new natives to TF2 ext. (bug 4166, r=pred)
Added natives for Regenerate, add/remove condition, and SetPlayerPowerPlay. Also updated TF2 test plugin.
2010-03-18 11:35:05 -04:00
Scott Ehlert
5f4befcb6a Argh, really fixed HookEntityOutput() crash on TF2 (bug 4375). 2010-04-30 10:54:34 -04:00
Scott Ehlert
314011f118 Fixed HookEntityOutput() crash with recent updates to TF2 and L4D2 (bug 4375, r=pred, a=blocking). 2010-04-30 03:51:01 -04:00
Fyren
a9b7a5dc5c Triggering build 2010-04-29 20:55:16 -07:00
Scott Ehlert
9b78c68b1b Updated sample extension makefiles to compile against latest changes for L4D2 and TF2 (bug 4374, r=dvander). 2010-04-29 16:04:47 -04:00
Nicholas Hastings
328109be1f Fixed SetUserCvar offset for ep2v (bug 4371, r=fyren) 2010-04-29 14:17:44 -04:00
Nicholas Hastings
aba0f29666 ep2v gamedata (bug 4371, r=fyren) 2010-04-28 23:48:26 -07:00
Fyren
bdefb58e5f ep2v build changes (bug 4371, r=dvander) 2010-04-28 19:44:20 -07:00
David Anderson
7ad3a57c47 Check return value of GetPlayerNetInfo (bug 4315, r=fyren). 2010-04-27 23:40:04 -07:00
Nicholas Hastings
5a2f3cf433 Fixed entity output hooks not working in Insurgency (bug 4365, r=fyren) 2010-04-24 21:10:15 -04:00
Nicholas Hastings
fffc27a348 Added obsidian to list of games which support tempents with ep2 gamedata (thread 75480, r=me). 2010-04-24 21:08:13 -04:00
Nicholas Hastings
cecb7cb6a2 Fixed functions.inc not having double-include prevention (bug 4342, r=fyren) 2010-04-24 21:05:48 -04:00
Zach Kanzler
f5cc5eb757 Fixed IGamePlayer->GetUserId() returning junk (bug 3916, r=fyren) 2010-04-24 21:02:40 -04:00
Nicholas Hastings
689406744b Updated L4D2 gamedata for The Passing update (bug 4361, r=pending) 2010-04-23 12:11:49 -04:00
Nicholas Hastings
603da899b7 Updated Insurgency gamedata for Insurgency 2010-04-19 update (bug 4356, r=fyren) 2010-04-23 10:38:37 -04:00
Fyren
9b4d1f6972 Build stuff for L4D2 DLC (bug 4361, r=what's a review?) 2010-04-23 07:33:58 -07:00
David Anderson
d56b222637 Merge backout. 2010-04-23 02:31:15 -04:00
David Anderson
dfefda81e1 Backed out changeset 6b5fa86c1a0d (bug 4315). 2010-04-23 02:31:07 -04:00
David Anderson
0c463b1f86 Merge. 2010-04-22 17:55:55 -04:00
David Anderson
bf345eea8c Attempting fix for SendConVarValue crash (bug 4315, r=fyren, a13=me). 2010-04-22 17:55:35 -04:00
Nicholas Hastings
5328447b74 Merge 2010-04-19 11:15:51 -04:00
Nicholas Hastings
db6881b7e3 Updated Empires gamedata for Empires 2.25a release (bug 4108, r=fyren) 2010-04-19 10:38:50 -04:00
David Anderson
3e96b1617a Added obsidian to list of games which need menu redraws (thread 123237, r=me). 2010-04-04 23:51:20 -07:00
Nicholas Hastings
7ef8b415bb Added Gamerules SDKCall support to CSPromod gamedata (bug 4320, r=pred) 2010-03-23 19:42:58 -04:00
Nicholas Hastings
9a5d6957b9 Fixed bad caller crash with HookEntityOutput (bug 4311, r=fyren) 2010-03-20 11:08:48 -04:00
Nicholas Hastings
b5a2ec7a5e Added missing core voting cvars to sourcemod.cfg (bug 4309, r=fyren) 2010-03-11 22:01:11 -05:00
Nicholas Hastings
373136af4e Added missing core voting cvars to sourcemod.cfg (bug 4224, r=pred) 2010-03-11 17:44:41 -05:00
Nicholas Hastings
8673c11341 Fixed incorrect DispatchSpawn signature for ZPS and added ShowMenu and Hudtext support (bug 4279, r=Fyren) 2010-03-09 20:40:06 -05:00
Nicholas Hastings
8c082de148 Re-enabled ShowMenu support for FortressForever (bug 4028, r=dvander) 2010-03-09 18:50:57 -05:00
Nicholas Hastings
e62d47a85e Fixed FireOutput signature for FortressForever (bug 4062, r=dvander) 2010-03-09 18:48:03 -05:00
Nicholas Hastings
2d32c587ba Fixed sm_rename on L4D2 on Windows (bug 4131, r=dvander) 2010-03-09 18:44:28 -05:00
Nicholas Hastings
31e29ee94c Fixed inaccurate description for sm_unban (bug 4245, r=dvander) 2010-03-09 18:41:43 -05:00
Nicholas Hastings
e95e453258 Fixed CSPromod SDKTools Support (bug 4255, r=dvander) 2010-03-09 18:36:51 -05:00
Nicholas Hastings
48b0f61551 Updated PVKII support for PVKII 2.3 (bug 4276, r=dvander) 2010-03-09 18:30:15 -05:00
Nicholas Hastings
ef88fc21a1 Fixed CreateGameRulesObject sig for ZPS (bug 4285, r=dvander). 2010-03-09 18:15:23 -05:00
Erik Minekus
558c8c9745 Fixed Insurgency gamedata file (bug 4282, r+a13=dvander). 2010-02-24 23:41:06 -08:00
Fyren
2f60680cfb maxClients -> MaxClients in sdktools_sound.inc (fix bug 3985, r=psychonicsux) 2010-02-10 10:53:54 -08:00
David Anderson
1ffcf7a664 Bumped version to 1.3.2-dev. 2010-02-09 22:00:28 -08:00
David Anderson
c00b688ab7 Added tag sourcemod-1.3.1 for changeset 3f33d01fcc28 2010-02-09 21:59:41 -08:00
David Anderson
4169e25451 Removed -dev tag for release. 2010-02-09 21:12:39 -08:00
David Anderson
85fc65bae5 Updated changelog for 1.3.1. 2010-02-08 20:58:15 -08:00
Nicholas Hastings
aed62d5670 Added missing file for Battlegrounds support (bug 4146). 2010-02-08 20:47:51 -08:00
Nicholas Hastings
01d46b8c9c Added SDKTools support for CSProMod (bug 4255, r+a13=dvander) 2010-02-08 20:25:11 -08:00
Nicholas Hastings
f70b48276e Fixed base plugins using GetMaxClients() instead of MaxClients (bug 3985, r+a13=dvander). 2010-02-08 20:14:52 -08:00
Nicholas Hastings
f0070e45a0 Fixed CreateEntityByName for Insurgency (bug 4180, r+a13=dvander). 2010-02-08 20:10:24 -08:00
Nicholas Hastings
11782cee90 Updated Garry's Mod offsets (bug 4230, r+a13=dvander). 2010-02-08 20:08:14 -08:00
Nicholas Hastings
24b86584a2 Fixed nextmap errors on Garry's Mod (bug 4268, r+a13=dvander). 2010-02-08 20:06:48 -08:00
Fyren
01b3eee1b8 Compiler confused |any| tag with |String| on array arguments (bug 4274, r=dvander, a13=blocking). 2010-02-08 20:00:06 -08:00
David Anderson
2fb3f8ab0f Fixed GetClientCookieTime not being exported (bug 4237, r=fyren, a13=blocking). 2010-02-08 19:58:36 -08:00
Nicholas Hastings
2c7b0e04e9 Fixed FindEntityByClassname crash in Synergy (bug 4235, r=dvander, a13=blocking). 2010-02-08 19:57:20 -08:00
Fyren
9f3e9a2ff4 Fixed NULL-deref in entity handling code (bug 4249, r=dvander, a13=blocking). 2010-02-08 19:55:14 -08:00
Nicholas Hastings
d9d5b65f2c Fixed Day of Defeat offsets for 20100203 update (bug 4262, r=dvander, a13=blocking). 2010-02-04 19:28:06 -06:00
Scott Ehlert
90cc2d6c37 Webternet and Updater extensions now load on Windows 2000 (bug 4234, r+a13=dvander). 2010-01-20 15:11:13 -06:00
David Anderson
1ab5403f9f Fixed accidental hook against garbage offset (bug 4231, r=fyren, a13=blocking). 2010-01-20 04:03:46 -08:00
David Anderson
4d03a4ef60 Added PlayerRunCmd offset for DoD:s (bug 4231 mitigation, r=me). 2010-01-20 04:01:39 -08:00
Scott Ehlert
46b714108a Updated .hgignore for Explorer files beginning with a lowercase letter (argh!) 2010-01-20 02:39:52 -06:00
David Anderson
242e1c99e2 Bumped version to 1.3.1-dev. 2010-01-14 23:34:20 -08:00
David Anderson
3d0e661cd6 Added tag sourcemod-1.3.0 for changeset a71318396392 2010-01-14 23:33:20 -08:00
David Anderson
d076aca9ea Forgotten file needed version bump. 2010-01-14 22:26:57 -08:00
David Anderson
f824a53f21 Another credits update. 2010-01-14 03:34:26 -08:00
David Anderson
87ca5e6e05 Prep for release. 2010-01-14 03:30:20 -08:00
David Anderson
23af5357c0 Updated credits for 1.3 release. 2010-01-14 03:29:16 -08:00
David Anderson
d6b82d5dcc Updated changelog. 2010-01-14 03:26:16 -08:00
David Anderson
664da97c42 Registering "sm" logs an error, but will not throw. 2010-01-14 01:11:38 -08:00
Greyscale
b6507b12d4 Fixed missing #endif in adminmenu.inc (bug 4216, r+a13=dvander). 2010-01-14 00:22:45 -08:00
David Anderson
a5ab381c0a Fixed command listener spew on server shutdown (bug 4198, r=fyren, a13=blocking). 2010-01-13 21:48:51 -08:00
David Anderson
8ec5e42c7a Merge. 2010-01-11 20:10:57 -08:00
David Anderson
006423dcf2 Triggering build. 2010-01-11 20:10:38 -08:00
Nicholas Hastings
d0f4d943b6 Added missing SDK define for L4D2 (bug 4209, r+a13=dvander). 2010-01-10 17:38:03 -08:00
Nicholas Hastings
2fd9674e09 Follow-up to bug 4194 (r=dvander). 2010-01-10 17:29:32 -08:00
David Anderson
23fca5e645 Merge. 2010-01-10 17:17:22 -08:00
David Anderson
4beb7fe869 Backed out changeset f8861500d3bc (bug 4211). 2010-01-10 17:17:16 -08:00
David Anderson
2e832f2d01 Fixed reentrancy problems with iterators, forwards, and function removals (bug
4059, r=fyren, a13=blocking).
2010-01-10 16:58:17 -08:00
Fyren
43602da743 Fixed GuessSDKVersion() not working on ep2v (bug 4194, r=dvander, a13=blocking). 2010-01-09 11:33:19 -08:00
Nicholas Hastings
763b73b9c5 Fixed SetUserCvar (sm_rename) on TF/DoD Linux (bug 4196, r=dvander, a13=blocking). 2010-01-07 01:26:01 -08:00
Nicholas Hastings
ffb6e84b45 Add Insurgency to list of games that support HUD messages (bug 4136, r+a13=dvander). 2009-12-19 20:42:32 -08:00
David Anderson
025f216e09 Fixed crashes from rare plugin loading errors (bug 4153, r=ds). 2009-12-19 18:19:28 -08:00
David Anderson
603ac75a08 Fixed SourceMod crashing after flushing the profiler (bug 4149, r=ds). 2009-12-19 18:18:17 -08:00
David Anderson
1b07722848 Add ability to change dontBroadcast status on hooked events (bug 3886, r=ds). 2009-12-19 18:16:41 -08:00
Nicholas Hastings
4de8d67e27 Another TF2 gamedata fix (bug 4151, r=dvander). 2009-12-19 12:57:26 -08:00
Nicholas Hastings
83baabe00a Battlegrounds 2 gamedata support (bug 4146, r+a13=dvander). 2009-12-19 00:03:57 -08:00
Nicholas Hastings
3080ca1ccb TF2 gamedata updates (bug 4147, r=dvander). 2009-12-18 22:01:41 -08:00
David Anderson
c3b0f8a4d2 Fixed infinite loop in Windows build failure path (bug 4144, r=ds). 2009-12-14 23:07:54 -08:00
David Anderson
726ee43736 Fixed profiler being set to NULL (bug 4143, r=fyren). 2009-12-14 23:08:15 -08:00
Fyren
8e1f37a5d2 Improve/fix GetEntity (bug 4092, r=pred) 2009-12-15 23:13:53 -08:00
Dog
ce342ef6fc Fixed CommitSuicide offset on DoD:S (bug 4109, r=dvander). 2009-12-14 20:26:45 -08:00
Nicholas Hastings
0a6acda737 Fixed CreateEntityByName for Insurgency (bug 4046, r+a13=dvander). 2009-12-14 20:31:16 -08:00
Nicholas Hastings
c907c723c3 Updated Synergy gamedata (bug 3987, r=dvander). 2009-12-14 20:30:44 -08:00
Nicholas Hastings
479815911d Added HudText support for Zombie Master (bug 4004, r+a13=dvander). 2009-12-14 20:30:04 -08:00
Nicholas Hastings
29954ec122 Added Neotokyo support (bug 4126, r+a13=dvander). 2009-12-14 20:29:23 -08:00
David Anderson
75455b65da Fixed basecomm description spelling (bug 4112, r=me). 2009-12-14 20:27:26 -08:00
Fyren
5688eb55ec Fix wonky handling of path separators for "sm plugins" commands (bug 3857, r=dvander) 2009-11-23 02:27:24 -08:00
Scott Ehlert
2e4aae95db Updated .hgignore to ignore some Windows and OS X files. 2009-11-22 01:39:29 -06:00
Scott Ehlert
cc43b63508 Updated sample_ext and mms_sample_ext for Orange Box Valve and Left 4 Dead 2 (bug 4118, r=pred). 2009-11-22 01:33:08 -06:00
David Anderson
59593a2d5f Use GetVSPInfo() from MM:S 1.8 legacy API (bug 4081, r=ds). 2009-11-20 15:18:58 -06:00
Fyren
e3f5d5b6cf Fix leak in nominations plugin (bug 4104, r=pred) 2009-11-18 15:44:51 -08:00
Fyren
6085125367 Fix rare basevotes map vote problem (bug 3905, r=dvander) 2009-11-18 15:42:21 -08:00
Scott Ehlert
27805463e3 Added IMemoryUtils::ResolveSymbol() for resolving symbols hidden via GCC's -fvisibility=hidden (bug 4091, r=dvander,pred).
Right now, this is only used on L4D2 where it is needed.
2009-11-06 16:24:40 -06:00
David Anderson
45f2b9f8ca Clarified handle invariants in clientprefs.inc (no bug, r=me). 2009-11-04 13:52:12 -05:00
Fyren
2bd979d62e Don't call edict_t::StateChanged(ushort) on L4D2 (bug 4090, r=dvander) 2009-11-01 02:53:34 -08:00
Scott Ehlert
e434c43e28 Use SOURCE_ENGINE_ORANGEBOXVALVE constant from MM:S 1.8 (bug 4087, r=dvander). 2009-10-30 14:04:43 -05:00
Fyren
02922df1cd More L4D2 gamedata changes 2009-10-30 01:24:55 -07:00
Scott Ehlert
035875f5c1 Fixed Windows build, I think. 2009-10-30 00:03:54 -05:00
Fyren
e3a3dda1d8 Typo fix 2009-10-29 20:34:00 -07:00
Scott Ehlert
7187dde64d Changed name in credits. 2009-10-29 17:35:00 -05:00
Matt Woodrow
fd09d7b176 New sdk uses _linux for vstdlib/tier0 2009-10-30 15:20:48 +13:00
Matt Woodrow
4e8fc2749b AMBuild updated to use new MM:S version 2009-10-30 14:58:17 +13:00
Fyren
8b362700ff Updated GuessSDKVersion 2009-10-30 14:34:34 +13:00
Matt Woodrow
e926a3b94b SourceMod MSVC project files updated, windows tested and loading, gamedata fix. 2009-10-30 14:29:45 +13:00
Matt Woodrow
83d1fc9abe L4D2 gamedata updates 2009-10-30 13:27:05 +13:00
Matt Woodrow
d6a5d99e55 AMBuild updated for new L4D2 engine 2009-10-30 13:27:19 +13:00
Fyren
6322a711c2 Inital SM fixes for l4d2 and linux makefile updates 2009-10-30 13:26:13 +13:00
David Anderson
e024871b50 Use GetSourceEngineBuild() from MM:S 1.8 legacy API (bug 4083, r=ds). 2009-10-29 01:33:57 -07:00
David Anderson
b4e939e273 Let SourceMod use OnUnlinkConCommandBase from MM:S 1.8 legacy API (bug 4080, r=ds). 2009-10-29 00:53:31 -07:00
David Anderson
13743376ee Build against MM:S 1.8 with core-legacy (bug 4076, r=ds). 2009-10-28 23:30:52 -07:00
David Anderson
69cfd36054 Fix iloop race when using AddCommandListener and SourceMod unloads (bug 4077, r=pred). 2009-10-29 00:07:15 -07:00
David Anderson
7d15ec4d15 Fixed crash when activating command listeners on EP1 while inside a command callback (bug 4072, r=pred). 2009-10-28 00:50:48 -07:00
David Anderson
b52a10f28b Added feature testing functionality (bug 4021, r=pred). 2009-10-28 00:37:34 -07:00
David Anderson
da7c65f0e3 Fix maphistory command not returning a value (bug 4069, r=pred, a13=blocking). 2009-10-28 01:55:42 -07:00
David Anderson
a5d2945fc5 Added @spec target (bug 2718, r=fyren). 2009-10-27 03:41:36 -07:00
1502 changed files with 282003 additions and 501008 deletions

View File

@ -1,5 +0,0 @@
{
"project_id" : "sourcemod",
"conduit_uri" : "https://phabricator.alliedmods.net/",
"history.immutable" : false
}

12
.gitattributes vendored
View File

@ -1,12 +0,0 @@
# 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

View File

@ -1,74 +0,0 @@
# Contributing to SourceMod
## Issue reports
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). 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 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 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.
* What is your end goal, or what are you trying to accomplish?
* Why is this necessary, or what benefit do you see with it?
* Will this be useful to others?
#### Issues with security implications
Please report any security bugs to [security@alliedmods.net](mailto:security@alliedmods.net) rather than to this public issue tracker.
#### We're only human
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 contributing changes to the project!
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 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.*
> If you send us a patch without coordinating it with us first, it will probably be immediately rejected, or sit in limbo for a long time and eventually be rejected. The reasons we do this vary from patch to patch, but some of the most common reasons are:
>
> **Unjustifiable Costs**: We support code in the upstream forever. Support is enormously expensive and takes up a huge amount of our time. The cost to support a change over its lifetime is often 10x or 100x or 1000x greater than the cost to write the first version of it. Many uncoordinated patches we receive are "white elephants", which would cost much more to maintain than the value they provide.
>
> As an author, it may look like you're giving us free work and we're rejecting it as too expensive, but this viewpoint doesn't align with the reality of a large project which is actively supported by a small, experienced team. Writing code is cheap; maintaining it is expensive.
>
> By coordinating with us first, you can make sure the patch is something we consider valuable enough to put long-term support resources behind, and that you're building it in a way that we're comfortable taking over.
>
> **Not a Good Fit**: Many patches aren't good fits for the upstream: they implement features we simply don't want. You can find more information in Contributing Feature Requests. Coordinating with us first helps make sure we're on the same page and interested in a feature.
>
> The most common type of patch along these lines is a patch which adds new configuration options. We consider additional configuration options to have an exceptionally high lifetime support cost and are very unlikely to accept them. Coordinate with us first.
>
> **Not a Priority**: If you send us a patch against something which isn't a priority, we probably won't have time to look at it. We don't give special treatment to low-priority issues just because there's code written: we'd still be spending time on something lower-priority when we could be spending it on something higher-priority instead.
>
> If you coordinate with us first, you can make sure your patch is in an area of the codebase that we can prioritize.
>
> **Overly Ambitious Patches**: Sometimes we'll get huge patches from new contributors. These can have a lot of fundamental problems and require a huge amount of our time to review and correct. If you're interested in contributing, you'll have more success if you start small and learn as you go.
>
> We can help you break a large change into smaller pieces and learn how the codebase works as you proceed through the implementation, but only if you coordinate with us first.
>
> **Generality**: We often receive several feature requests which ask for similar features, and can come up with a general approach which covers all of the use cases. If you send us a patch for your use case only, the approach may be too specific. When a cleaner and more general approach is available, we usually prefer to pursue it.
>
> By coordinating with us first, we can make you aware of similar use cases and opportunities to generalize an approach. These changes are often small, but can have a big impact on how useful a piece of code is.
>
> **Infrastructure and Sequencing**: Sometimes patches are written against a piece of infrastructure with major planned changes. We don't want to accept these because they'll make the infrastructure changes more difficult to implement.
>
> Coordinate with us first to make sure a change doesn't need to wait on other pieces of infrastructure. We can help you identify technical blockers and possibly guide you through resolving them if you're interested.

8
.github/FUNDING.yml vendored
View File

@ -1,8 +0,0 @@
# 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

View File

@ -1,26 +0,0 @@
# 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.

38
.gitignore vendored
View File

@ -1,38 +0,0 @@
# Binaries
*.dll
*.dylib
*.exe
*.so
# Files generated by Visual Studio
*.aps
*.ipch
*.ncb
*.opensdf
*.sdf
*.suo
*.user
# Build directories
CrazyDebug*/
Debug*/
Release*/
LIB-Release/
LIB-Debug/
# Files generated by Mac OS X Finder
.DS_Store
# Files generated by Windows Explorer
[Dd]esktop.ini
[Tt]humbs.db
# AMBuild build directories
build/
obj-*/
*~
*.rej
*.orig
*.smx
*.swp
*.gdb_history

6
.gitmodules vendored
View File

@ -1,6 +0,0 @@
[submodule "public/amtl"]
path = public/amtl
url = https://github.com/alliedmodders/amtl
[submodule "sourcepawn"]
path = sourcepawn
url = https://github.com/BotoX/sourcepawn.git

23
.hgignore Normal file
View File

@ -0,0 +1,23 @@
# Binaries
\.dll$
\.exe$
\.so$
# Files generated by Visual Studio
\.aps$
\.ncb$
\.sdf$
\.suo$
\.user$
# Build directories
/CrazyDebug.*
/Debug.*
/Release.*
# Files generated by Mac OS X Finder
(^|/)\.DS_Store$
# Files generated by Windows Explorer
(^|/)[dD]esktop\.ini$
(^|/)[tT]humbs\.db$

9
.hgtags Normal file
View File

@ -0,0 +1,9 @@
54539fa4e51f98d7841c853c1370dff6ccd3cdf2 sourcemod-1.0.0
e6ef5ecdf8d75740ca2685a709bf321f8873bc3b sourcemod-1.1.0
e877885fac80be71822641f7a9122cebc9812521 sourcemod-1.1.1
b3ffa8a4511c4eadaf533fc790aa6b14f7f0c6ea sourcemod-1.1.2
3a73bbf60f34befa9b66be03fa5974b394bb3411 sourcemod-1.2.0
a71318396392e3c6d0ff31e069a60fbde59e0cea sourcemod-1.3.0
3f33d01fcc28b509bed30f7be07ed215cc000538 sourcemod-1.3.1
a3e8f7a7fdf7f012dd02ad491a2e1ccdad151687 sourcemod-1.3.2
c5062ca8283033cf33f45da6f19e486ca10835ff sourcemod-1.3.3

View File

@ -1,118 +0,0 @@
git:
depth: 3
sudo: false
language: cpp
os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-3.9
- llvm-toolchain-trusty-4.0
- llvm-toolchain-trusty-5.0
packages:
- lib32stdc++6
- lib32z1-dev
- libc6-dev-i386
- linux-libc-dev
- g++-multilib
# - clang-3.6
# - clang-3.8
# - clang-4.0
# - clang-5.0
# - g++-6
# - g++-6-multilib
- clang-3.9
- g++-4.8-multilib
- g++-4.8
- g++-4.9-multilib
- g++-4.9
- g++-5-multilib
- g++-5
- g++-7-multilib
- g++-7
cache:
directories:
- ../mysql-5.0
env:
- MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9"
- MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8"
- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9"
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
matrix:
fast_finish: true
include:
- os: linux
sudo: false
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"']
- os: linux
sudo: false
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"']
- os: linux
sudo: false
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"
before_script:
- CHECKOUT_DIR=$PWD && cd .. && $CHECKOUT_DIR/tools/checkout-deps.sh && cd $CHECKOUT_DIR
script:
- mkdir build && cd build
- PATH="~/.local/bin:$PATH"
- eval "${MATRIX_EVAL}"
- python ../configure.py --enable-optimize --sdks=episode1,css,tf2,l4d2,csgo,dota
- ambuild

View File

@ -1,671 +1,311 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: # vim: set ts=2 sw=2 tw=99 noet ft=python:
import os, sys import os
import sys
from ambuild.command import SymlinkCommand
class SDK(object): class SM:
def __init__(self, sdk, ext, aDef, name, platform, dir): def __init__(self):
self.folder = 'hl2sdk-' + dir self.compiler = Cpp.Compiler()
self.envvar = sdk
self.ext = ext
self.code = aDef
self.define = name
self.platform = platform
self.name = dir
self.path = None # Actual path
self.platformSpec = platform
# By default, nothing supports x64. #Build SDK info
if type(platform) is list: self.sdkInfo = { }
self.platformSpec = {p: ['x86'] for p in platform} self.sdkInfo['ep1'] = {'sdk': 'HL2SDK', 'ext': '1.ep1', 'def': '1',
else: 'name': 'EPISODEONE'}
self.platformSpec = platform self.sdkInfo['ep2'] = {'sdk': 'HL2SDKOB', 'ext': '2.ep2', 'def': '3',
'name': 'ORANGEBOX'}
self.sdkInfo['ep2v'] = {'sdk': 'HL2SDKOBVALVE', 'ext': '2.ep2v', 'def': '4',
'name': 'ORANGEBOXVALVE'}
self.sdkInfo['l4d'] = {'sdk': 'HL2SDKL4D', 'ext': '2.l4d', 'def': '5',
'name': 'LEFT4DEAD'}
self.sdkInfo['l4d2'] = {'sdk': 'HL2SDKL4D2', 'ext': '2.l4d2', 'def': '6',
'name': 'LEFT4DEAD2'}
if AMBuild.target['platform'] == 'windows':
self.sdkInfo['darkm'] = {'sdk': 'HL2SDK-DARKM', 'ext': '2.darkm', 'def': '2',
'name': 'DARKMESSIAH'}
def shouldBuild(self, target, archs): if AMBuild.mode == 'config':
if target.platform not in self.platformSpec: #Detect compilers
return False self.compiler.DetectAll(AMBuild)
if not len([i for i in self.platformSpec[target.platform] if i in archs]):
return False
return True
WinOnly = ['windows'] #Detect variables
WinLinux = ['windows', 'linux'] envvars = { 'MMSOURCE18': 'mmsource-1.8',
WinLinuxMac = ['windows', 'linux', 'mac'] 'HL2SDK': 'hl2sdk',
CSGO = { 'HL2SDKOB': 'hl2sdk-ob',
'windows': ['x86'], 'HL2SDKL4D': 'hl2sdk-l4d',
'linux': ['x86', 'x64'], 'HL2SDKOBVALVE': 'hl2sdk-ob-valve',
'mac': ['x64'] 'HL2SDKL4D2': 'hl2sdk-l4d2',
'MYSQL5': 'mysql-5.0'
}
#Dark Messiah is Windows-only
if AMBuild.target['platform'] == 'windows':
envvars['HL2SDK-DARKM'] = 'hl2sdk-darkm'
#Must have a path for each envvar (file a bug if you don't like this)
for i in envvars:
if i in os.environ:
path = os.environ[i]
if not os.path.isdir(path):
raise Exception('Path for {0} was not found: {1}'.format(i, path))
else:
head = os.getcwd()
oldhead = None
while head != None and head != oldhead:
path = os.path.join(head, envvars[i])
if os.path.isdir(path):
break
oldhead = head
head, tail = os.path.split(head)
if head == None or head == oldhead:
raise Exception('Could not find a valid path for {0}'.format(i))
AMBuild.cache.CacheVariable(i, path)
#Set up defines
cxx = self.compiler.cxx
if isinstance(cxx, Cpp.GCC):
self.vendor = 'gcc'
self.compiler.AddToListVar('CDEFINES', 'stricmp=strcasecmp')
self.compiler.AddToListVar('CDEFINES', '_stricmp=strcasecmp')
self.compiler.AddToListVar('CDEFINES', '_snprintf=snprintf')
self.compiler.AddToListVar('CDEFINES', '_vsnprintf=vsnprintf')
self.compiler.AddToListVar('CFLAGS', '-pipe')
self.compiler.AddToListVar('CFLAGS', '-fno-strict-aliasing')
if cxx.majorVersion >= 4:
self.compiler.AddToListVar('CFLAGS', '-fvisibility=hidden')
self.compiler.AddToListVar('CXXFLAGS', '-fvisibility-inlines-hidden')
self.compiler.AddToListVar('CFLAGS', '-Wall')
self.compiler.AddToListVar('CFLAGS', '-Werror')
self.compiler.AddToListVar('CFLAGS', '-Wno-uninitialized')
self.compiler.AddToListVar('CFLAGS', '-Wno-unused')
self.compiler.AddToListVar('CFLAGS', '-Wno-switch')
self.compiler.AddToListVar('CFLAGS', '-mfpmath=sse')
self.compiler.AddToListVar('CFLAGS', '-msse')
self.compiler.AddToListVar('CFLAGS', '-m32')
self.compiler.AddToListVar('CFLAGS', '-static-libgcc')
self.compiler.AddToListVar('CXXFLAGS', '-fno-exceptions')
self.compiler.AddToListVar('CXXFLAGS', '-fno-rtti')
self.compiler.AddToListVar('CXXFLAGS', '-fno-threadsafe-statics')
self.compiler.AddToListVar('CXXFLAGS', '-Wno-non-virtual-dtor')
self.compiler.AddToListVar('CDEFINES', 'HAVE_STDINT_H')
elif isinstance(cxx, Cpp.MSVC):
self.vendor = 'msvc'
if AMBuild.options.debug == '1':
self.compiler.AddToListVar('CFLAGS', '/MTd')
else:
self.compiler.AddToListVar('CFLAGS', '/MT')
self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_DEPRECATE')
self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_WARNINGS')
self.compiler.AddToListVar('CDEFINES', '_CRT_NONSTDC_NO_DEPRECATE')
self.compiler.AddToListVar('CXXFLAGS', '/EHsc')
self.compiler.AddToListVar('CXXFLAGS', '/GR-')
self.compiler.AddToListVar('CFLAGS', '/W3')
self.compiler.AddToListVar('CFLAGS', '/nologo')
self.compiler.AddToListVar('CFLAGS', '/Zi')
self.compiler.AddToListVar('CXXFLAGS', '/TP')
self.compiler.AddToListVar('POSTLINKFLAGS', '/DEBUG')
self.compiler.AddToListVar('POSTLINKFLAGS', '/MACHINE:X86')
self.compiler.AddToListVar('POSTLINKFLAGS', '/SUBSYSTEM:WINDOWS')
self.compiler.AddToListVar('POSTLINKFLAGS', 'kernel32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'user32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'gdi32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'winspool.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'comdlg32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'advapi32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'shell32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'ole32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'oleaut32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'uuid.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'odbc32.lib')
self.compiler.AddToListVar('POSTLINKFLAGS', 'odbccp32.lib')
#Optimization
if AMBuild.options.opt == '1':
self.compiler.AddToListVar('CDEFINES', 'NDEBUG')
if self.vendor == 'gcc':
self.compiler.AddToListVar('CFLAGS', '-O3')
elif self.vendor == 'msvc':
self.compiler.AddToListVar('CFLAGS', '/Ot')
self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:ICF')
#Debugging
if AMBuild.options.debug == '1':
self.compiler.AddToListVar('CDEFINES', 'DEBUG')
self.compiler.AddToListVar('CDEFINES', '_DEBUG')
if self.vendor == 'gcc':
self.compiler.AddToListVar('CFLAGS', '-g3')
elif self.vendor == 'msvc':
self.compiler.AddToListVar('CFLAGS', '/Od')
self.compiler.AddToListVar('CFLAGS', '/RTC1')
#Platform-specifics
if AMBuild.target['platform'] == 'linux':
self.compiler.AddToListVar('CDEFINES', '_LINUX')
elif AMBuild.target['platform'] == 'windows':
self.compiler.AddToListVar('CDEFINES', 'WIN32')
self.compiler.AddToListVar('CDEFINES', '_WINDOWS')
#Finish up
self.compiler.AddToListVar('CDEFINES', 'SOURCEMOD_BUILD')
self.compiler.AddToListVar('CDEFINES', 'SM_GENERATED_BUILD')
self.compiler.AddToListVar('CINCLUDES',
os.path.join(AMBuild.outputFolder, 'includes'))
self.compiler.ToConfig(AMBuild, 'compiler')
AMBuild.cache.CacheVariable('vendor', self.vendor)
self.targetMap = { }
AMBuild.cache.CacheVariable('targetMap', self.targetMap)
else:
self.compiler.FromConfig(AMBuild, 'compiler')
self.targetMap = AMBuild.cache['targetMap']
if AMBuild.target['platform'] == 'windows':
self.compiler.AddToListVar('RCINCLUDES', os.path.join(AMBuild.sourceFolder, 'public'))
self.compiler.AddToListVar('RCINCLUDES',
os.path.join(AMBuild.outputFolder, 'includes'))
self.mmsPath = AMBuild.cache['MMSOURCE18']
def DefaultCompiler(self):
return self.compiler.Clone()
def JobMatters(self, jobname):
file = sys._getframe().f_code.co_filename
if AMBuild.mode == 'config':
self.targetMap[jobname] = file
return True
if len(AMBuild.args) == 0:
return True
if not jobname in AMBuild.args:
return False
def DefaultExtCompiler(self, path):
compiler = self.DefaultCompiler()
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, path))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, path, 'sdk'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public', 'extensions'))
compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'public', 'sourcepawn'))
return compiler
def AutoVersion(self, folder, binary):
if AMBuild.target['platform'] != 'windows':
return
env = {'RCDEFINES': ['BINARY_NAME="' + binary.binaryFile + '"', 'SM_GENERATED_BUILD']}
binary.AddResourceFile(os.path.join(folder, 'version.rc' ), env)
def PreSetupHL2Job(self, job, builder, sdk):
info = self.sdkInfo[sdk]
sdkPath = AMBuild.cache[info['sdk']]
if AMBuild.target['platform'] == 'linux':
if sdk == 'ep1':
staticLibs = os.path.join(sdkPath, 'linux_sdk')
else:
staticLibs = os.path.join(sdkPath, 'lib', 'linux')
workFolder = os.path.join(AMBuild.outputFolder, job.workFolder)
if sdk == 'ep2v' or sdk == 'l4d2':
for i in ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.so', 'libtier0.so']:
link = os.path.join(workFolder, i)
target = os.path.join(staticLibs, i)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
else:
for i in ['tier1_i486.a', 'mathlib_i486.a', 'vstdlib_i486.so', 'tier0_i486.so']:
link = os.path.join(workFolder, i)
target = os.path.join(staticLibs, i)
try:
os.lstat(link)
except:
job.AddCommand(SymlinkCommand(link, target))
elif AMBuild.target['platform'] == 'windows':
for lib in ['tier0', 'tier1', 'vstdlib', 'mathlib']:
libPath = os.path.join(sdkPath, 'lib', 'public', lib) + '.lib'
builder.RebuildIfNewer(libPath)
builder['POSTLINKFLAGS'].append(libPath)
def PostSetupHL2Job(self, job, builder, sdk):
if AMBuild.target['platform'] == 'linux':
builder.AddObjectFiles(['tier1_i486.a', 'mathlib_i486.a'])
def DefaultHL2Compiler(self, path, sdk, noLink = False, oldMms = '-legacy'):
compiler = self.DefaultExtCompiler(path)
mms = 'core'
if sdk == 'ep1':
mms += oldMms
compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms))
compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms, 'sourcehook'))
info = self.sdkInfo
compiler['CDEFINES'].extend(['SE_' + info[i]['name'] + '=' + info[i]['def'] for i in info])
compiler['CDEFINES'].append('SE_DARKMESSIAH=2')
paths = [['public'], ['public', 'engine'], ['public', 'mathlib'], ['public', 'vstdlib'],
['public', 'tier0'], ['public', 'tier1']]
if sdk == 'ep1' or sdk == 'darkm':
paths.append(['public', 'dlls'])
paths.append(['game_shared'])
else:
paths.append(['public', 'game', 'server'])
paths.append(['game', 'shared'])
paths.append(['common'])
info = self.sdkInfo[sdk]
sdkPath = AMBuild.cache[info['sdk']]
compiler['CDEFINES'].append('SOURCE_ENGINE=' + info['def'])
if sdk == 'ep1':
if AMBuild.target['platform'] == 'linux':
staticLibs = os.path.join(sdkPath, 'linux_sdk')
else:
if AMBuild.target['platform'] == 'linux':
staticLibs = os.path.join(sdkPath, 'lib', 'linux')
for i in paths:
compiler['CXXINCLUDES'].append(os.path.join(sdkPath, *i))
if not noLink:
if AMBuild.target['platform'] == 'linux':
compiler['POSTLINKFLAGS'][0:0] = ['-lm']
if sdk == 'ep2v' or sdk == 'l4d2':
compiler['POSTLINKFLAGS'][0:0] = ['libtier0.so']
compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.so']
else:
compiler['POSTLINKFLAGS'][0:0] = ['tier0_i486.so']
compiler['POSTLINKFLAGS'][0:0] = ['vstdlib_i486.so']
return compiler
sm = SM()
globals = {
'SM': sm
} }
PossibleSDKs = { AMBuild.Include(os.path.join('tools', 'buildbot', 'Versioning'), globals)
'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'), FileList = [
'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'), ['loader', 'AMBuilder'],
'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'), ['core', 'AMBuilder'],
'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'), ['core', 'logic', 'AMBuilder'],
'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'), ['extensions', 'bintools', 'AMBuilder'],
'tf2': SDK('HL2SDKTF2', '2.tf2', '11', 'TF2', WinLinuxMac, 'tf2'), ['extensions', 'clientprefs', 'AMBuilder'],
'l4d': SDK('HL2SDKL4D', '2.l4d', '12', 'LEFT4DEAD', WinLinuxMac, 'l4d'), ['extensions', 'cstrike', 'AMBuilder'],
'nucleardawn': SDK('HL2SDKND', '2.nd', '13', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'), ['extensions', 'curl', 'AMBuilder'],
'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '15', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'), ['extensions', 'geoip', 'AMBuilder'],
'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'), ['extensions', 'mysql', 'AMBuilder'],
'swarm': SDK('HL2SDK-SWARM', '2.swarm', '16', 'ALIENSWARM', WinOnly, 'swarm'), ['extensions', 'sdktools', 'AMBuilder'],
'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), ['extensions', 'topmenus', 'AMBuilder'],
'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), ['extensions', 'updater', 'AMBuilder'],
'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', CSGO, 'csgo'), ['extensions', 'sqlite', 'AMBuilder'],
'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'), ['extensions', 'regex', 'AMBuilder'],
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'), ['extensions', 'tf2', 'AMBuilder'],
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'), ['sourcepawn', 'jit', 'AMBuilder'],
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'), ['sourcepawn', 'compiler', 'AMBuilder'],
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'), ['plugins', 'AMBuilder'],
'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'), ['tools', 'buildbot', 'PackageScript']
} ]
for parts in FileList:
AMBuild.Include(os.path.join(*parts), globals)
def ResolveEnvPath(env, folder):
if env in os.environ:
path = os.environ[env]
if os.path.isdir(path):
return path
return None
head = os.getcwd()
oldhead = None
while head != None and head != oldhead:
path = os.path.join(head, folder)
if os.path.isdir(path):
return path
oldhead = head
head, tail = os.path.split(head)
return None
def Normalize(path):
return os.path.abspath(os.path.normpath(path))
def SetArchFlags(compiler, arch, platform):
if compiler.behavior == 'gcc':
if arch == 'x86':
compiler.cflags += ['-m32']
compiler.linkflags += ['-m32']
if platform == 'mac':
compiler.linkflags += ['-arch', 'i386']
elif arch == 'x64':
compiler.cflags += ['-m64', '-fPIC']
compiler.linkflags += ['-m64']
if platform == 'mac':
compiler.linkflags += ['-arch', 'x86_64']
elif compiler.like('msvc'):
if builder.target.arch == 'x86':
compiler.linkflags += ['/MACHINE:X86']
elif builder.target.arch == 'x64':
compiler.linkflags += ['/MACHINE:X64']
def AppendArchSuffix(binary, name, arch):
if arch == 'x64':
binary.localFolder = name + '.x64'
class SMConfig(object):
def __init__(self):
self.sdks = {}
self.binaries = []
self.extensions = []
self.generated_headers = None
self.mms_root = None
self.mysql_root = {}
self.spcomp = None
self.spcomp_bins = None
self.smx_files = {}
self.versionlib = None
self.archs = builder.target.arch.replace('x86_64', 'x64').split(',')
def use_auto_versioning(self):
if builder.backend != 'amb2':
return False
return not getattr(builder.options, 'disable_auto_versioning', False)
@property
def tag(self):
if builder.options.debug == '1':
return 'Debug'
return 'Release'
def detectProductVersion(self):
builder.AddConfigureFile('product.version')
# For OS X dylib versioning
import re
with open(os.path.join(builder.sourcePath, 'product.version'), 'r') as fp:
productContents = fp.read()
m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents)
if m == None:
self.productVersion = '1.0.0'
else:
major, minor, release = m.groups()
self.productVersion = '{0}.{1}.{2}'.format(major, minor, release)
def detectSDKs(self):
sdk_list = builder.options.sdks.split(',')
use_all = sdk_list[0] == 'all'
use_present = sdk_list[0] == 'present'
for sdk_name in PossibleSDKs:
sdk = PossibleSDKs[sdk_name]
if sdk.shouldBuild(builder.target, self.archs):
if builder.options.hl2sdk_root:
sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder)
else:
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
if sdk_path is None or not os.path.isdir(sdk_path):
if use_all or sdk_name in sdk_list:
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
continue
if use_all or use_present or sdk_name in sdk_list:
sdk.path = Normalize(sdk_path)
self.sdks[sdk_name] = sdk
if len(self.sdks) < 1 and len(sdk_list):
raise Exception('No SDKs were found that build on {0}-{1}, nothing to do.'.format(
builder.target.platform, builder.target.arch))
if builder.options.mms_path:
self.mms_root = builder.options.mms_path
else:
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:
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central')
if not self.mms_root or not os.path.isdir(self.mms_root):
raise Exception('Could not find a source copy of Metamod:Source')
self.mms_root = Normalize(self.mms_root)
if builder.options.hasMySql:
if builder.options.mysql_path:
self.mysql_root['x86'] = builder.options.mysql_path
else:
for i in range(10):
self.mysql_root['x86'] = ResolveEnvPath('MYSQL55', 'mysql-5.' + str(i))
if self.mysql_root['x86']:
break
if not self.mysql_root['x86'] or not os.path.isdir(self.mysql_root['x86']):
raise Exception('Could not find a path to MySQL!')
self.mysql_root['x86'] = Normalize(self.mysql_root['x86'])
# For now, ignore 64-bit MySQL on Windows
if 'x64' in self.archs and builder.target.platform != 'windows':
if builder.options.mysql64_path:
self.mysql_root['x64'] = builder.options.mysql64_path
else:
for i in range(10):
self.mysql_root['x64'] = ResolveEnvPath('MYSQL55_64', 'mysql-5.' + str(i) + '-x86_64')
if self.mysql_root['x64']:
break
if not self.mysql_root['x64'] or not os.path.isdir(self.mysql_root['x64']):
raise Exception('Could not find a path to 64-bit MySQL!')
self.mysql_root['x64'] = Normalize(self.mysql_root['x64'])
def configure(self):
builder.AddConfigureFile('pushbuild.txt')
if not set(self.archs).issubset(['x86', 'x64']):
raise Exception('Unknown target architecture: {0}'.format(builder.target.arch))
cxx = builder.DetectCxx()
if cxx.like('msvc') and len(self.archs) > 1:
raise Exception('Building multiple archs with MSVC is not currently supported')
if cxx.like('gcc'):
self.configure_gcc(cxx)
elif cxx.family == 'msvc':
self.configure_msvc(cxx)
# Optimizaiton
if builder.options.opt == '1':
cxx.defines += ['NDEBUG']
# Debugging
if builder.options.debug == '1':
cxx.defines += ['DEBUG', '_DEBUG']
# Platform-specifics
if builder.target.platform == 'linux':
self.configure_linux(cxx)
elif builder.target.platform == 'mac':
self.configure_mac(cxx)
elif builder.target.platform == 'windows':
self.configure_windows(cxx)
# Finish up.
cxx.defines += [
'SOURCEMOD_BUILD',
'SM_USE_VERSIONLIB',
]
cxx.includes += [
os.path.join(builder.sourcePath, 'public'),
]
if self.use_auto_versioning():
cxx.defines += ['SM_GENERATED_BUILD']
cxx.includes += [
os.path.join(builder.buildPath, 'includes'),
os.path.join(builder.sourcePath, 'versionlib'),
]
def configure_gcc(self, cxx):
cxx.defines += [
'stricmp=strcasecmp',
'_stricmp=strcasecmp',
'_snprintf=snprintf',
'_vsnprintf=vsnprintf',
'HAVE_STDINT_H',
'GNUC',
]
cxx.cflags += [
'-pipe',
'-fno-strict-aliasing',
'-Wall',
'-Werror',
'-Wno-unused',
'-Wno-switch',
'-Wno-array-bounds',
'-msse',
'-fvisibility=hidden',
]
cxx.cxxflags += [
'-std=c++11',
'-fno-exceptions',
'-fno-threadsafe-statics',
'-Wno-non-virtual-dtor',
'-Wno-overloaded-virtual',
'-fvisibility-inlines-hidden',
]
have_gcc = cxx.family == 'gcc'
have_clang = cxx.family == 'clang'
if cxx.version >= 'clang-3.9':
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':
cxx.cxxflags += ['-Wno-inconsistent-missing-override']
if cxx.version >= 'clang-2.9' or cxx.version >= 'apple-clang-3.0':
cxx.cxxflags += ['-Wno-null-dereference']
if have_clang or (cxx.version >= 'gcc-4.6'):
cxx.cflags += ['-Wno-narrowing']
if have_clang or (cxx.version >= 'gcc-4.7'):
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':
cxx.cxxflags += ['-Wno-deprecated-register']
else:
cxx.cxxflags += ['-Wno-deprecated']
cxx.cflags += ['-Wno-sometimes-uninitialized']
if have_gcc:
cxx.cflags += ['-mfpmath=sse']
cxx.cflags += ['-Wno-maybe-uninitialized']
if builder.options.opt == '1':
cxx.cflags += ['-O3']
def configure_msvc(self, cxx):
if builder.options.debug == '1':
cxx.cflags += ['/MTd']
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
else:
cxx.cflags += ['/MT']
cxx.defines += [
'_CRT_SECURE_NO_DEPRECATE',
'_CRT_SECURE_NO_WARNINGS',
'_CRT_NONSTDC_NO_DEPRECATE',
'_ITERATOR_DEBUG_LEVEL=0',
]
cxx.cflags += [
'/W3',
]
cxx.cxxflags += [
'/EHsc',
'/GR-',
'/TP',
]
cxx.linkflags += [
'kernel32.lib',
'user32.lib',
'gdi32.lib',
'winspool.lib',
'comdlg32.lib',
'advapi32.lib',
'shell32.lib',
'ole32.lib',
'oleaut32.lib',
'uuid.lib',
'odbc32.lib',
'odbccp32.lib',
]
if builder.options.opt == '1':
cxx.cflags += ['/Ox', '/Zo']
cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
if builder.options.debug == '1':
cxx.cflags += ['/Od', '/RTC1']
# This needs to be after our optimization flags which could otherwise disable it.
# Don't omit the frame pointer.
cxx.cflags += ['/Oy-']
def configure_linux(self, cxx):
cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
cxx.linkflags += ['-lm']
if cxx.family == 'gcc':
cxx.linkflags += ['-static-libgcc']
elif cxx.family == 'clang':
cxx.linkflags += ['-lgcc_eh']
def configure_mac(self, cxx):
cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL']
cxx.cflags += ['-mmacosx-version-min=10.5']
cxx.linkflags += [
'-mmacosx-version-min=10.5',
'-lstdc++',
'-stdlib=libstdc++',
]
cxx.cxxflags += ['-stdlib=libstdc++']
def configure_windows(self, cxx):
cxx.defines += ['WIN32', '_WINDOWS']
def AddVersioning(self, binary, arch):
if builder.target.platform == 'windows':
binary.sources += ['version.rc']
binary.compiler.rcdefines += [
'BINARY_NAME="{0}"'.format(binary.outputFile),
'RC_COMPILE',
]
if self.use_auto_versioning():
binary.compiler.rcdefines += ['SM_GENERATED_BUILD']
elif builder.target.platform == 'mac':
if binary.type == 'library':
binary.compiler.postlink += [
'-compatibility_version', '1.0.0',
'-current_version', self.productVersion
]
if self.use_auto_versioning():
binary.compiler.linkflags += [self.versionlib[arch]]
binary.compiler.sourcedeps += SM.generated_headers
return binary
def LibraryBuilder(self, compiler, name, arch):
binary = compiler.Library(name)
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
return binary
def ProgramBuilder(self, compiler, name, arch):
binary = compiler.Program(name)
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
if '-static-libgcc' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-static-libgcc')
if '-lgcc_eh' in binary.compiler.linkflags:
binary.compiler.linkflags.remove('-lgcc_eh')
if binary.compiler.like('gcc'):
binary.compiler.linkflags += ['-lstdc++', '-lpthread']
if binary.compiler.like('msvc'):
binary.compiler.linkflags += ['/SUBSYSTEM:CONSOLE']
return binary
def StaticLibraryBuilder(self, compiler, name, arch):
binary = compiler.StaticLibrary(name)
AppendArchSuffix(binary, name, arch)
return binary;
def Library(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.LibraryBuilder(compiler, name, arch)
def Program(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.ProgramBuilder(compiler, name, arch)
def StaticLibrary(self, context, name, arch):
compiler = context.cxx.clone()
SetArchFlags(compiler, arch, builder.target.platform)
return self.StaticLibraryBuilder(compiler, name, arch)
def ConfigureForExtension(self, context, compiler):
compiler.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(context.currentSourcePath, 'sdk'),
os.path.join(builder.sourcePath, 'public', 'extensions'),
os.path.join(builder.sourcePath, 'sourcepawn', 'include'),
os.path.join(builder.sourcePath, 'public', 'amtl', 'amtl'),
os.path.join(builder.sourcePath, 'public', 'amtl'),
]
return compiler
def ExtLibrary(self, context, name, arch):
binary = self.Library(context, name, arch)
self.ConfigureForExtension(context, binary.compiler)
return binary
def ConfigureForHL2(self, binary, sdk, arch):
compiler = binary.compiler
SetArchFlags(compiler, arch, builder.target.platform)
compiler.cxxincludes += [
os.path.join(self.mms_root, 'core'),
os.path.join(self.mms_root, 'core', 'sourcehook'),
]
defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs]
compiler.defines += defines
paths = [
['public'],
['public', 'engine'],
['public', 'mathlib'],
['public', 'vstdlib'],
['public', 'tier0'],
['public', 'tier1']
]
if sdk.name == 'episode1' or sdk.name == 'darkm':
paths.append(['public', 'dlls'])
paths.append(['game_shared'])
else:
paths.append(['public', 'game', 'server'])
paths.append(['public', 'toolframework'])
paths.append(['game', 'shared'])
paths.append(['common'])
compiler.defines += ['SOURCE_ENGINE=' + sdk.code]
if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'):
# The 2013 SDK already has these in public/tier0/basetypes.h
compiler.defines.remove('stricmp=strcasecmp')
compiler.defines.remove('_stricmp=strcasecmp')
compiler.defines.remove('_snprintf=snprintf')
compiler.defines.remove('_vsnprintf=vsnprintf')
if compiler.like('msvc'):
compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32']
if compiler.version >= 1900:
compiler.linkflags += ['legacy_stdio_definitions.lib']
else:
compiler.defines += ['COMPILER_GCC']
if arch == 'x64':
compiler.defines += ['X64BITS', 'PLATFORM_64BITS']
# For everything after Swarm, this needs to be defined for entity networking
# to work properly with sendprop value changes.
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
compiler.defines += ['NETWORK_VARS_ENABLED']
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2']:
if builder.target.platform in ['linux', 'mac']:
compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE']
if sdk.name == 'csgo' and builder.target.platform == 'linux':
compiler.linkflags += ['-lstdc++']
compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
if builder.target.platform == 'linux':
if sdk.name == 'episode1':
lib_folder = os.path.join(sdk.path, 'linux_sdk')
elif sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32')
elif arch == 'x64':
lib_folder = os.path.join(sdk.path, 'lib', 'linux64')
else:
lib_folder = os.path.join(sdk.path, 'lib', 'linux')
elif builder.target.platform == 'mac':
if sdk.name in ['sdk2013', 'bms']:
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32')
elif arch == 'x64':
lib_folder = os.path.join(sdk.path, 'lib', 'osx64')
else:
lib_folder = os.path.join(sdk.path, 'lib', 'mac')
if builder.target.platform in ['linux', 'mac']:
if sdk.name in ['sdk2013', 'bms'] or arch == 'x64':
compiler.postlink += [
compiler.Dep(os.path.join(lib_folder, 'tier1.a')),
compiler.Dep(os.path.join(lib_folder, 'mathlib.a'))
]
else:
compiler.postlink += [
compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a')),
compiler.Dep(os.path.join(lib_folder, 'mathlib_i486.a'))
]
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
if arch == 'x64':
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces.a'))]
else:
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))]
dynamic_libs = []
if builder.target.platform == 'linux':
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
elif arch == 'x64' and sdk.name == 'csgo':
dynamic_libs = ['libtier0_client.so', 'libvstdlib_client.so']
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
else:
dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so']
elif builder.target.platform == 'mac':
compiler.linkflags.append('-liconv')
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
elif builder.target.platform == 'windows':
libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
if sdk.name in ['swarm', 'blade', 'insurgency', 'doi', 'csgo']:
libs.append('interfaces')
for lib in libs:
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
compiler.linkflags.append(compiler.Dep(lib_path))
for library in dynamic_libs:
source_path = os.path.join(lib_folder, library)
output_path = os.path.join(binary.localFolder, library)
def make_linker(source_path, output_path):
def link(context, binary):
cmd_node, (output,) = context.AddSymlink(source_path, output_path)
return output
return link
linker = make_linker(source_path, output_path)
compiler.linkflags[0:0] = [compiler.Dep(library, linker)]
return binary
def HL2Library(self, context, name, sdk, arch):
binary = self.Library(context, name, arch)
self.ConfigureForExtension(context, binary.compiler)
return self.ConfigureForHL2(binary, sdk, arch)
def HL2Project(self, context, name):
project = context.cxx.LibraryProject(name)
self.ConfigureForExtension(context, project.compiler)
return project
def HL2Config(self, project, name, sdk, arch):
binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name))
AppendArchSuffix(binary, name, arch)
self.AddVersioning(binary, arch)
return self.ConfigureForHL2(binary, sdk, arch)
SM = SMConfig()
SM.detectProductVersion()
SM.detectSDKs()
SM.configure()
if SM.use_auto_versioning():
SM.generated_headers = builder.Build(
'tools/buildbot/Versioning',
{ 'SM': SM }
)
SM.versionlib = builder.Build(
'versionlib/AMBuilder',
{ 'SM': SM }
)
# Build SourcePawn externally.
SP = builder.Build('sourcepawn/AMBuildScript', {
'external_root': SM,
'external_amtl': os.path.join(builder.sourcePath, 'public', 'amtl'),
'external_build': ['core'],
})
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())
for arch in SM.archs:
SM.binaries += [
SP.libsourcepawn[arch]
]
BuildScripts = [
'loader/AMBuilder',
'core/AMBuilder',
'core/logic/AMBuilder',
'extensions/bintools/AMBuilder',
'extensions/clientprefs/AMBuilder',
'extensions/curl/AMBuilder',
'extensions/cstrike/AMBuilder',
'extensions/geoip/AMBuilder',
'extensions/mysql/AMBuilder',
'extensions/regex/AMBuilder',
'extensions/sdkhooks/AMBuilder',
'extensions/sdktools/AMBuilder',
'extensions/sqlite/AMBuilder',
'extensions/tf2/AMBuilder',
'extensions/topmenus/AMBuilder',
'extensions/updater/AMBuilder',
]
if builder.backend == 'amb2':
BuildScripts += [
'plugins/AMBuilder',
'tools/buildbot/PackageScript',
]
builder.Build(BuildScripts, { 'SM': SM })
if builder.options.breakpad_dump:
builder.Build('tools/buildbot/BreakpadSymbols', { 'SM': SM })

6
NOTICE.txt Normal file
View File

@ -0,0 +1,6 @@
CONFIGURE YOUR IDENTITY FOR MERCURIAL!
IF YOU ARE A DEVELOPER WITH PUSH ACCESS, DO IT NOW KPLZTHX.
http://wiki.alliedmods.net/Mercurial_Tutorial#Identity

View File

@ -1,27 +0,0 @@
SourceMod
=========
General
-------
- [SourceMod website](http://www.sourcemod.net): Source Engine scripting and server administration
- [Forum](https://forums.alliedmods.net/forumdisplay.php?f=52): Discussion forum including plugin/extension development
- [General documentation](https://wiki.alliedmods.net/Category:SourceMod_Documentation): Miscellaneous information about SourceMod
- [Stable builds](http://www.sourcemod.net/downloads.php?branch=stable): The latest stable SourceMod releases
- [Dev builds](http://www.sourcemod.net/downloads.php?branch=dev): Builds of recent development versions
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.

View File

@ -1,46 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_API_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_API_H_
#include <bridge/include/CoreProvider.h>
#include <bridge/include/LogicProvider.h>
#include <stdint.h>
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 - 57;
} // namespace SourceMod
typedef void (*LogicInitFunction)(SourceMod::CoreProvider *core, SourceMod::sm_logic_t *logic);
typedef LogicInitFunction (*LogicLoadFunction)(uint32_t magic);
typedef SourceMod::ITextParsers *(*GetITextParsers)();
#endif // _INCLUDE_SOURCEMOD_BRIDGE_API_H_

View File

@ -1,140 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_CORE_PROVIDER_API_H_
#define _INCLUDE_SOURCEMOD_CORE_PROVIDER_API_H_
#include <stddef.h>
#include <stdint.h>
#include <IAdminSystem.h>
#include <amtl/am-function.h>
namespace SourcePawn {
class ISourcePawnEngine;
class ISourcePawnEngine2;
} // namespace SourcePawn
// SDK types.
#if defined(SM_LOGIC)
class ConCommandBase {};
class ConVar : public ConCommandBase {};
#else
class ConCommandBase;
class ConVar;
#endif
class KeyValues;
struct ServerGlobals
{
const double *universalTime;
float *interval_per_tick;
float *frametime;
};
namespace SourceMod {
class ISourceMod;
class IVEngineServerBridge;
class IFileSystemBridge;
class ITimerSystem;
class IPlayerManager;
class IGameHelpers;
class IMenuManager;
struct DatabaseInfo;
class IPlayerInfoBridge;
class ICommandArgs;
typedef ke::Lambda<bool(int client, const ICommandArgs*)> CommandFunc;
class CoreProvider
{
public:
/* Objects */
ISourceMod *sm;
IVEngineServerBridge *engine;
IFileSystemBridge *filesystem;
IPlayerInfoBridge *playerInfo;
ITimerSystem *timersys;
IPlayerManager *playerhelpers;
IGameHelpers *gamehelpers;
IMenuManager *menus;
SourcePawn::ISourcePawnEngine **spe1;
SourcePawn::ISourcePawnEngine2 **spe2;
const char *gamesuffix;
/* Data */
ServerGlobals *serverGlobals;
void * serverFactory;
void * engineFactory;
void * matchmakingDSFactory;
SMGlobalClass * listeners;
// ConVar functions.
virtual ConVar *FindConVar(const char *name) = 0;
virtual const char *GetCvarString(ConVar *cvar) = 0;
virtual bool GetCvarBool(ConVar* cvar) = 0;
// Command functions.
virtual void DefineCommand(const char *cmd, const char *help, const CommandFunc &callback) = 0;
// Game description functions.
virtual bool GetGameName(char *buffer, size_t maxlength) = 0;
virtual const char *GetGameDescription() = 0;
virtual const char *GetSourceEngineName() = 0;
virtual bool SymbolsAreHidden() = 0;
// Game state and helper functions.
virtual bool IsMapLoading() = 0;
virtual bool IsMapRunning() = 0;
virtual int MaxClients() = 0;
virtual bool DescribePlayer(int index, const char **namep, const char **authp, int *useridp) = 0;
virtual void LogToGame(const char *message) = 0;
virtual void ConPrint(const char *message) = 0;
virtual void ConsolePrint(const char *fmt, ...) = 0;
virtual void ConsolePrintVa(const char *fmt, va_list ap) = 0;
// Game engine helper functions.
virtual bool IsClientConVarQueryingSupported() = 0;
virtual int QueryClientConVar(int client, const char *cvar) = 0;
// Metamod:Source functions.
virtual int LoadMMSPlugin(const char *file, bool *ok, char *error, size_t maxlength) = 0;
virtual void UnloadMMSPlugin(int id) = 0;
const char * (*GetCoreConfigValue)(const char*);
void (*DoGlobalPluginLoads)();
bool (*AreConfigsExecuted)();
void (*ExecuteConfigs)(IPluginContext *ctx);
void (*GetDBInfoFromKeyValues)(KeyValues *, DatabaseInfo *);
int (*GetActivityFlags)();
int (*GetImmunityMode)();
void (*UpdateAdminCmdFlags)(const char *cmd, OverrideType type, FlagBits bits, bool remove);
bool (*LookForCommandAdminFlags)(const char *cmd, FlagBits *pFlags);
int (*GetGlobalTarget)();
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_CORE_PROVIDER_API_H_

View File

@ -1,81 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_INCLUDE_IEXTBRIDGE_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IEXTBRIDGE_H_
#include <sp_vm_api.h>
#include <IExtensionSys.h>
#include <sh_vector.h>
struct edict_t;
namespace SourceMod {
using namespace SourceHook;
using namespace SourcePawn;
class SMPlugin;
class IExtensionSys : public IExtensionManager
{
public:
virtual IExtension *LoadAutoExtension(const char *name, bool bErrorOnMissing=true) = 0;
virtual void TryAutoload() = 0;
virtual void Shutdown() = 0;
virtual IExtension *FindExtensionByFile(const char *name) = 0;
virtual bool LibraryExists(const char *name) = 0;
virtual void CallOnCoreMapStart(edict_t *edictList, int edictCount, int maxClients) = 0;
virtual IExtension *GetExtensionFromIdent(IdentityToken_t *token) = 0;
virtual void BindChildPlugin(IExtension *ext, SMPlugin *plugin) = 0;
virtual void AddRawDependency(IExtension *myself, IdentityToken_t *token, void *iface) = 0;
virtual const CVector<IExtension *> *ListExtensions() = 0;
virtual void FreeExtensionList(const CVector<IExtension *> *list) = 0;
virtual void CallOnCoreMapEnd() = 0;
};
class AutoExtensionList
{
public:
AutoExtensionList(IExtensionSys *extensions)
: extensions_(extensions), list_(extensions_->ListExtensions())
{
}
~AutoExtensionList()
{
extensions_->FreeExtensionList(list_);
}
const CVector<IExtension *> *operator ->()
{
return list_;
}
private:
IExtensionSys *extensions_;
const CVector<IExtension *> *list_;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IEXTBRIDGE_H_

View File

@ -1,63 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_IFILESYSTEM_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_IFILESYSTEM_H_
typedef void * FileHandle_t;
typedef int FileFindHandle_t;
namespace SourceMod {
class IFileSystemBridge
{
public:
virtual const char *FindFirstEx(const char *pWildCard, const char *pPathID, FileFindHandle_t *pHandle) = 0;
virtual const char *FindNext(FileFindHandle_t handle) = 0;
virtual bool FindIsDirectory(FileFindHandle_t handle) = 0;
virtual void FindClose(FileFindHandle_t handle) = 0;
virtual FileHandle_t Open(const char *pFileName, const char *pOptions, const char *pathID = 0) = 0;
virtual void Close(FileHandle_t file) = 0;
virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file) = 0;
virtual bool EndOfFile(FileHandle_t file) = 0;
virtual bool FileExists(const char *pFileName, const char *pPathID = 0) = 0;
virtual unsigned int Size(const char *pFileName, const char *pPathID = 0) = 0;
virtual int Read(void* pOutput, int size, FileHandle_t file) = 0;
virtual int Write(void const* pInput, int size, FileHandle_t file) = 0;
virtual void Seek(FileHandle_t file, int post, int seekType) = 0;
virtual unsigned int Tell(FileHandle_t file) = 0;
virtual int FPrint(FileHandle_t file, const char *pData) = 0;
virtual void Flush(FileHandle_t file) = 0;
virtual bool IsOk(FileHandle_t file) = 0;
virtual void RemoveFile(const char *pRelativePath, const char *pathID = 0) = 0;
virtual void RenameFile(char const *pOldPath, char const *pNewPath, const char *pathID = 0) = 0;
virtual bool IsDirectory(const char *pFileName, const char *pathID = 0) = 0;
virtual void CreateDirHierarchy(const char *path, const char *pathID = 0) = 0;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_IFILESYSTEM_H_

View File

@ -1,43 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_LOGGER_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_LOGGER_H_
namespace SourceMod {
class ILogger
{
public:
virtual void LogMessage(const char *msg, ...) = 0;
virtual void LogError(const char *msg, ...) = 0;
virtual void LogFatal(const char *msg, ...) = 0;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_LOGGER_H_

View File

@ -1,55 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_INCLUDE_IPLAYERINFO_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IPLAYERINFO_H_
class IPlayerInfo;
namespace SourceMod {
class IPlayerInfoBridge
{
public:
virtual bool IsObserver(IPlayerInfo *pInfo) = 0;
virtual int GetTeamIndex(IPlayerInfo *pInfo) = 0;
virtual int GetFragCount(IPlayerInfo *pInfo) = 0;
virtual int GetDeathCount(IPlayerInfo *pInfo) = 0;
virtual int GetArmorValue(IPlayerInfo *pInfo) = 0;
virtual void GetAbsOrigin(IPlayerInfo *pInfo, float *x, float *y, float *z) = 0;
virtual void GetAbsAngles(IPlayerInfo *pInfo, float *x, float *y, float *z) = 0;
virtual void GetPlayerMins(IPlayerInfo *pInfo, float *x, float *y, float *z) = 0;
virtual void GetPlayerMaxs(IPlayerInfo *pInfo, float *x, float *y, float *z) = 0;
virtual const char *GetWeaponName(IPlayerInfo *pInfo) = 0;
virtual const char *GetModelName(IPlayerInfo *pInfo) = 0;
virtual int GetHealth(IPlayerInfo *pInfo) = 0;
virtual void ChangeTeam(IPlayerInfo *pInfo, int iTeamNum) = 0;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IPLAYERINFO_H_

View File

@ -1,45 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_INCLUDE_IPROVIDERCALLBACK_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IPROVIDERCALLBACK_H_
namespace SourceMod {
// Global callbacks provided to Core.
class IProviderCallbacks
{
public:
// Called when a log message is printed. Return true to supercede.
virtual bool OnLogPrint(const char *msg) = 0;
// Called each frame tick.
virtual void OnThink(bool simulating) = 0;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_IPROVIDERCALLBACK_H_

View File

@ -1,112 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_INCLUDE_ISCRIPTMANAGER_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_ISCRIPTMANAGER_H_
#include <sp_vm_api.h>
#include <IPluginSys.h>
#include <sh_vector.h>
#include <sh_string.h>
namespace SourceMod {
using namespace SourceHook;
using namespace SourcePawn;
class IChangeableForward;
enum LibraryAction
{
LibraryAction_Removed,
LibraryAction_Added
};
struct AutoConfig
{
SourceHook::String autocfg;
SourceHook::String folder;
bool create;
};
class SMPlugin : public IPlugin
{
public:
virtual size_t GetConfigCount() = 0;
virtual AutoConfig *GetConfig(size_t i) = 0;
virtual void AddLibrary(const char *name) = 0;
virtual void AddConfig(bool create, const char *cfg, const char *folder) = 0;
virtual void EvictWithError(PluginStatus status, const char *fmt, ...) = 0;
};
class IScriptManager
{
public:
virtual void LoadAll(const char *config_path, const char *plugins_path) = 0;
virtual void RefreshAll() = 0;
virtual void Shutdown() = 0;
virtual IdentityToken_t *GetIdentity() = 0;
virtual void SyncMaxClients(int maxClients) = 0;
virtual void AddPluginsListener(IPluginsListener *listener) = 0;
virtual void RemovePluginsListener(IPluginsListener *listener) = 0;
virtual IPluginIterator *GetPluginIterator() = 0;
virtual void OnLibraryAction(const char *name, LibraryAction action) = 0;
virtual bool LibraryExists(const char *name) = 0;
virtual SMPlugin *FindPluginByOrder(unsigned num) = 0;
virtual SMPlugin *FindPluginByIdentity(IdentityToken_t *ident) = 0;
virtual SMPlugin *FindPluginByContext(IPluginContext *ctx) = 0;
virtual SMPlugin *FindPluginByContext(sp_context_t *ctx) = 0;
virtual SMPlugin *FindPluginByConsoleArg(const char *text) = 0;
virtual SMPlugin *FindPluginByHandle(Handle_t hndl, HandleError *errp) = 0;
virtual bool UnloadPlugin(IPlugin *plugin) = 0;
virtual const CVector<SMPlugin *> *ListPlugins() = 0;
virtual void FreePluginList(const CVector<SMPlugin *> *list) = 0;
virtual void AddFunctionsToForward(const char *name, IChangeableForward *fwd) = 0;
};
class AutoPluginList
{
public:
AutoPluginList(IScriptManager *scripts)
: scripts_(scripts), list_(scripts->ListPlugins())
{
}
~AutoPluginList()
{
scripts_->FreePluginList(list_);
}
const CVector<SMPlugin *> *operator ->()
{
return list_;
}
private:
IScriptManager *scripts_;
const CVector<SMPlugin *> *list_;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_INCLUDE_ISCRIPTMANAGER_H_

View File

@ -1,48 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_BRIDGE_IVENGINESERVER_H_
#define _INCLUDE_SOURCEMOD_BRIDGE_IVENGINESERVER_H_
struct edict_t;
namespace SourceMod {
class IVEngineServerBridge
{
public:
virtual bool IsDedicatedServer() = 0;
virtual void InsertServerCommand(const char *cmd) = 0;
virtual void ServerCommand(const char *cmd) = 0;
virtual void ServerExecute() = 0;
virtual const char *GetClientConVarValue(int clientIndex, const char *name) = 0;
virtual void ClientCommand(edict_t *pEdict, const char *szCommand) = 0;
virtual void FakeClientCommand(edict_t *pEdict, const char *szCommand) = 0;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_BRIDGE_IVENGINESERVER_H_

View File

@ -1,92 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet:
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_LOGIC_PROVIDER_API_H_
#define _INCLUDE_SOURCEMOD_LOGIC_PROVIDER_API_H_
#include <sp_vm_api.h>
class SMGlobalClass;
namespace SourceMod {
using namespace SourcePawn;
class CoreProvider;
class IThreader;
class ITranslator;
class IGameConfig;
class IScriptManager;
class IShareSys;
class IHandleSys;
class ICommandArgs;
class IForwardManager;
class IAdminSystem;
class IRootConsole;
class IProviderCallbacks;
class IExtensionSys;
class ITextParsers;
class ILogger;
class ICellArray;
struct sm_logic_t
{
SMGlobalClass *head;
IThreader *threader;
ITranslator *translator;
const char *(*stristr)(const char *, const char *);
size_t (*atcprintf)(char *, size_t, const char *, IPluginContext *, const cell_t *, int *);
bool (*CoreTranslate)(char *, size_t, const char *, unsigned int, size_t *, ...);
void (*AddCorePhraseFile)(const char *filename);
unsigned int (*ReplaceAll)(char*, size_t, const char *, const char *, bool);
char *(*ReplaceEx)(char *, size_t, const char *, size_t, const char *, size_t, bool);
size_t (*DecodeHexString)(unsigned char *, size_t, const char *);
IGameConfig * (*GetCoreGameConfig)();
IDebugListener *debugger;
void (*GenerateError)(IPluginContext *, cell_t, int, const char *, ...);
void (*AddNatives)(sp_nativeinfo_t *natives);
void (*RegisterProfiler)(IProfilingTool *tool);
ICellArray * (*CreateCellArray)(size_t blocksize);
void (*FreeCellArray)(ICellArray *arr);
void * (*FromPseudoAddress)(uint32_t pseudoAddr);
uint32_t (*ToPseudoAddress)(void *addr);
IScriptManager *scripts;
IShareSys *sharesys;
IExtensionSys *extsys;
IHandleSys *handlesys;
IForwardManager *forwardsys;
IAdminSystem *adminsys;
IdentityToken_t *core_ident;
ILogger *logger;
IRootConsole *rootmenu;
IProviderCallbacks *callbacks;
float sentinel;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_LOGIC_PROVIDER_API_H_

View File

@ -2,47 +2,6 @@ SourceMod Changelog
---------------------------- ----------------------------
SourceMod 1.3.6 [2010-10-31]
URL: http://wiki.alliedmods.net/SourceMod_1.3.6_Release_Notes
User Changes:
- Updated support for latest Left 4 Dead 1 (bug 4681).
Developer Changes:
- Updated TF2 custom kill defines (bug 4682).
----------------------------
SourceMod 1.3.5 [2010-10-17]
URL: http://wiki.alliedmods.net/SourceMod_1.3.5_Release_Notes
User Changes:
- Updated support for CS:S, Garry's Mod, DoD:S, TF2, HL2DM.
- Added support for Firearms: Source (bug 4537).
- Added support for Fistful of Frags (bug 3883).
- Added support for Alien Swarm (bug 4530).
- Fixed living players hearing dead players if alltalk is disabled while deadtalk is on (bug 4533, thanks Zach Callear).
- Fixed timeleft not getting reset on Game_Commencing in CS:S (bug 4557).
- Fixed PlayerRunCmd being hooked when not used (bug 3990).
- Fixed errors and missing effects caused by missing sprites on L4D2 (bug 4512).
- Fixed 'sm plugins' public command skipping plugins in recommended listing (bug 4501).
- Fixed corruption with large database.cfg files (bug 4286).
Developer Changes:
- Added TF2 object stocks (bug 4536).
- Added TF2_MakeBleed native to TF2 ext (bug 4542).
- Added support for logical entities in Synergy (bug 4534).
- Updated TF2 conditions enum and defines (bug 4526).
- Fixed potential crash with IMemUtils::FindPattern on Linux (bug 4554).
----------------------------
SourceMod 1.3.4 [2010-07-18] SourceMod 1.3.4 [2010-07-18]
URL: http://wiki.alliedmods.net/SourceMod_1.3.4_Release_Notes URL: http://wiki.alliedmods.net/SourceMod_1.3.4_Release_Notes

View File

@ -1,16 +0,0 @@
"banreasons"
{
"Abusive" "Abusive"
"Racism" "Racism"
"General cheating/exploits" "General cheating/exploits"
"Wallhack" "Wallhack"
"Aimbot" "Aimbot"
"Speedhacking" "Speedhacking"
"Mic spamming" "Mic spamming"
"Admin disrespect" "Admin disrespect"
"Camping" "Camping"
"Team killing" "Team killing"
"Unacceptable Spray" "Unacceptable Spray"
"Breaking Server Rules" "Breaking Server Rules"
"Other" "Other"
}

View File

@ -65,10 +65,6 @@ sm_flood_time 0.75
// no reserved slot access (spectator players are selected first) is kicked to make room. Thus, the reserved // no reserved slot access (spectator players are selected first) is kicked to make room. Thus, the reserved
// slots always remains free. The only situation where the reserved slot(s) can become properly occupied is // slots always remains free. The only situation where the reserved slot(s) can become properly occupied is
// if the server is full with reserve slot access clients. // if the server is full with reserve slot access clients.
// 2 : The same as sm_reserve_type 1 except once a certain number of admins have been reached, the reserve slot
// stops kicking people and anyone can join to fill the server. You can use this to simulate having a large
// number of reserved slots with sm_reserve_type 0 but with only need to have 1 slot unavailable when there are
// less admins connected.
// -- // --
// Requires: reservedslots.smx // Requires: reservedslots.smx
// Default: 0 // Default: 0
@ -97,7 +93,7 @@ sm_hide_slots 0
// Default: 1 // Default: 1
sm_chat_mode 1 sm_chat_mode 1
// Specifies whether or not "timeleft" will automatically be triggered every // Specifies whether or not "timeleft" will automaticly be triggered every
// x seconds. Valid values are 0 (Disabled) to 1800 seconds. // x seconds. Valid values are 0 (Disabled) to 1800 seconds.
// -- // --
// Requires: basetriggers.smx // Requires: basetriggers.smx
@ -109,8 +105,8 @@ sm_timeleft_interval 0
// 1 (Enabled) // 1 (Enabled)
// -- // --
// Requires: basetriggers.smx // Requires: basetriggers.smx
// Default: 0 // Default: 1
sm_trigger_show 0 sm_trigger_show 1
// Specifies whether or not to display vote progress to clients in the // Specifies whether or not to display vote progress to clients in the
// "hint" box (near the bottom of the screen in most games). // "hint" box (near the bottom of the screen in most games).

View File

@ -30,12 +30,12 @@
"ServerLang" "en" "ServerLang" "en"
/** /**
* List of characters to use for public chat triggers. Set an empty list to disable. * String to use as the public chat trigger. Set an empty string to disable.
*/ */
"PublicChatTrigger" "!" "PublicChatTrigger" "!"
/** /**
* List of characters to use for silent chat triggers. Set an empty list to disable. * String to use as the silent chat trigger. Set an empty string to disable.
*/ */
"SilentChatTrigger" "/" "SilentChatTrigger" "/"
@ -44,7 +44,7 @@
* but it does not evaluate to an actual command, it will be displayed * but it does not evaluate to an actual command, it will be displayed
* publicly. This setting allows you to suppress accidental typings. * publicly. This setting allows you to suppress accidental typings.
* *
* The default value is "no". A value of "yes" will suppress. * The default value is "no". A value of "yes" will supress.
*/ */
"SilentFailSuppress" "no" "SilentFailSuppress" "no"
@ -107,43 +107,4 @@
* Currently this will log details about the gamedata updating process. * Currently this will log details about the gamedata updating process.
*/ */
"DebugSpew" "no" "DebugSpew" "no"
/**
* If set to yes, SourceMod will validate steamid auth strings with the Steam backend before giving out admin access.
* This can prevent malicious users from impersonating admins with stolen Steam apptickets.
* If Steam is down, admins will not be authenticated until Steam comes back up.
* This option increases the security of your server, but is still experimental.
*/
"SteamAuthstringValidation" "yes"
/**
* Enables or disables whether SourceMod blocks known or potentially malicious plugins from loading.
* It is STRONGLY advised that this is left enabled, there have been cases in the past with plugins that
* allow anyone to delete files on the server, gain full rcon control, etc.
*
* "yes" - Block malware or illegal plugins from loading (default)
* "no" - Warn about malware or illegal plugins loading
*/
"BlockBadPlugins" "yes"
/**
* If a plugin takes too long to execute, hanging or freezing the game server in the process,
* SourceMod will attempt to terminate that plugin after the specified timeout length has
* passed. You can disable this feature by setting the value to "0".
*/
"SlowScriptTimeout" "8"
/**
* Per "http://blog.counter-strike.net/index.php/server_guidelines/", certain plugin
* functionality will trigger all of the game server owner's Game Server Login Tokens
* (GSLTs) to get banned when executed on a Counter-Strike: Global Offensive game server.
*
* Enabling this option will block plugins from using functionality that is known to cause this.
* This option only has any effect on CS:GO. Note that this does NOT guarantee that you cannot
* receive a ban.
*
* Disable this option at your own risk.
*/
"FollowCSGOServerGuidelines" "yes"
} }

View File

@ -2,8 +2,6 @@
{ {
"driver_default" "mysql" "driver_default" "mysql"
// When specifying "host", you may use an IP address, a hostname, or a socket file path
"default" "default"
{ {
"driver" "default" "driver" "default"

View File

@ -0,0 +1,36 @@
/**
* Each sub-section of "Plugins" should have a title which specifies a plugin filename.
* Filenames have a wildcard of *. Appending .smx is not required.
* If the filename has no explicit path, it will be patched to any sub-path in the plugins folder.
*
* Available properties for plugins are:
* "pause" - Whether or not the plugin should load paused - "yes" or "no" (default)
* "lifetime" - Lifetime of the plugin. Options:
* "mapsync" - Plugins should be reloaded on mapchange if changed (default)
* "global" - Plugin will never be unloaded or updated
* "blockload" - Plugin will always be blocked from loading. Implicit (automatic) loads
* produce no error, but explicit (manual) loads will show an error message.
* (Options are one of "yes" or "no")
*
* You can also have an "Options" section declaring options to pass onto the JIT:
* "profile" - Bit flags for profiling level. Add flags together to reach a value.
* WARNING: Profiler is _ALPHA_ software! Use it at your own risk for
* development cycles only (not production setups).
* See the wiki article "SourceMod Profiler" for more information.
* 1 - Profile natives
* 2 - Profile callbacks
* 4 - Profile internal plugin function calls
*/
"Plugins"
{
"*"
{
"pause" "no"
"lifetime" "mapsync"
"Options"
{
}
}
}

View File

@ -1,42 +1,10 @@
# vim: set ts=2 sw=2 tw=99 noet: # vim: set ts=2 sw=2 tw=99 noet:
import sys import sys
try: import ambuild.runner as runner
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)
def make_objdir_name(p): run = runner.Runner()
return 'obj-' + util.Platform() + '-' + p.target_arch run.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
parser = run.BuildParser(sourcePath=sys.path[0], api='2.1')
parser.default_arch = 'x86'
parser.default_build_folder = make_objdir_name
parser.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
help='Root search folder for HL2SDKs')
parser.options.add_option('--mysql-path', type=str, dest='mysql_path', default=None,
help='Path to MySQL 5')
parser.options.add_option('--mysql64-path', type=str, dest='mysql64_path', default=None,
help='Path to 64-bit MySQL 5')
parser.options.add_option('--mms-path', type=str, dest='mms_path', default=None,
help='Path to Metamod:Source')
parser.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
help='Enable debugging symbols') help='Enable debugging symbols')
parser.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', run.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt',
help='Enable optimization') help='Enable optimization')
parser.options.add_option('--no-mysql', action='store_false', default=True, dest='hasMySql', run.Configure(sys.path[0])
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',
default=False, help='Disable the auto versioning script')
parser.Configure()

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -30,6 +30,7 @@
*/ */
#include "ADTFactory.h" #include "ADTFactory.h"
#include "sm_globals.h"
#include "ShareSys.h" #include "ShareSys.h"
ADTFactory g_AdtFactory; ADTFactory g_AdtFactory;
@ -56,30 +57,32 @@ IBasicTrie *ADTFactory::CreateBasicTrie()
BaseTrie::BaseTrie() BaseTrie::BaseTrie()
{ {
m_pTrie = sm_trie_create();
} }
BaseTrie::~BaseTrie() BaseTrie::~BaseTrie()
{ {
sm_trie_destroy(m_pTrie);
} }
bool BaseTrie::Insert(const char *key, void *value) bool BaseTrie::Insert(const char *key, void *value)
{ {
return map_.insert(key, value); return sm_trie_insert(m_pTrie, key, value);
} }
bool BaseTrie::Retrieve(const char *key, void **value) bool BaseTrie::Retrieve(const char *key, void **value)
{ {
return map_.retrieve(key, value); return sm_trie_retrieve(m_pTrie, key, value);
} }
bool BaseTrie::Delete(const char *key) bool BaseTrie::Delete(const char *key)
{ {
return map_.remove(key); return sm_trie_delete(m_pTrie, key);
} }
void BaseTrie::Clear() void BaseTrie::Clear()
{ {
map_.clear(); sm_trie_clear(m_pTrie);
} }
void BaseTrie::Destroy() void BaseTrie::Destroy()
@ -89,5 +92,5 @@ void BaseTrie::Destroy()
bool BaseTrie::Replace(const char *key, void *value) bool BaseTrie::Replace(const char *key, void *value)
{ {
return map_.replace(key, value); return sm_trie_replace(m_pTrie, key, value);
} }

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -33,8 +33,8 @@
#define _INCLUDE_SOURCEMOD_ADTFACTORY_H_ #define _INCLUDE_SOURCEMOD_ADTFACTORY_H_
#include <IADTFactory.h> #include <IADTFactory.h>
#include "common_logic.h" #include "sm_globals.h"
#include <sm_stringhashmap.h> #include "sm_trie.h"
using namespace SourceMod; using namespace SourceMod;
@ -50,7 +50,7 @@ public:
virtual void Clear(); virtual void Clear();
virtual void Destroy(); virtual void Destroy();
private: private:
StringHashMap<void *> map_; Trie *m_pTrie;
}; };
class ADTFactory : class ADTFactory :

View File

@ -1,113 +1,90 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: # vim: set ts=2 sw=2 tw=99 noet ft=python:
import os import os
project = SM.HL2Project(builder, 'sourcemod') for i in SM.sdkInfo:
project.sources += [ sdk = SM.sdkInfo[i]
'MenuStyle_Valve.cpp', name = 'sourcemod.' + sdk['ext']
'logic_bridge.cpp',
'smn_entities.cpp',
'sm_stringutil.cpp',
'MenuVoting.cpp',
'smn_events.cpp',
'frame_hooks.cpp',
'smn_nextmap.cpp',
'sourcemm_api.cpp',
'ChatTriggers.cpp',
'smn_player.cpp',
'sourcemod.cpp',
'concmd_cleaner.cpp',
'HalfLife2.cpp',
'NextMap.cpp',
'ConCmdManager.cpp',
'ConVarManager.cpp',
'PlayerManager.cpp',
'TimerSys.cpp',
'CoreConfig.cpp',
'Logger.cpp',
'smn_halflife.cpp',
'smn_console.cpp',
'UserMessages.cpp',
'MenuManager.cpp',
'smn_hudtext.cpp',
'smn_usermsgs.cpp',
'MenuStyle_Base.cpp',
'smn_keyvalues.cpp',
'smn_vector.cpp',
'EventManager.cpp',
'MenuStyle_Radio.cpp',
'sm_autonatives.cpp',
'ConsoleDetours.cpp',
'vprof_tool.cpp',
'smn_commandline.cpp',
'GameHooks.cpp',
]
for sdk_name in SM.sdks: compiler = SM.DefaultHL2Compiler('core', i)
sdk = SM.sdks[sdk_name]
for arch in SM.archs:
if not arch in sdk.platformSpec[builder.target.platform]:
continue
binary_name = 'sourcemod.' + sdk.ext extension = AMBuild.AddJob(name)
binary = Cpp.LibraryBuilder(name, AMBuild, extension, compiler)
binary = SM.HL2Config(project, binary_name, sdk, arch) SM.PreSetupHL2Job(extension, binary, i)
compiler = binary.compiler files = [
'AdminCache.cpp',
compiler.cxxincludes += [ 'ExtensionSys.cpp',
builder.sourcePath 'MenuStyle_Valve.cpp',
] 'sm_crc32.cpp',
'logic_bridge.cpp',
if sdk.name == 'csgo': 'smn_entities.cpp',
compiler.cxxincludes += [ 'sm_stringutil.cpp',
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'), 'ADTFactory.cpp',
os.path.join(sdk.path, 'public', 'engine', 'protobuf'), 'ForwardSys.cpp',
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf') 'MenuVoting.cpp',
] 'sm_memtable.cpp',
'smn_events.cpp',
if compiler.like('msvc'): 'smn_menus.cpp',
compiler.defines += ['_ALLOW_KEYWORD_MACROS'] 'sm_trie.cpp',
if builder.target.platform == 'linux': 'CDataPack.cpp',
compiler.postlink += ['-lpthread', '-lrt'] 'frame_hooks.cpp',
'NativeInvoker.cpp',
if sdk.name == 'csgo': 'smn_admin.cpp',
if builder.target.platform == 'linux': 'smn_fakenatives.cpp',
if arch == 'x86': 'smn_nextmap.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'linux32', 'release', 'libprotobuf.a') 'sourcemm_api.cpp',
elif arch == 'x64': 'ChatTriggers.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'linux64', 'release', 'libprotobuf.a') 'GameConfigs.cpp',
compiler.linkflags += ['-Wl,--exclude-libs=libprotobuf.a'] 'NativeOwner.cpp',
elif builder.target.platform == 'mac': 'smn_filesystem.cpp',
if arch == 'x86': 'smn_player.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'osx32', 'release', 'libprotobuf.a') 'sourcemod.cpp',
elif arch == 'x64': 'concmd_cleaner.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'osx64', 'release', 'libprotobuf.a') 'HalfLife2.cpp',
elif builder.target.platform == 'windows': 'NextMap.cpp',
msvc_ver = compiler.version 'smn_profiler.cpp',
vs_year = '' 'ConCmdManager.cpp',
if msvc_ver == 1800: 'HandleSys.cpp',
vs_year = '2013' 'PhraseCollection.cpp',
elif 1900 <= msvc_ver < 2000: 'ConVarManager.cpp',
vs_year = '2015' 'LibrarySys.cpp',
else: 'PlayerManager.cpp',
raise Exception('Cannot find libprotobuf for MSVC version "' + str(compiler.version) + '"') 'smn_banning.cpp',
'smn_gameconfigs.cpp',
if 'DEBUG' in compiler.defines: 'smn_string.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'debug', 'vs' + vs_year, 'libprotobuf.lib') 'TimerSys.cpp',
else: 'CoreConfig.cpp',
lib_path = os.path.join(sdk.path, 'lib', 'win32', 'release', 'vs' + vs_year, 'libprotobuf.lib') 'Logger.cpp',
compiler.linkflags.insert(0, binary.Dep(lib_path)) 'PluginInfoDatabase.cpp',
'smn_bitbuffer.cpp',
if sdk.name == 'csgo': 'smn_halflife.cpp',
binary.sources += ['smn_protobuf.cpp'] 'Translator.cpp',
else: 'MemoryUtils.cpp',
binary.sources += ['smn_bitbuffer.cpp'] 'PluginSys.cpp',
'smn_console.cpp',
if sdk.name == 'csgo': 'smn_handles.cpp',
binary.sources += [ 'smn_timers.cpp',
os.path.join(sdk.path, 'public', 'engine', 'protobuf', 'netmessages.pb.cc'), 'UserMessages.cpp',
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessages.pb.cc'), 'Database.cpp',
os.path.join(sdk.path, 'public', 'game', 'shared', 'csgo', 'protobuf', 'cstrike15_usermessage_helpers.cpp'), 'MenuManager.cpp',
] 'smn_core.cpp',
'smn_hudtext.cpp',
SM.binaries += builder.Add(project) 'smn_usermsgs.cpp',
'DebugReporter.cpp',
'MenuStyle_Base.cpp',
'ShareSys.cpp',
'smn_database.cpp',
'smn_keyvalues.cpp',
'smn_vector.cpp',
'EventManager.cpp',
'MenuStyle_Radio.cpp',
'sm_autonatives.cpp',
'smn_datapacks.cpp',
'smn_lang.cpp',
'sm_srvcmds.cpp',
'ConsoleDetours.cpp'
]
binary.AddSourceFiles('core', files)
SM.PostSetupHL2Job(extension, binary, i)
SM.AutoVersion('core', binary)
binary.SendToJob()

File diff suppressed because it is too large Load Diff

View File

@ -1,231 +1,208 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation. * Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * 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 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details. * details.
* *
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
* *
* As a special exception, AlliedModders LLC gives you permission to link the * 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 * 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 * "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 * by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants * all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further * this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>. * or <http://www.sourcemod.net/license.php>.
* *
* Version: $Id$ * Version: $Id$
*/ */
#ifndef _INCLUDE_SOURCEMOD_ADMINCACHE_H_ #ifndef _INCLUDE_SOURCEMOD_ADMINCACHE_H_
#define _INCLUDE_SOURCEMOD_ADMINCACHE_H_ #define _INCLUDE_SOURCEMOD_ADMINCACHE_H_
#include "common_logic.h" #include "sm_memtable.h"
#include <IAdminSystem.h> #include <sm_trie.h>
#include "sm_memtable.h" #include <sh_list.h>
#include <sm_trie.h> #include <sh_string.h>
#include <sh_list.h> #include <IAdminSystem.h>
#include <sh_string.h> #include "sm_globals.h"
#include <IForwardSys.h> #include <IForwardSys.h>
#include <sm_stringhashmap.h>
#include <sm_namehashset.h> using namespace SourceHook;
using namespace SourceHook; #define GRP_MAGIC_SET 0xDEADFADE
#define GRP_MAGIC_UNSET 0xFACEFACE
#define GRP_MAGIC_SET 0xDEADFADE #define USR_MAGIC_SET 0xDEADFACE
#define GRP_MAGIC_UNSET 0xFACEFACE #define USR_MAGIC_UNSET 0xFADEDEAD
#define USR_MAGIC_SET 0xDEADFACE
#define USR_MAGIC_UNSET 0xFADEDEAD struct AdminGroup
{
typedef StringHashMap<OverrideRule> OverrideMap; uint32_t magic; /* Magic flag, for memory validation (ugh) */
unsigned int immunity_level; /* Immunity level */
struct AdminGroup /* Immune from target table (-1 = nonexistent)
{ * [0] = number of entries
uint32_t magic; /* Magic flag, for memory validation (ugh) */ * [1...N] = immune targets
unsigned int immunity_level; /* Immunity level */ */
/* Immune from target table (-1 = nonexistent) int immune_table;
* [0] = number of entries Trie *pCmdTable; /* Command override table (can be NULL) */
* [1...N] = immune targets Trie *pCmdGrpTable; /* Command group override table (can be NULL) */
*/ int next_grp; /* Next group in the chain */
int immune_table; int prev_grp; /* Previous group in the chain */
OverrideMap *pCmdTable; /* Command override table (can be NULL) */ int nameidx; /* Name */
OverrideMap *pCmdGrpTable; /* Command group override table (can be NULL) */ FlagBits addflags; /* Additive flags */
int next_grp; /* Next group in the chain */ };
int prev_grp; /* Previous group in the chain */
int nameidx; /* Name */ struct AuthMethod
FlagBits addflags; /* Additive flags */ {
}; String name;
Trie *table;
struct AuthMethod };
{
String name; struct UserAuth
StringHashMap<AdminId> identities; {
unsigned int index; /* Index into auth table */
AuthMethod(const char *name) int identidx; /* Index into the string table */
: name(name) };
{
} struct AdminUser
{
static inline bool matches(const char *name, const AuthMethod *method) uint32_t magic; /* Magic flag, for memory validation */
{ FlagBits flags; /* Flags */
return strcmp(name, method->name.c_str()) == 0; FlagBits eflags; /* Effective flags */
} int nameidx; /* Name index */
static inline uint32_t hash(const detail::CharsAndLength &key) int password; /* Password index */
{ unsigned int grp_count; /* Number of groups */
return key.hash(); unsigned int grp_size; /* Size of groups table */
} int grp_table; /* Group table itself */
}; int next_user; /* Next user in the list */
int prev_user; /* Previous user in the list */
struct UserAuth UserAuth auth; /* Auth method for this user */
{ unsigned int immunity_level; /* Immunity level */
unsigned int index; /* Index into auth table */ unsigned int serialchange; /* Serial # for changes */
int identidx; /* Index into the string table */ };
};
class AdminCache :
struct AdminUser public IAdminSystem,
{ public SMGlobalClass
uint32_t magic; /* Magic flag, for memory validation */ {
FlagBits flags; /* Flags */ public:
FlagBits eflags; /* Effective flags */ AdminCache();
int nameidx; /* Name index */ ~AdminCache();
int password; /* Password index */ public: //SMGlobalClass
unsigned int grp_count; /* Number of groups */ void OnSourceModStartup(bool late);
unsigned int grp_size; /* Size of groups table */ void OnSourceModAllInitialized();
int grp_table; /* Group table itself */ void OnSourceModLevelChange(const char *mapName);
int next_user; /* Next user in the list */ void OnSourceModShutdown();
int prev_user; /* Previous user in the list */ void OnSourceModPluginsLoaded();
UserAuth auth; /* Auth method for this user */ public: //IAdminSystem
unsigned int immunity_level; /* Immunity level */ /** Command cache stuff */
unsigned int serialchange; /* Serial # for changes */ void AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags);
}; bool GetCommandOverride(const char *cmd, OverrideType type, FlagBits *flags);
void UnsetCommandOverride(const char *cmd, OverrideType type);
class AdminCache : /** Group cache stuff */
public IAdminSystem, GroupId AddGroup(const char *group_name);
public SMGlobalClass GroupId FindGroupByName(const char *group_name);
{ void SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled);
public: bool GetGroupAddFlag(GroupId id, AdminFlag flag);
AdminCache(); FlagBits GetGroupAddFlags(GroupId id);
~AdminCache(); void SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled);
public: //SMGlobalClass bool GetGroupGenericImmunity(GroupId id, ImmunityType type);
void OnSourceModStartup(bool late); void InvalidateGroup(GroupId id);
void OnSourceModAllInitialized(); void AddGroupImmunity(GroupId id, GroupId other_id);
void OnSourceModLevelChange(const char *mapName); unsigned int GetGroupImmunityCount(GroupId id);
void OnSourceModShutdown(); GroupId GetGroupImmunity(GroupId id, unsigned int number);
void OnSourceModPluginsLoaded(); void AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule);
public: //IAdminSystem bool GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule);
/** Command cache stuff */ void DumpAdminCache(AdminCachePart part, bool rebuild);
void AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags); void AddAdminListener(IAdminListener *pListener);
bool GetCommandOverride(const char *cmd, OverrideType type, FlagBits *flags); void RemoveAdminListener(IAdminListener *pListener);
void UnsetCommandOverride(const char *cmd, OverrideType type); /** User stuff */
/** Group cache stuff */ void RegisterAuthIdentType(const char *name);
GroupId AddGroup(const char *group_name); AdminId CreateAdmin(const char *name);
GroupId FindGroupByName(const char *group_name); const char *GetAdminName(AdminId id);
void SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled); bool BindAdminIdentity(AdminId id, const char *auth, const char *ident);
bool GetGroupAddFlag(GroupId id, AdminFlag flag); virtual void SetAdminFlag(AdminId id, AdminFlag flag, bool enabled);
FlagBits GetGroupAddFlags(GroupId id); bool GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode);
void SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled); FlagBits GetAdminFlags(AdminId id, AccessMode mode);
bool GetGroupGenericImmunity(GroupId id, ImmunityType type); bool AdminInheritGroup(AdminId id, GroupId gid);
void InvalidateGroup(GroupId id); unsigned int GetAdminGroupCount(AdminId id);
void AddGroupImmunity(GroupId id, GroupId other_id); GroupId GetAdminGroup(AdminId id, unsigned int index, const char **name);
unsigned int GetGroupImmunityCount(GroupId id); void SetAdminPassword(AdminId id, const char *password);
GroupId GetGroupImmunity(GroupId id, unsigned int number); const char *GetAdminPassword(AdminId id);
void AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule); AdminId FindAdminByIdentity(const char *auth, const char *identity);
bool GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule); bool InvalidateAdmin(AdminId id);
void DumpAdminCache(AdminCachePart part, bool rebuild); unsigned int FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize);
void AddAdminListener(IAdminListener *pListener); FlagBits FlagBitArrayToBits(const bool array[], unsigned int maxSize);
void RemoveAdminListener(IAdminListener *pListener); FlagBits FlagArrayToBits(const AdminFlag array[], unsigned int numFlags);
/** User stuff */ unsigned int FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize);
void RegisterAuthIdentType(const char *name); bool CheckAdminFlags(AdminId id, FlagBits bits);
AdminId CreateAdmin(const char *name); bool CanAdminTarget(AdminId id, AdminId target);
const char *GetAdminName(AdminId id); void SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits);
bool BindAdminIdentity(AdminId id, const char *auth, const char *ident); bool FindFlag(const char *str, AdminFlag *pFlag);
virtual void SetAdminFlag(AdminId id, AdminFlag flag, bool enabled); bool FindFlag(char c, AdminFlag *pAdmFlag);
bool GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode); FlagBits ReadFlagString(const char *flags, const char **end);
FlagBits GetAdminFlags(AdminId id, AccessMode mode); size_t FillFlagString(FlagBits bits, char *buffer, size_t maxlen);
bool AdminInheritGroup(AdminId id, GroupId gid); unsigned int GetAdminSerialChange(AdminId id);
unsigned int GetAdminGroupCount(AdminId id); bool CanAdminUseCommand(int client, const char *cmd);
GroupId GetAdminGroup(AdminId id, unsigned int index, const char **name); const char *GetGroupName(GroupId gid);
void SetAdminPassword(AdminId id, const char *password); unsigned int SetGroupImmunityLevel(GroupId gid, unsigned int level);
const char *GetAdminPassword(AdminId id); unsigned int GetGroupImmunityLevel(GroupId gid);
AdminId FindAdminByIdentity(const char *auth, const char *identity); unsigned int SetAdminImmunityLevel(AdminId id, unsigned int level);
bool InvalidateAdmin(AdminId id); unsigned int GetAdminImmunityLevel(AdminId id);
unsigned int FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize); bool CheckAccess(int client,
FlagBits FlagBitArrayToBits(const bool array[], unsigned int maxSize); const char *cmd,
FlagBits FlagArrayToBits(const AdminFlag array[], unsigned int numFlags); FlagBits flags,
unsigned int FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize); bool override_only);
bool CheckAdminFlags(AdminId id, FlagBits bits); bool FindFlagChar(AdminFlag flag, char *c);
bool CanAdminTarget(AdminId id, AdminId target); public:
void SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits); bool IsValidAdmin(AdminId id);
bool FindFlag(const char *str, AdminFlag *pFlag); void DumpCache(FILE *fp);
bool FindFlag(char c, AdminFlag *pAdmFlag); AdminGroup *GetGroup(GroupId gid);
FlagBits ReadFlagString(const char *flags, const char **end); AdminUser *GetUser(AdminId id);
size_t FillFlagString(FlagBits bits, char *buffer, size_t maxlen); const char *GetString(int idx);
unsigned int GetAdminSerialChange(AdminId id); private:
bool CanAdminUseCommand(int client, const char *cmd); void _UnsetCommandOverride(const char *cmd);
const char *GetGroupName(GroupId gid); void _UnsetCommandGroupOverride(const char *group);
unsigned int SetGroupImmunityLevel(GroupId gid, unsigned int level); void InvalidateGroupCache();
unsigned int GetGroupImmunityLevel(GroupId gid); void InvalidateAdminCache(bool unlink_admins);
unsigned int SetAdminImmunityLevel(AdminId id, unsigned int level); void DumpCommandOverrideCache(OverrideType type);
unsigned int GetAdminImmunityLevel(AdminId id); Trie *GetMethodByIndex(unsigned int index);
bool CheckAccess(int client, bool GetMethodIndex(const char *name, unsigned int *_index);
const char *cmd, const char *GetMethodName(unsigned int index);
FlagBits flags, void NameFlag(const char *str, AdminFlag flag);
bool override_only); public:
bool FindFlagChar(AdminFlag flag, char *c); BaseStringTable *m_pStrings;
bool IsValidAdmin(AdminId id); BaseMemTable *m_pMemory;
bool CheckClientCommandAccess(int client, const char *cmd, FlagBits cmdflags); Trie *m_pCmdOverrides;
public: Trie *m_pCmdGrpOverrides;
bool DumpCache(const char *filename); int m_FirstGroup;
AdminGroup *GetGroup(GroupId gid); int m_LastGroup;
AdminUser *GetUser(AdminId id); int m_FreeGroupList;
const char *GetString(int idx); Trie *m_pGroups;
bool CheckAdminCommandAccess(AdminId adm, const char *cmd, FlagBits flags); List<IAdminListener *> m_hooks;
private: List<AuthMethod> m_AuthMethods;
void _UnsetCommandOverride(const char *cmd); Trie *m_pAuthTables;
void _UnsetCommandGroupOverride(const char *group); IForward *m_pCacheFwd;
void InvalidateGroupCache(); int m_FirstUser;
void InvalidateAdminCache(bool unlink_admins); int m_LastUser;
void DumpCommandOverrideCache(OverrideType type); int m_FreeUserList;
AuthMethod *GetMethodByIndex(unsigned int index); bool m_InvalidatingAdmins;
bool GetMethodIndex(const char *name, unsigned int *_index); bool m_destroying;
const char *GetMethodName(unsigned int index); Trie *m_pLevelNames;
void NameFlag(const char *str, AdminFlag flag); };
bool GetUnifiedSteamIdentity(const char *ident, char *out, size_t maxlen);
public: extern AdminCache g_Admins;
typedef StringHashMap<FlagBits> FlagMap;
#endif //_INCLUDE_SOURCEMOD_ADMINCACHE_H_
BaseStringTable *m_pStrings;
BaseMemTable *m_pMemory;
FlagMap m_CmdOverrides;
FlagMap m_CmdGrpOverrides;
int m_FirstGroup;
int m_LastGroup;
int m_FreeGroupList;
StringHashMap<GroupId> m_Groups;
List<IAdminListener *> m_hooks;
List<AuthMethod *> m_AuthMethods;
NameHashSet<AuthMethod *> m_AuthTables;
IForward *m_pCacheFwd;
int m_FirstUser;
int m_LastUser;
int m_FreeUserList;
bool m_InvalidatingAdmins;
bool m_destroying;
StringHashMap<AdminFlag> m_LevelNames;
};
extern AdminCache g_Admins;
#endif //_INCLUDE_SOURCEMOD_ADMINCACHE_H_

259
core/CDataPack.cpp Normal file
View File

@ -0,0 +1,259 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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 <stdlib.h>
#include <string.h>
#include "CDataPack.h"
#define DATAPACK_INITIAL_SIZE 512
CDataPack::CDataPack()
{
m_pBase = (char *)malloc(DATAPACK_INITIAL_SIZE);
m_capacity = DATAPACK_INITIAL_SIZE;
Initialize();
}
CDataPack::~CDataPack()
{
free(m_pBase);
}
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;
do
{
m_capacity *= 2;
m_pBase = (char *)realloc(m_pBase, m_capacity);
m_curptr = m_pBase + pos;
} while (m_curptr - m_pBase + typesize > m_capacity);
}
void CDataPack::ResetSize()
{
m_size = 0;
}
size_t CDataPack::CreateMemory(size_t size, void **addr)
{
CheckSize(sizeof(size_t) + size);
size_t pos = m_curptr - m_pBase;
*(size_t *)m_curptr = size;
m_curptr += sizeof(size_t);
if (addr)
{
*addr = m_curptr;
}
m_curptr += size;
m_size += sizeof(size_t) + size;
return pos;
}
void CDataPack::PackCell(cell_t cell)
{
CheckSize(sizeof(size_t) + sizeof(cell_t));
*(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(size_t) + sizeof(cell_t);
}
void CDataPack::PackFloat(float val)
{
CheckSize(sizeof(size_t) + sizeof(float));
*(size_t *)m_curptr = sizeof(float);
m_curptr += sizeof(size_t);
*(float *)m_curptr = val;
m_curptr += sizeof(float);
m_size += sizeof(size_t) + sizeof(float);
}
void CDataPack::PackString(const char *string)
{
size_t len = strlen(string);
size_t maxsize = sizeof(size_t) + len + 1;
CheckSize(maxsize);
// Pack the string length first for buffer overrun checking.
*(size_t *)m_curptr = len;
m_curptr += sizeof(size_t);
// Now pack the string.
memcpy(m_curptr, string, len);
m_curptr[len] = '\0';
m_curptr += len + 1;
m_size += maxsize;
}
void CDataPack::Reset() const
{
m_curptr = m_pBase;
}
size_t CDataPack::GetPosition() const
{
return static_cast<size_t>(m_curptr - m_pBase);
}
bool CDataPack::SetPosition(size_t pos) const
{
if (pos > m_size-1)
{
return false;
}
m_curptr = m_pBase + pos;
return true;
}
cell_t CDataPack::ReadCell() const
{
if (!IsReadable(sizeof(size_t) + sizeof(cell_t)))
{
return 0;
}
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(size_t) + sizeof(float)))
{
return 0;
}
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(size_t)))
{
return NULL;
}
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;
}
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;
}

71
core/CDataPack.h Normal file
View File

@ -0,0 +1,71 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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_SOURCEMOD_CDATAPACK_H_
#define _INCLUDE_SOURCEMOD_CDATAPACK_H_
#include <IDataPack.h>
using namespace SourceMod;
class CDataPack : public IDataPack
{
public:
CDataPack();
~CDataPack();
public: //IDataReader
void Reset() const;
size_t GetPosition() const;
bool SetPosition(size_t pos) const;
cell_t ReadCell() const;
float ReadFloat() const;
bool IsReadable(size_t bytes) const;
const char *ReadString(size_t *len) const;
void *GetMemory() const;
void *ReadMemory(size_t *size) const;
public: //IDataPack
void ResetSize();
void PackCell(cell_t cell);
void PackFloat(float val);
void PackString(const char *string);
size_t CreateMemory(size_t size, void **addr);
public:
void Initialize();
private:
void CheckSize(size_t sizetype);
private:
char *m_pBase;
mutable char *m_curptr;
size_t m_capacity;
size_t m_size;
};
#endif //_INCLUDE_SOURCEMOD_CDATAPACK_H_

View File

@ -81,17 +81,11 @@ inline int CellRecipientFilter::GetRecipientCount() const
inline int CellRecipientFilter::GetRecipientIndex(int slot) const inline int CellRecipientFilter::GetRecipientIndex(int slot) const
{ {
int ret;
if ((slot < 0) || (slot >= GetRecipientCount())) if ((slot < 0) || (slot >= GetRecipientCount()))
{ {
ret = -1; return -1;
} }
else return static_cast<int>(m_Players[slot]);
{
ret = static_cast<int>(m_Players[slot]);
}
return ret;
} }
inline void CellRecipientFilter::SetToInit(bool isinitmsg) inline void CellRecipientFilter::SetToInit(bool isinitmsg)

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -8,7 +8,7 @@
* This program is free software; you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation. * Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * 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 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@ -34,71 +34,66 @@
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "ConCmdManager.h" #include "ConCmdManager.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "Translator.h"
#include "HalfLife2.h" #include "HalfLife2.h"
#include "logic_bridge.h"
#include "sourcemod.h" /* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does.
#include "provider.h" * While the OB build only runs on MM:S 1.6.0+ (SH 5+), the older one
#include <bridge/include/ILogger.h> * can technically be compiled against any MM:S version after 1.4.2.
#include <amtl/am-string.h> */
#if SOURCE_ENGINE >= SE_ORANGEBOX
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
#else
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
#if SH_IMPL_VERSION >= 5
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate0<void>);
#elif SH_IMPL_VERSION == 4
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
#elif SH_IMPL_VERSION == 3
extern bool __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
#endif //SH_IMPL_VERSION
#endif //SE_ORANGEBOX
ChatTriggers g_ChatTriggers; ChatTriggers g_ChatTriggers;
bool g_bSupressSilentFails = false; bool g_bSupressSilentFails = false;
ChatTriggers::ChatTriggers() : m_bWillProcessInPost(false), ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false),
m_ReplyTo(SM_REPLY_CONSOLE), m_ArgSBackup(NULL) m_bTriggerWasSilent(false), m_ReplyTo(SM_REPLY_CONSOLE)
{ {
m_PubTrigger = "!"; m_PubTrigger = sm_strdup("!");
m_PrivTrigger = "/"; m_PrivTrigger = sm_strdup("/");
m_PubTriggerSize = 1;
m_PrivTriggerSize = 1;
m_bIsChatTrigger = false; m_bIsChatTrigger = false;
m_bPluginIgnored = true;
#if SOURCE_ENGINE == SE_EPISODEONE
m_bIsINS = false;
#endif
} }
ChatTriggers::~ChatTriggers() ChatTriggers::~ChatTriggers()
{ {
delete [] m_ArgSBackup; delete [] m_PubTrigger;
m_ArgSBackup = NULL; m_PubTrigger = NULL;
delete [] m_PrivTrigger;
m_PrivTrigger = NULL;
} }
void ChatTriggers::SetChatTrigger(ChatTriggerType type, const char *value) ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key,
{ const char *value,
ke::AutoPtr<char[]> filtered(new char[strlen(value) + 1]);
const char *src = value;
char *dest = filtered.get();
char c;
while ((c = *src++) != '\0') {
if (c <= ' ' || c == '"' || c == '\'' || (c >= '0' && c <= '9') || c == ';' || (c >= 'A' && c <= 'Z') || c == '\\' || (c >= 'a' && c <= 'z') || c >= 0x7F) {
logger->LogError("Ignoring %s chat trigger character '%c', not in valid set: %s", (type == ChatTrigger_Private ? "silent" : "public"), c, "!#$%&()*+,-./:<=>?@[]^_`{|}~");
continue;
}
*dest++ = c;
}
*dest = '\0';
if (type == ChatTrigger_Private) {
m_PrivTrigger = filtered.get();
} else {
m_PubTrigger = filtered.get();
}
}
ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source, ConfigSource source,
char *error, char *error,
size_t maxlength) size_t maxlength)
{ {
if (strcmp(key, "PublicChatTrigger") == 0) if (strcmp(key, "PublicChatTrigger") == 0)
{ {
SetChatTrigger(ChatTrigger_Public, value); delete [] m_PubTrigger;
m_PubTrigger = sm_strdup(value);
m_PubTriggerSize = strlen(m_PubTrigger);
return ConfigResult_Accept; return ConfigResult_Accept;
} }
else if (strcmp(key, "SilentChatTrigger") == 0) else if (strcmp(key, "SilentChatTrigger") == 0)
{ {
SetChatTrigger(ChatTrigger_Private, value); delete [] m_PrivTrigger;
m_PrivTrigger = sm_strdup(value);
m_PrivTriggerSize = strlen(m_PrivTrigger);
return ConfigResult_Accept; return ConfigResult_Accept;
} }
else if (strcmp(key, "SilentFailSuppress") == 0) else if (strcmp(key, "SilentFailSuppress") == 0)
@ -112,238 +107,204 @@ ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key,
void ChatTriggers::OnSourceModAllInitialized() void ChatTriggers::OnSourceModAllInitialized()
{ {
m_pShouldFloodBlock = forwardsys->CreateForward("OnClientFloodCheck", ET_Event, 1, NULL, Param_Cell); m_pShouldFloodBlock = g_Forwards.CreateForward("OnClientFloodCheck", ET_Event, 1, NULL, Param_Cell);
m_pDidFloodBlock = forwardsys->CreateForward("OnClientFloodResult", ET_Event, 2, NULL, Param_Cell, Param_Cell); m_pDidFloodBlock = g_Forwards.CreateForward("OnClientFloodResult", ET_Event, 2, NULL, Param_Cell, Param_Cell);
m_pOnClientSayCmd = forwardsys->CreateForward("OnClientSayCommand", ET_Event, 3, NULL, Param_Cell, Param_String, Param_String);
m_pOnClientSayCmd_Post = forwardsys->CreateForward("OnClientSayCommand_Post", ET_Ignore, 3, NULL, Param_Cell, Param_String, Param_String);
} }
void ChatTriggers::OnSourceModAllInitialized_Post() void ChatTriggers::OnSourceModAllInitialized_Post()
{ {
logicore.AddCorePhraseFile("antiflood.phrases"); g_pCorePhrases->AddPhraseFile("antiflood.phrases");
} }
void ChatTriggers::OnSourceModGameInitialized() void ChatTriggers::OnSourceModGameInitialized()
{ {
ConCommand *say_team = FindCommand("say_team"); m_pSayCmd = FindCommand("say");
m_pSayTeamCmd = FindCommand("say_team");
CommandHook::Callback pre_hook = [this] (int client, const ICommandArgs *args) -> bool { if (m_pSayCmd)
return this->OnSayCommand_Pre(client, args); {
}; SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
CommandHook::Callback post_hook = [this] (int client, const ICommandArgs *args) -> bool { SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
return this->OnSayCommand_Post(client, args);
};
if (ConCommand *say = FindCommand("say")) {
hooks_.append(sCoreProviderImpl.AddCommandHook(say, pre_hook));
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say, post_hook));
} }
if (ConCommand *say_team = FindCommand("say_team")) { if (m_pSayTeamCmd)
hooks_.append(sCoreProviderImpl.AddCommandHook(say_team, pre_hook)); {
hooks_.append(sCoreProviderImpl.AddPostCommandHook(say_team, post_hook)); SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
} }
#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));
}
}
#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));
}
#endif
} }
void ChatTriggers::OnSourceModShutdown() void ChatTriggers::OnSourceModShutdown()
{ {
hooks_.clear(); if (m_pSayTeamCmd)
{
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
}
if (m_pSayCmd)
{
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
}
forwardsys->ReleaseForward(m_pShouldFloodBlock); g_Forwards.ReleaseForward(m_pShouldFloodBlock);
forwardsys->ReleaseForward(m_pDidFloodBlock); g_Forwards.ReleaseForward(m_pDidFloodBlock);
forwardsys->ReleaseForward(m_pOnClientSayCmd);
forwardsys->ReleaseForward(m_pOnClientSayCmd_Post);
} }
bool ChatTriggers::OnSayCommand_Pre(int client, const ICommandArgs *command) #if SOURCE_ENGINE >= SE_ORANGEBOX
void ChatTriggers::OnSayCommand_Pre(const CCommand &command)
{ {
#else
void ChatTriggers::OnSayCommand_Pre()
{
CCommand command;
#endif
int client;
CPlayer *pPlayer;
client = g_ConCmds.GetCommandClient();
m_bIsChatTrigger = false; m_bIsChatTrigger = false;
m_bWasFloodedMessage = false; m_bWasFloodedMessage = false;
m_bPluginIgnored = true;
const char *args = command->ArgS();
if (!args)
return false;
/* Save these off for post hook as the command data returned from the engine in older engine versions
* can be NULL, despite the data still being there and valid. */
m_Arg0Backup = command->Arg(0);
size_t len = strlen(args);
#if SOURCE_ENGINE == SE_EPISODEONE
if (m_bIsINS)
{
if (strcmp(m_Arg0Backup, "say2") == 0 && len >= 4)
{
args += 4;
len -= 4;
}
if (len == 0)
return true;
}
#endif
/* The first pair of quotes are stripped from client say commands, but not console ones.
* We do not want the forwards to differ from what is displayed.
* So only strip the first pair of quotes from client say commands. */
bool is_quoted = false;
if (
#if SOURCE_ENGINE == SE_EPISODEONE
!m_bIsINS &&
#endif
client != 0 && args[0] == '"' && args[len-1] == '"')
{
/* The server normally won't display empty say commands, but in this case it does.
* I don't think it's desired so let's block it. */
if (len <= 2)
return true;
args++;
len--;
is_quoted = true;
}
/* Some? engines strip the last quote when printing the string to chat.
* This results in having a double-quoted message passed to the OnClientSayCommand ("message") forward,
* but losing the last quote in the OnClientSayCommand_Post ("message) forward.
* To compensate this, we copy the args into our own buffer where the engine won't mess with
* and strip the quotes. */
delete [] m_ArgSBackup;
m_ArgSBackup = new char[CCommand::MaxCommandLength()+1];
memcpy(m_ArgSBackup, args, len+1);
/* Strip the quotes from the argument */
if (is_quoted)
{
if (m_ArgSBackup[len-1] == '"')
{
m_ArgSBackup[--len] = '\0';
}
}
/* The server console cannot do this */ /* The server console cannot do this */
if (client == 0) if (client == 0 || (pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
{ {
if (CallOnClientSayCommand(client) >= Pl_Handled) RETURN_META(MRES_IGNORED);
return true;
return false;
} }
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
/* We guarantee the client is connected */ /* We guarantee the client is connected */
if (!pPlayer || !pPlayer->IsConnected()) if (!pPlayer->IsConnected())
return false; {
RETURN_META(MRES_IGNORED);
}
const char *args = command.ArgS();
if (!args)
{
RETURN_META(MRES_IGNORED);
}
/* Check if we need to block this message from being sent */ /* Check if we need to block this message from being sent */
if (ClientIsFlooding(client)) if (ClientIsFlooding(client))
{ {
char buffer[128]; char buffer[128];
if (!logicore.CoreTranslate(buffer, sizeof(buffer), "%T", 2, NULL, "Flooding the server", &client)) if (!CoreTranslate(
ke::SafeSprintf(buffer, sizeof(buffer), "You are flooding the server!"); buffer,
sizeof(buffer),
"%T",
2,
NULL,
"Flooding the server",
&client))
{
UTIL_Format(buffer, sizeof(buffer), "You are flooding the server!");
}
/* :TODO: we should probably kick people who spam too much. */ /* :TODO: we should probably kick people who spam too much. */
char fullbuffer[192]; char fullbuffer[192];
ke::SafeSprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s", buffer); UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s", buffer);
g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer); g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer);
m_bWasFloodedMessage = true; m_bWasFloodedMessage = true;
return true;
RETURN_META(MRES_SUPERCEDE);
}
/* Handle quoted string sets */
bool is_quoted = false;
if (args[0] == '"')
{
args++;
is_quoted = true;
} }
bool is_trigger = false; bool is_trigger = false;
bool is_silent = false; bool is_silent = false;
// Prefer the silent trigger in case of clashes. /* Check for either trigger */
if (strchr(m_PrivTrigger.chars(), m_ArgSBackup[0])) { if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
{
is_trigger = true;
args = &args[m_PubTriggerSize];
}
else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0)
{
is_trigger = true; is_trigger = true;
is_silent = true; is_silent = true;
} else if (strchr(m_PubTrigger.chars(), m_ArgSBackup[0])) { args = &args[m_PrivTriggerSize];
is_trigger = true;
} }
if (is_trigger) { if (!is_trigger)
// Bump the args past the chat trigger - we only support single-character triggers now. {
args = &m_ArgSBackup[1]; RETURN_META(MRES_IGNORED);
} }
/** /**
* Test if this is actually a command! * Test if this is actually a command!
*/ */
if (is_trigger && PreProcessTrigger(PEntityOfEntIndex(client), args)) if (!PreProcessTrigger(PEntityOfEntIndex(client), args, is_quoted))
{ {
m_bIsChatTrigger = true; CPlayer *pPlayer;
if (is_silent
/** && g_bSupressSilentFails
* We'll execute it in post. && client != 0
*/ && (pPlayer = g_Players.GetPlayerByIndex(client)) != NULL
m_bWillProcessInPost = true; && pPlayer->GetAdminId() != INVALID_ADMIN_ID)
{
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
} }
if (is_silent && (m_bIsChatTrigger || (g_bSupressSilentFails && pPlayer->GetAdminId() != INVALID_ADMIN_ID))) m_bIsChatTrigger = true;
return true;
if (CallOnClientSayCommand(client) >= Pl_Handled) /**
return true; * We'll execute it in post.
*/
m_bWillProcessInPost = true;
m_bTriggerWasSilent = is_silent;
/* If we're silent, block */
if (is_silent)
{
RETURN_META(MRES_SUPERCEDE);
}
/* Otherwise, let the command continue */ /* Otherwise, let the command continue */
return false; RETURN_META(MRES_IGNORED);
} }
bool ChatTriggers::OnSayCommand_Post(int client, const ICommandArgs *command) #if SOURCE_ENGINE >= SE_ORANGEBOX
void ChatTriggers::OnSayCommand_Post(const CCommand &command)
#else
void ChatTriggers::OnSayCommand_Post()
#endif
{ {
m_bIsChatTrigger = false;
m_bWasFloodedMessage = false;
if (m_bWillProcessInPost) if (m_bWillProcessInPost)
{ {
/* Reset this for re-entrancy */ /* Reset this for re-entrancy */
m_bWillProcessInPost = false; m_bWillProcessInPost = false;
/* Execute the cached command */ /* Execute the cached command */
int client = g_ConCmds.GetCommandClient();
unsigned int old = SetReplyTo(SM_REPLY_CHAT); unsigned int old = SetReplyTo(SM_REPLY_CHAT);
serverpluginhelpers->ClientCommand(PEntityOfEntIndex(client), m_ToExecute); serverpluginhelpers->ClientCommand(PEntityOfEntIndex(client), m_ToExecute);
SetReplyTo(old); SetReplyTo(old);
} }
if (!m_bPluginIgnored && m_pOnClientSayCmd_Post->GetFunctionCount() != 0)
{
m_pOnClientSayCmd_Post->PushCell(client);
m_pOnClientSayCmd_Post->PushString(m_Arg0Backup);
m_pOnClientSayCmd_Post->PushString(m_ArgSBackup);
m_pOnClientSayCmd_Post->Execute(NULL);
}
m_bIsChatTrigger = false;
m_bWasFloodedMessage = false;
return false;
} }
bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args) bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
{ {
/* Extract a command. This is kind of sloppy. */ /* Extract a command. This is kind of sloppy. */
char cmd_buf[64]; char cmd_buf[64];
size_t cmd_len = 0; size_t cmd_len = 0;
const char *inptr = args; const char *inptr = args;
while (*inptr != '\0' while (*inptr != '\0'
&& !textparsers->IsWhitespace(inptr) && !textparsers->IsWhitespace(inptr)
&& *inptr != '"' && *inptr != '"'
&& cmd_len < sizeof(cmd_buf) - 1) && cmd_len < sizeof(cmd_buf) - 1)
{ {
@ -366,57 +327,50 @@ bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args)
return false; return false;
} }
/* Now, prepend. Don't worry about the buffers. This will /* Now, prepend. Don't worry about the buffers. This will
* work because the sizes are limited from earlier. * work because the sizes are limited from earlier.
*/ */
char new_buf[80]; char new_buf[80];
strcpy(new_buf, "sm_"); strcpy(new_buf, "sm_");
ke::SafeStrcpy(&new_buf[3], sizeof(new_buf)-3, cmd_buf); strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
/* Recheck */ /* Recheck */
if (!g_ConCmds.LookForSourceModCommand(new_buf)) if (!g_ConCmds.LookForSourceModCommand(new_buf))
{ {
return false; return false;
} }
prepended = true; prepended = true;
} }
/* See if we need to do extra string manipulation */ /* See if we need to do extra string manipulation */
if (prepended) if (is_quoted || prepended)
{ {
size_t len; size_t len;
/* Check if we need to prepend sm_ */ /* Check if we need to prepend sm_ */
if (prepended) if (prepended)
{ {
len = ke::SafeSprintf(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args); len = UTIL_Format(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
} else { } else {
len = ke::SafeStrcpy(m_ToExecute, sizeof(m_ToExecute), args); len = strncopy(m_ToExecute, args, sizeof(m_ToExecute));
}
/* Check if we need to strip a quote */
if (is_quoted)
{
if (m_ToExecute[len-1] == '"')
{
m_ToExecute[--len] = '\0';
}
} }
} else { } else {
ke::SafeStrcpy(m_ToExecute, sizeof(m_ToExecute), args); strncopy(m_ToExecute, args, sizeof(m_ToExecute));
} }
return true; return true;
} }
cell_t ChatTriggers::CallOnClientSayCommand(int client)
{
cell_t res = Pl_Continue;
if (m_pOnClientSayCmd->GetFunctionCount() != 0)
{
m_pOnClientSayCmd->PushCell(client);
m_pOnClientSayCmd->PushString(m_Arg0Backup);
m_pOnClientSayCmd->PushString(m_ArgSBackup);
m_pOnClientSayCmd->Execute(&res);
}
m_bPluginIgnored = (res >= Pl_Stop);
return res;
}
unsigned int ChatTriggers::SetReplyTo(unsigned int reply) unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
{ {
unsigned int old = m_ReplyTo; unsigned int old = m_ReplyTo;

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -8,7 +8,7 @@
* This program is free software; you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation. * Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * 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 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@ -34,11 +34,9 @@
#include "sm_globals.h" #include "sm_globals.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "GameHooks.h"
#include <IGameHelpers.h> #include <IGameHelpers.h>
#include <compat_wrappers.h> #include <compat_wrappers.h>
#include <IForwardSys.h> #include <IForwardSys.h>
#include <amtl/am-string.h>
class ChatTriggers : public SMGlobalClass class ChatTriggers : public SMGlobalClass
{ {
@ -50,47 +48,42 @@ public: //SMGlobalClass
void OnSourceModAllInitialized_Post(); void OnSourceModAllInitialized_Post();
void OnSourceModGameInitialized(); void OnSourceModGameInitialized();
void OnSourceModShutdown(); void OnSourceModShutdown();
ConfigResult OnSourceModConfigChanged(const char *key, ConfigResult OnSourceModConfigChanged(const char *key,
const char *value, const char *value,
ConfigSource source, ConfigSource source,
char *error, char *error,
size_t maxlength); size_t maxlength);
private: //ConCommand private: //ConCommand
bool OnSayCommand_Pre(int client, const ICommandArgs *args); #if SOURCE_ENGINE >= SE_ORANGEBOX
bool OnSayCommand_Post(int client, const ICommandArgs *args); void OnSayCommand_Pre(const CCommand &command);
void OnSayCommand_Post(const CCommand &command);
#else
void OnSayCommand_Pre();
void OnSayCommand_Post();
#endif
public: public:
unsigned int GetReplyTo(); unsigned int GetReplyTo();
unsigned int SetReplyTo(unsigned int reply); unsigned int SetReplyTo(unsigned int reply);
bool IsChatTrigger(); bool IsChatTrigger();
bool WasFloodedMessage(); bool WasFloodedMessage();
private: private:
enum ChatTriggerType { bool PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted);
ChatTrigger_Public,
ChatTrigger_Private,
};
void SetChatTrigger(ChatTriggerType type, const char *value);
bool PreProcessTrigger(edict_t *pEdict, const char *args);
bool ClientIsFlooding(int client); bool ClientIsFlooding(int client);
cell_t CallOnClientSayCommand(int client);
private: private:
ke::Vector<ke::RefPtr<CommandHook>> hooks_; ConCommand *m_pSayCmd;
ke::AString m_PubTrigger; ConCommand *m_pSayTeamCmd;
ke::AString m_PrivTrigger; char *m_PubTrigger;
size_t m_PubTriggerSize;
char *m_PrivTrigger;
size_t m_PrivTriggerSize;
bool m_bWillProcessInPost; bool m_bWillProcessInPost;
bool m_bTriggerWasSilent;
bool m_bIsChatTrigger; bool m_bIsChatTrigger;
bool m_bWasFloodedMessage; bool m_bWasFloodedMessage;
bool m_bPluginIgnored;
unsigned int m_ReplyTo; unsigned int m_ReplyTo;
char m_ToExecute[300]; char m_ToExecute[300];
const char *m_Arg0Backup;
char *m_ArgSBackup;
IForward *m_pShouldFloodBlock; IForward *m_pShouldFloodBlock;
IForward *m_pDidFloodBlock; IForward *m_pDidFloodBlock;
IForward *m_pOnClientSayCmd;
IForward *m_pOnClientSayCmd_Post;
#if SOURCE_ENGINE == SE_EPISODEONE
bool m_bIsINS;
#endif
}; };
extern ChatTriggers g_ChatTriggers; extern ChatTriggers g_ChatTriggers;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,93 +34,75 @@
#include "sm_globals.h" #include "sm_globals.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include <IForwardSys.h> #include "ForwardSys.h"
#include "sm_trie.h"
#include "sm_memtable.h"
#include <sh_list.h> #include <sh_list.h>
#include <sh_string.h> #include <sh_string.h>
#include <IRootConsoleMenu.h> #include <IRootConsoleMenu.h>
#include <IAdminSystem.h> #include <IAdminSystem.h>
#include "concmd_cleaner.h" #include "concmd_cleaner.h"
#include "GameHooks.h"
#include <am-autoptr.h>
#include <sm_stringhashmap.h>
#include <am-utility.h>
#include <am-inlinelist.h>
#include <am-linkedlist.h>
#include <am-refcounting.h>
using namespace SourceHook; using namespace SourceHook;
struct CmdHook; enum CmdType
struct ConCmdInfo;
struct CommandGroup : public ke::Refcounted<CommandGroup>
{ {
ke::LinkedList<CmdHook *> hooks; Cmd_Server,
Cmd_Console,
Cmd_Admin,
}; };
struct AdminCmdInfo struct AdminCmdInfo
{ {
AdminCmdInfo(const ke::RefPtr<CommandGroup> &group, FlagBits flags) AdminCmdInfo()
: group(group),
flags(flags),
eflags(0)
{ {
cmdGrpId = -1;
flags = 0;
eflags = 0;
} }
ke::RefPtr<CommandGroup> group; int cmdGrpId; /* index into cmdgroup string table */
FlagBits flags; /* default flags */ FlagBits flags; /* default flags */
FlagBits eflags; /* effective flags */ FlagBits eflags; /* effective flags */
}; };
struct CmdHook : public ke::InlineListNode<CmdHook> struct CmdHook
{ {
enum Type { CmdHook()
Server,
Client
};
CmdHook(Type type, ConCmdInfo *cmd, IPluginFunction *fun, const char *description)
: type(type),
info(cmd),
pf(fun),
helptext(description)
{ {
pf = NULL;
pAdmin = NULL;
} }
IPluginFunction *pf; /* function hook */
Type type; String helptext; /* help text */
ConCmdInfo *info; AdminCmdInfo *pAdmin; /* admin requirements, if any */
IPluginFunction *pf; /* function hook */
ke::AString helptext; /* help text */
ke::AutoPtr<AdminCmdInfo> admin; /* admin requirements, if any */
}; };
typedef ke::InlineList<CmdHook> CmdHookList;
struct ConCmdInfo struct ConCmdInfo
{ {
ConCmdInfo() ConCmdInfo()
{ {
pPlugin = nullptr;
sourceMod = false; sourceMod = false;
pCmd = nullptr; pCmd = NULL;
eflags = 0;
} }
bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */ bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */
ConCommand *pCmd; /**< Pointer to the command itself */ ConCommand *pCmd; /**< Pointer to the command itself */
CmdHookList hooks; /**< Hook list */ List<CmdHook *> srvhooks; /**< Hooks as a server command */
FlagBits eflags; /**< Effective admin flags */ List<CmdHook *> conhooks; /**< Hooks as a console command */
ke::RefPtr<CommandHook> sh_hook; /**< SourceHook hook, if any. */ AdminCmdInfo admin; /**< Admin info, if any */
IPlugin *pPlugin; /**< Owning plugin handle. */ bool is_admin_set; /**< Whether or not admin info is set */
}; };
typedef List<ConCmdInfo *> ConCmdList;
class ConCmdManager : class ConCmdManager :
public SMGlobalClass, public SMGlobalClass,
public IRootConsoleCommand, public IRootConsoleCommand,
public IPluginsListener, public IPluginsListener,
public IConCommandTracker public IConCommandTracker
{ {
friend void CommandCallback(DISPATCH_ARGS); #if SOURCE_ENGINE >= SE_ORANGEBOX
friend void CommandCallback(const CCommand &command);
#else
friend void CommandCallback();
#endif
public: public:
ConCmdManager(); ConCmdManager();
~ConCmdManager(); ~ConCmdManager();
@ -130,46 +112,48 @@ public: //SMGlobalClass
public: //IPluginsListener public: //IPluginsListener
void OnPluginDestroyed(IPlugin *plugin); void OnPluginDestroyed(IPlugin *plugin);
public: //IRootConsoleCommand public: //IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) override; void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public: //IConCommandTracker public: //IConCommandTracker
void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name) override; void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe);
public: public:
bool AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags, IPlugin *pPlugin); bool AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
bool AddConsoleCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
bool AddAdminCommand(IPluginFunction *pFunction, bool AddAdminCommand(IPluginFunction *pFunction,
const char *name, const char *name,
const char *group, const char *group,
int adminflags, int adminflags,
const char *description, const char *description,
int flags, int flags);
IPlugin *pPlugin);
ResultType DispatchClientCommand(int client, const char *cmd, int args, ResultType type); ResultType DispatchClientCommand(int client, const char *cmd, int args, ResultType type);
void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove); void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove);
bool LookForSourceModCommand(const char *cmd); bool LookForSourceModCommand(const char *cmd);
bool LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags); bool LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags);
bool CheckCommandAccess(int client, const char *cmd, FlagBits flags);
private: private:
bool InternalDispatch(int client, const ICommandArgs *args); void InternalDispatch(const CCommand &command);
ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args); ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags, IPlugin *pPlugin); ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags);
void SetCommandClient(int client);
void AddToCmdList(ConCmdInfo *info); void AddToCmdList(ConCmdInfo *info);
void RemoveConCmd(ConCmdInfo *info, const char *cmd, bool untrack); void RemoveConCmd(ConCmdInfo *info, const char *cmd, bool is_read_safe, bool untrack);
void RemoveConCmds(List<CmdHook *> &cmdlist);
void RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext);
bool CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin); 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: public:
inline int GetCommandClient()
{
return m_CmdClient;
}
inline const List<ConCmdInfo *> & GetCommandList() inline const List<ConCmdInfo *> & GetCommandList()
{ {
return m_CmdList; return m_CmdList;
} }
private: private:
typedef StringHashMap<ke::RefPtr<CommandGroup> > GroupMap; Trie *m_pCmds; /* command lookup */
Trie *m_pCmdGrps; /* command group lookup */
StringHashMap<ConCmdInfo *> m_Cmds; /* command lookup */ List<ConCmdInfo *> m_CmdList; /* command list */
GroupMap m_CmdGrps; /* command group map */ int m_CmdClient; /* current client */
ConCmdList m_CmdList; /* command list */ BaseStringTable m_Strings; /* string table */
}; };
extern ConCmdManager g_ConCmds; extern ConCmdManager g_ConCmds;

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -31,59 +31,59 @@
#ifndef _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_ #ifndef _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_
#define _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_ #define _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_
#if SOURCE_ENGINE >= SE_LEFT4DEAD
class ConCommandBaseIterator class ConCommandBaseIterator
{ {
public: #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
inline ConCommandBaseIterator() : iter(icvar) ICvarIteratorInternal *cvarIter;
{
iter.SetFirst();
}
inline bool IsValid()
{
return iter.IsValid();
}
inline void Next()
{
iter.Next();
}
inline ConCommandBase *Get()
{
return iter.Get();
}
private:
ICvar::Iterator iter;
};
#else #else
class ConCommandBaseIterator ConCommandBase *cvarIter;
{ #endif
public: public:
inline ConCommandBaseIterator() ConCommandBaseIterator()
{ {
iter = icvar->GetCommands(); #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
cvarIter = icvar->FactoryInternalIterator();
cvarIter->SetFirst();
#else
cvarIter = icvar->GetCommands();
#endif
}
~ConCommandBaseIterator()
{
#if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
g_pMemAlloc->Free(cvarIter);
#endif
} }
inline bool IsValid() inline bool IsValid()
{ {
return iter != NULL; #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
return cvarIter->IsValid();
#else
return cvarIter != NULL;
#endif
} }
inline void Next() inline void Next()
{ {
iter = const_cast<ConCommandBase *>(iter->GetNext()); #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
cvarIter->Next();
#else
cvarIter = const_cast<ConCommandBase*>(cvarIter->GetNext());
#endif
} }
inline ConCommandBase *Get() inline ConCommandBase *Get()
{ {
return iter; #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
} return cvarIter->Get();
private: #else
ConCommandBase *iter; return cvarIter;
};
#endif #endif
}
};
#endif /* _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_ */ #endif /* _INCLUDE_SOURCEMOD_CON_COMMAND_BASE_ITERATOR_H_ */

View File

@ -29,19 +29,34 @@
#include "ConVarManager.h" #include "ConVarManager.h"
#include "HalfLife2.h" #include "HalfLife2.h"
#include "PluginSys.h"
#include "ForwardSys.h"
#include "HandleSys.h"
#include "sm_srvcmds.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include <sh_vector.h> #include <sh_vector.h>
#include <sm_namehashset.h> #include <sm_trie_tpl.h>
#include "logic_bridge.h"
#include "sourcemod.h"
#include "provider.h"
#include <bridge/include/IScriptManager.h>
ConVarManager g_ConVarManager; ConVarManager g_ConVarManager;
#if SOURCE_ENGINE <= SE_DARKMESSIAH
#define CallGlobalChangeCallbacks CallGlobalChangeCallback
#endif
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
#else
SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
#endif
#if SOURCE_ENGINE != SE_DARKMESSIAH
SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
#endif
const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String}; const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
typedef List<const ConVar *> ConVarList; typedef List<const ConVar *> ConVarList;
NameHashSet<ConVarInfo *> convar_cache; KTrie<ConVarInfo *> convar_cache;
class ConVarReentrancyGuard class ConVarReentrancyGuard
{ {
@ -77,7 +92,7 @@ public:
ConVarReentrancyGuard *ConVarReentrancyGuard::chain = NULL; ConVarReentrancyGuard *ConVarReentrancyGuard::chain = NULL;
ConVarManager::ConVarManager() : m_ConVarType(0) ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false)
{ {
} }
@ -95,17 +110,28 @@ void ConVarManager::OnSourceModStartup(bool late)
sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
/* Create the 'ConVar' handle type */ /* Create the 'ConVar' handle type */
m_ConVarType = handlesys->CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL); m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
} }
void ConVarManager::OnSourceModAllInitialized() void ConVarManager::OnSourceModAllInitialized()
{ {
g_Players.AddClientListener(this); /**
* Episode 2 has this function by default, but the older versions do not.
*/
#if SOURCE_ENGINE == SE_EPISODEONE
if (g_SMAPI->GetGameDLLVersion() >= 6)
{
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_bIsDLLQueryHooked = true;
}
#endif
scripts->AddPluginsListener(this); SH_ADD_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
g_PluginSys.AddPluginsListener(this);
/* Add the 'convars' option to the 'sm' console command */ /* Add the 'convars' option to the 'sm' console command */
rootmenu->AddRootConsoleCommand3("cvars", "View convars created by a plugin", this); g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
} }
void ConVarManager::OnSourceModShutdown() void ConVarManager::OnSourceModShutdown()
@ -120,10 +146,10 @@ void ConVarManager::OnSourceModShutdown()
iter = m_ConVars.erase(iter); iter = m_ConVars.erase(iter);
handlesys->FreeHandle(pInfo->handle, &sec); g_HandleSys.FreeHandle(pInfo->handle, &sec);
if (pInfo->pChangeForward != NULL) if (pInfo->pChangeForward != NULL)
{ {
forwardsys->ReleaseForward(pInfo->pChangeForward); g_Forwards.ReleaseForward(pInfo->pChangeForward);
} }
if (pInfo->sourceMod) if (pInfo->sourceMod)
{ {
@ -151,23 +177,79 @@ void ConVarManager::OnSourceModShutdown()
} }
convar_cache.clear(); convar_cache.clear();
g_Players.RemoveClientListener(this); #if SOURCE_ENGINE != SE_DARKMESSIAH
/* Unhook things */
if (m_bIsDLLQueryHooked)
{
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_bIsDLLQueryHooked = false;
}
else if (m_bIsVSPQueryHooked)
{
SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_bIsVSPQueryHooked = false;
}
#endif
SH_REMOVE_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
/* Remove the 'convars' option from the 'sm' console command */ /* Remove the 'convars' option from the 'sm' console command */
rootmenu->RemoveRootConsoleCommand("cvars", this); g_RootMenu.RemoveRootConsoleCommand("cvars", this);
scripts->RemovePluginsListener(this); g_PluginSys.RemovePluginsListener(this);
/* Remove the 'ConVar' handle type */ /* Remove the 'ConVar' handle type */
handlesys->RemoveType(m_ConVarType, g_pCoreIdent); g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
}
/**
* Orange Box will never use this.
*/
void ConVarManager::OnSourceModVSPReceived()
{
/**
* Don't bother if the DLL is already hooked.
*/
if (m_bIsDLLQueryHooked)
{
return;
}
/* For later MM:S versions, use the updated API, since it's cleaner. */
#if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
int engine = g_SMAPI->GetSourceEngineBuild();
if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
{
return;
}
#else
if (g_HL2.IsOriginalEngine() || vsp_version < 2)
{
return;
}
#endif
#if SOURCE_ENGINE != SE_DARKMESSIAH
SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_bIsVSPQueryHooked = true;
#endif
} }
bool convar_cache_lookup(const char *name, ConVarInfo **pVar) bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
{ {
return convar_cache.retrieve(name, pVar); ConVarInfo **pLookup = convar_cache.retrieve(name);
if (pLookup != NULL)
{
*pVar = *pLookup;
return true;
}
else
{
return false;
}
} }
void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name) void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
{ {
/* Only check convars that have not been created by SourceMod's core */ /* Only check convars that have not been created by SourceMod's core */
ConVarInfo *pInfo; ConVarInfo *pInfo;
@ -183,7 +265,7 @@ void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *na
convar_cache.remove(name); convar_cache.remove(name);
/* Now make sure no plugins are referring to this pointer */ /* Now make sure no plugins are referring to this pointer */
IPluginIterator *pl_iter = scripts->GetPluginIterator(); IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
while (pl_iter->MorePlugins()) while (pl_iter->MorePlugins())
{ {
IPlugin *pl = pl_iter->GetPlugin(); IPlugin *pl = pl_iter->GetPlugin();
@ -199,7 +281,7 @@ void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *na
} }
/* Free resources */ /* Free resources */
handlesys->FreeHandle(pInfo->handle, &sec); g_HandleSys.FreeHandle(pInfo->handle, &sec);
delete pInfo; delete pInfo;
} }
@ -214,35 +296,14 @@ void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
delete pConVarList; delete pConVarList;
} }
const IPluginRuntime * pRuntime = plugin->GetRuntime();
/* Remove convar queries for this plugin that haven't returned results yet */ /* Remove convar queries for this plugin that haven't returned results yet */
for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();) for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
{ {
ConVarQuery &query = (*iter); ConVarQuery &query = (*iter);
if (query.pCallback->GetParentRuntime() == pRuntime) if (query.pCallback->GetParentRuntime() == plugin->GetRuntime())
{ {
iter = m_ConVarQueries.erase(iter); m_ConVarQueries.erase(iter);
continue;
} }
++iter;
}
}
void ConVarManager::OnClientDisconnected(int client)
{
/* Remove convar queries for this client that haven't returned results yet */
for (List<ConVarQuery>::iterator iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end();)
{
ConVarQuery &query = (*iter);
if (query.client == client)
{
iter = m_ConVarQueries.erase(iter);
continue;
}
++iter;
} }
} }
@ -256,27 +317,20 @@ bool ConVarManager::GetHandleApproxSize(HandleType_t type, void *object, unsigne
return true; return true;
} }
void ConVarManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) void ConVarManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
{ {
int argcount = command->ArgC(); int argcount = command.ArgC();
if (argcount >= 3) if (argcount >= 3)
{ {
bool wantReset = false;
/* Get plugin index that was passed */ /* Get plugin index that was passed */
const char *arg = command->Arg(2); const char *arg = command.Arg(2);
if (argcount >= 4 && strcmp(arg, "reset") == 0)
{
wantReset = true;
arg = command->Arg(3);
}
/* Get plugin object */ /* Get plugin object */
IPlugin *plugin = scripts->FindPluginByConsoleArg(arg); CPlugin *plugin = g_PluginSys.FindPluginByConsoleArg(arg);
if (!plugin) if (!plugin)
{ {
UTIL_ConsolePrint("[SM] Plugin \"%s\" was not found.", arg); g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", arg);
return; return;
} }
@ -290,38 +344,25 @@ void ConVarManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs
/* If no convar list... */ /* If no convar list... */
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList)) if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
{ {
UTIL_ConsolePrint("[SM] No convars found for: %s", plname); g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
return; return;
} }
if (!wantReset) g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
{ g_RootMenu.ConsolePrint(" %-32.31s %s", "[Name]", "[Value]");
UTIL_ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
UTIL_ConsolePrint(" %-32.31s %s", "[Name]", "[Value]"); /* Iterate convar list and display each one */
}
/* Iterate convar list and display/reset each one */
for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++) for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
{ {
/*const */ConVar *pConVar = const_cast<ConVar *>(*iter); const ConVar *pConVar = (*iter);
if (!wantReset) g_RootMenu.ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
{
UTIL_ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
} else {
pConVar->Revert();
}
}
if (wantReset)
{
UTIL_ConsolePrint("[SM] Reset %d convars for: %s", pConVarList->size(), plname);
} }
return; return;
} }
/* Display usage of subcommand */ /* Display usage of subcommand */
UTIL_ConsolePrint("[SM] Usage: sm cvars [reset] <plugin #>"); g_RootMenu.ConsolePrint("[SM] Usage: sm cvars <plugin #>");
} }
Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *description, int flags, bool hasMin, float min, bool hasMax, float max) Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *description, int flags, bool hasMin, float min, bool hasMax, float max)
@ -353,7 +394,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->pVar = pConVar; pInfo->pVar = pConVar;
/* If we don't, then create a new handle from the convar */ /* If we don't, then create a new handle from the convar */
hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL); hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE) if (hndl == BAD_HANDLE)
{ {
delete pInfo; delete pInfo;
@ -384,7 +425,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo->pChangeForward = NULL; pInfo->pChangeForward = NULL;
/* Create a handle from the new convar */ /* Create a handle from the new convar */
hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL); hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE) if (hndl == BAD_HANDLE)
{ {
delete pInfo; delete pInfo;
@ -413,13 +454,7 @@ Handle_t ConVarManager::FindConVar(const char *name)
ConVarInfo *pInfo; ConVarInfo *pInfo;
Handle_t hndl; Handle_t hndl;
/* Check convar cache to find out if we already have a handle */ /* Search for convar */
if (convar_cache_lookup(name, &pInfo))
{
return pInfo->handle;
}
/* Couldn't find it in cache, so search for it */
pConVar = icvar->FindVar(name); pConVar = icvar->FindVar(name);
/* If it doesn't exist, then return an invalid handle */ /* If it doesn't exist, then return an invalid handle */
@ -428,6 +463,12 @@ Handle_t ConVarManager::FindConVar(const char *name)
return BAD_HANDLE; return BAD_HANDLE;
} }
/* At this point, the convar exists. So, find out if we already have a handle */
if (convar_cache_lookup(name, &pInfo))
{
return pInfo->handle;
}
/* Create and initialize ConVarInfo structure */ /* Create and initialize ConVarInfo structure */
pInfo = new ConVarInfo(); pInfo = new ConVarInfo();
pInfo->sourceMod = false; pInfo->sourceMod = false;
@ -435,7 +476,7 @@ Handle_t ConVarManager::FindConVar(const char *name)
pInfo->pVar = pConVar; pInfo->pVar = pConVar;
/* If we don't have a handle, then create a new one */ /* If we don't have a handle, then create a new one */
hndl = handlesys->CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL); hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE) if (hndl == BAD_HANDLE)
{ {
delete pInfo; delete pInfo;
@ -493,7 +534,7 @@ void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction
/* If forward does not exist, create it */ /* If forward does not exist, create it */
if (!pForward) if (!pForward)
{ {
pForward = forwardsys->CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS); pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
pInfo->pChangeForward = pForward; pInfo->pChangeForward = pForward;
} }
@ -533,7 +574,7 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti
!ConVarReentrancyGuard::IsCvarInChain(pConVar)) !ConVarReentrancyGuard::IsCvarInChain(pConVar))
{ {
/* Free this forward */ /* Free this forward */
forwardsys->ReleaseForward(pForward); g_Forwards.ReleaseForward(pForward);
pInfo->pChangeForward = NULL; pInfo->pChangeForward = NULL;
} }
} }
@ -541,13 +582,26 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti
QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl) QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
{ {
QueryCvarCookie_t cookie = sCoreProviderImpl.QueryClientConVar(IndexOfEdict(pPlayer), name); QueryCvarCookie_t cookie = 0;
if (pCallback != NULL) #if SOURCE_ENGINE != SE_DARKMESSIAH
/* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
if (m_bIsDLLQueryHooked)
{ {
ConVarQuery query = { cookie, pCallback, (cell_t) hndl, IndexOfEdict(pPlayer) }; cookie = engine->StartQueryCvarValue(pPlayer, name);
m_ConVarQueries.push_back(query);
} }
else if (m_bIsVSPQueryHooked)
{
cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
}
else
{
return InvalidQueryCvarCookie;
}
ConVarQuery query = {cookie, pCallback, hndl};
m_ConVarQueries.push_back(query);
#endif
return cookie; return cookie;
} }
@ -559,7 +613,7 @@ void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar
bool inserted = false; bool inserted = false;
const char *orig = pConVar->GetName(); const char *orig = pConVar->GetName();
IPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext()); IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
/* Check plugin for an existing convar list */ /* Check plugin for an existing convar list */
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList)) if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
@ -590,7 +644,11 @@ void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar
} }
} }
#if SOURCE_ENGINE >= SE_ORANGEBOX
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue) void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
#else
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
#endif
{ {
/* If the values are the same, exit early in order to not trigger callbacks */ /* If the values are the same, exit early in order to not trigger callbacks */
if (strcmp(pConVar->GetString(), oldValue) == 0) if (strcmp(pConVar->GetString(), oldValue) == 0)
@ -610,8 +668,16 @@ void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float
if (pInfo->changeListeners.size() != 0) if (pInfo->changeListeners.size() != 0)
{ {
for (auto i = pInfo->changeListeners.begin(); i != pInfo->changeListeners.end(); i++) for (List<IConVarChangeListener *>::iterator i = pInfo->changeListeners.begin();
i != pInfo->changeListeners.end();
i++)
{
#if SOURCE_ENGINE >= SE_ORANGEBOX
(*i)->OnConVarChanged(pConVar, oldValue, flOldValue); (*i)->OnConVarChanged(pConVar, oldValue, flOldValue);
#else
(*i)->OnConVarChanged(pConVar, oldValue, atof(oldValue));
#endif
}
} }
if (pForward != NULL) if (pForward != NULL)
@ -628,15 +694,11 @@ void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float
bool ConVarManager::IsQueryingSupported() bool ConVarManager::IsQueryingSupported()
{ {
return sCoreProviderImpl.IsClientConVarQueryingSupported(); return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked);
} }
#if SOURCE_ENGINE != SE_DARKMESSIAH #if SOURCE_ENGINE != SE_DARKMESSIAH
void ConVarManager::OnClientQueryFinished(QueryCvarCookie_t cookie, void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
int client,
EQueryCvarValueStatus result,
const char *cvarName,
const char *cvarValue)
{ {
IPluginFunction *pCallback = NULL; IPluginFunction *pCallback = NULL;
cell_t value = 0; cell_t value = 0;
@ -658,7 +720,7 @@ void ConVarManager::OnClientQueryFinished(QueryCvarCookie_t cookie,
cell_t ret; cell_t ret;
pCallback->PushCell(cookie); pCallback->PushCell(cookie);
pCallback->PushCell(client); pCallback->PushCell(IndexOfEdict(pPlayer));
pCallback->PushCell(result); pCallback->PushCell(result);
pCallback->PushString(cvarName); pCallback->PushString(cvarName);
@ -684,7 +746,7 @@ HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
ConVarInfo *pInfo; ConVarInfo *pInfo;
HandleError error; HandleError error;
if ((error = handlesys->ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None) if ((error = g_HandleSys.ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None)
{ {
return error; return error;
} }

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -39,11 +39,13 @@
#include <IForwardSys.h> #include <IForwardSys.h>
#include <IHandleSys.h> #include <IHandleSys.h>
#include <IRootConsoleMenu.h> #include <IRootConsoleMenu.h>
#include <IPlayerHelpers.h>
#include <compat_wrappers.h> #include <compat_wrappers.h>
#include "concmd_cleaner.h" #include "concmd_cleaner.h"
#include "PlayerManager.h"
#include <sm_stringhashmap.h> #if SOURCE_ENGINE == SE_DARKMESSIAH
class EQueryCvarValueStatus;
typedef int QueryCvarCookie_t;
#endif
using namespace SourceHook; using namespace SourceHook;
@ -63,15 +65,6 @@ struct ConVarInfo
IChangeableForward *pChangeForward; /**< Forward associated with convar */ IChangeableForward *pChangeForward; /**< Forward associated with convar */
ConVar *pVar; /**< The actual convar */ ConVar *pVar; /**< The actual convar */
List<IConVarChangeListener *> changeListeners; List<IConVarChangeListener *> changeListeners;
static inline bool matches(const char *name, const ConVarInfo *info)
{
return strcmp(name, info->pVar->GetName()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
}; };
/** /**
@ -82,7 +75,6 @@ struct ConVarQuery
QueryCvarCookie_t cookie; /**< Cookie that identifies query */ QueryCvarCookie_t cookie; /**< Cookie that identifies query */
IPluginFunction *pCallback; /**< Function that will be called when query is finished */ IPluginFunction *pCallback; /**< Function that will be called when query is finished */
cell_t value; /**< Optional value passed to query function */ cell_t value; /**< Optional value passed to query function */
cell_t client; /**< Only used for cleaning up on client disconnection */
}; };
class ConVarManager : class ConVarManager :
@ -90,8 +82,7 @@ class ConVarManager :
public IHandleTypeDispatch, public IHandleTypeDispatch,
public IPluginsListener, public IPluginsListener,
public IRootConsoleCommand, public IRootConsoleCommand,
public IConCommandTracker, public IConCommandTracker
public IClientListener
{ {
public: public:
ConVarManager(); ConVarManager();
@ -100,17 +91,16 @@ public: // SMGlobalClass
void OnSourceModStartup(bool late); void OnSourceModStartup(bool late);
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
void OnSourceModShutdown(); void OnSourceModShutdown();
void OnSourceModVSPReceived();
public: // IHandleTypeDispatch public: // IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object); void OnHandleDestroy(HandleType_t type, void *object);
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
public: // IPluginsListener public: // IPluginsListener
void OnPluginUnloaded(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin);
public: //IRootConsoleCommand public: //IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) override; void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public: //IConCommandTracker public: //IConCommandTracker
void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name) override; void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe);
public: //IClientListener
void OnClientDisconnected(int client);
public: public:
/** /**
* Create a convar and return a handle to it. * Create a convar and return a handle to it.
@ -146,26 +136,34 @@ public:
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar); HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar);
// Called via game hooks.
void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
#if SOURCE_ENGINE != SE_DARKMESSIAH
void OnClientQueryFinished(
QueryCvarCookie_t cookie,
int client,
EQueryCvarValueStatus result,
const char *cvarName,
const char *cvarValue);
#endif
private: private:
/** /**
* Adds a convar to a plugin's list. * Adds a convar to a plugin's list.
*/ */
static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar); static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar);
/**
* Static callback that Valve's ConVar object executes when the convar's value changes.
*/
#if SOURCE_ENGINE >= SE_ORANGEBOX
static void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
#else
static void OnConVarChanged(ConVar *pConVar, const char *oldValue);
#endif
/**
* Callback for when StartQueryCvarValue() has finished.
*/
#if SOURCE_ENGINE != SE_DARKMESSIAH
void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result,
const char *cvarName, const char *cvarValue);
#endif
private: private:
HandleType_t m_ConVarType; HandleType_t m_ConVarType;
List<ConVarInfo *> m_ConVars; List<ConVarInfo *> m_ConVars;
List<ConVarQuery> m_ConVarQueries; List<ConVarQuery> m_ConVarQueries;
bool m_bIsDLLQueryHooked;
bool m_bIsVSPQueryHooked;
}; };
extern ConVarManager g_ConVarManager; extern ConVarManager g_ConVarManager;

View File

@ -44,30 +44,38 @@
#include "Logger.h" #include "Logger.h"
#include "compat_wrappers.h" #include "compat_wrappers.h"
#include "ConsoleDetours.h" #include "ConsoleDetours.h"
#include <IGameConfigs.h> #include "GameConfigs.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "ConCmdManager.h" #include "ConCmdManager.h"
#include "HalfLife2.h" #include "HalfLife2.h"
#include "ConCommandBaseIterator.h" #include "ConCommandBaseIterator.h"
#include "logic_bridge.h" #include "ShareSys.h"
#include "command_args.h"
#include "provider.h"
#include <am-utility.h>
#include <bridge/include/ILogger.h>
#if defined PLATFORM_POSIX #if defined PLATFORM_LINUX
# include <dlfcn.h> # include <dlfcn.h>
# include <sys/mman.h> # include <sys/mman.h>
# include <stdint.h> # include <stdint.h>
# include <unistd.h> # include <unistd.h>
#endif #endif
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SH_IMPL_VERSION >= 5
# if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
#else # else
SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false); SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
# endif
#else
# if SH_IMPL_VERSION >= 4
extern int __SourceHook_FHVPAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>,bool);
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
# else
extern bool __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
# endif
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
#endif #endif
#if SH_IMPL_VERSION >= 4
class GenericCommandHooker : public IConCommandLinkListener class GenericCommandHooker : public IConCommandLinkListener
{ {
struct HackInfo struct HackInfo
@ -123,16 +131,16 @@ class GenericCommandHooker : public IConCommandLinkListener
} }
} }
#if SOURCE_ENGINE >= SE_ORANGEBOX # if SOURCE_ENGINE >= SE_ORANGEBOX
void Dispatch(const CCommand& args) void Dispatch(const CCommand& args)
#else # else
void Dispatch() void Dispatch()
#endif # endif
{ {
cell_t res = ConsoleDetours::Dispatch(META_IFACEPTR(ConCommand) cell_t res = ConsoleDetours::Dispatch(META_IFACEPTR(ConCommand)
#if SOURCE_ENGINE >= SE_ORANGEBOX # if SOURCE_ENGINE >= SE_ORANGEBOX
, args , args
#endif # endif
); );
if (res >= Pl_Handled) if (res >= Pl_Handled)
RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_SUPERCEDE);
@ -182,7 +190,7 @@ class GenericCommandHooker : public IConCommandLinkListener
size_t index; size_t index;
if (!FindVtable(vtable, index)) if (!FindVtable(vtable, index))
{ {
logger->LogError("Console detour tried to unhook command \"%s\" but it wasn't found", pBase->GetName()); g_Logger.LogError("Console detour tried to unhook command \"%s\" but it wasn't found", pBase->GetName());
return; return;
} }
@ -206,7 +214,7 @@ public:
if (dispatch.thisptroffs < 0) if (dispatch.thisptroffs < 0)
{ {
logger->LogError("Command filter could not determine ConCommand layout"); g_Logger.LogError("Command filter could not determine ConCommand layout");
return false; return false;
} }
@ -215,7 +223,7 @@ public:
if (!vtables.size()) if (!vtables.size())
{ {
logger->LogError("Command filter could not find any cvars!"); g_Logger.LogError("Command filter could not find any cvars!");
return false; return false;
} }
@ -249,6 +257,308 @@ public:
} }
}; };
/**
* END DVP HOOK VERSION
*/
#else /* SH_IMPL_VERSION >= 4 */
/**
* BEGIN ENGINE DETOUR VERSION
*/
# include <jit/jit_helpers.h>
# include <jit/x86/x86_macros.h>
class GenericCommandHooker
{
struct Patch
{
Patch() : applied(false)
{
}
bool applied;
void *base;
unsigned char search[16];
size_t search_len;
size_t offset;
size_t saveat;
int regparam;
void *detour;
};
Patch ces;
Patch cgc;
public:
static void DelayedActivation(void *inparam)
{
GenericCommandHooker *cdtrs = reinterpret_cast<GenericCommandHooker*>(inparam);
/* Safe to re-enter because the frame queue is lock+swapped. */
if ((!cdtrs->ces.applied || !cdtrs->cgc.applied) &&
g_HL2.PeekCommandStack() != NULL)
{
g_SourceMod.AddFrameAction(GenericCommandHooker::DelayedActivation, cdtrs);
return;
}
if (!cdtrs->ces.applied)
cdtrs->ApplyPatch(&cdtrs->ces);
if (!cdtrs->cgc.applied)
cdtrs->ApplyPatch(&cdtrs->cgc);
}
bool Enable()
{
const char *platform = NULL;
# if defined(PLATFORM_WINDOWS)
platform = "Windows";
# else
void *addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
Dl_info info;
if (!dladdr(addrInBase, &info))
{
g_Logger.LogError("Command filter could not find engine name");
return false;
}
if (strstr(info.dli_fname, "engine_i486"))
{
platform = "Linux_486";
}
else if (strstr(info.dli_fname, "engine_i686"))
{
platform = "Linux_686";
}
else if (strstr(info.dli_fname, "engine_amd"))
{
platform = "Linux_AMD";
}
else
{
g_Logger.LogError("Command filter could not determine engine (%s)", info.dli_fname);
return false;
}
# endif
if (!PrepPatch("Cmd_ExecuteString", "CES", platform, &ces))
return false;
if (!PrepPatch("CGameClient::ExecuteString", "CGC", platform, &cgc))
return false;
if (g_HL2.PeekCommandStack() != NULL)
{
g_SourceMod.AddFrameAction(GenericCommandHooker::DelayedActivation, this);
return true;
}
ApplyPatch(&ces);
ApplyPatch(&cgc);
return true;
}
void Disable()
{
UndoPatch(&ces);
UndoPatch(&cgc);
}
private:
# if !defined PLATFORM_WINDOWS
# define PAGE_READWRITE PROT_READ|PROT_WRITE
# define PAGE_EXECUTE_READ PROT_READ|PROT_EXEC
static inline uintptr_t AddrToPage(uintptr_t address)
{
return (address & ~(uintptr_t(sysconf(_SC_PAGE_SIZE) - 1)));
}
# endif
void Protect(void *addr, size_t length, int prot)
{
# if defined PLATFORM_WINDOWS
DWORD ignore;
VirtualProtect(addr, length, prot, &ignore);
# else
uintptr_t startPage = AddrToPage(uintptr_t(addr));
length += (uintptr_t(addr) - startPage);
mprotect((void*)startPage, length, prot);
# endif
}
void UndoPatch(Patch *patch)
{
if (!patch->applied || patch->detour == NULL)
return;
g_pSourcePawn->FreePageMemory(patch->detour);
unsigned char *source = (unsigned char *)patch->base + patch->offset;
Protect(source, patch->search_len, PAGE_READWRITE);
for (size_t i = 0; i < patch->search_len; i++)
source[i] = patch->search[i];
Protect(source, patch->search_len, PAGE_EXECUTE_READ);
}
void ApplyPatch(Patch *patch)
{
assert(!patch->applied);
size_t length = 0;
void *callback = (void*)&ConsoleDetours::Dispatch;
/* Bogus assignment to make compiler is doing the right thing. */
patch->detour = callback;
/* Assemgle the detour. */
JitWriter writer;
writer.outbase = NULL;
writer.outptr = NULL;
do
{
/* Need a specific register, or value on stack? */
if (patch->regparam != -1)
IA32_Push_Reg(&writer, patch->regparam);
/* Call real function. */
IA32_Write_Jump32_Abs(&writer, IA32_Call_Imm32(&writer, 0), callback);
/* Restore stack. */
if (patch->regparam != -1)
IA32_Pop_Reg(&writer, patch->regparam);
/* Copy any saved bytes */
if (patch->saveat)
{
for (size_t i = patch->saveat; i < patch->search_len; i++)
{
writer.write_ubyte(patch->search[i]);
}
}
/* Jump back to the caller. */
unsigned char *target = (unsigned char *)patch->base + patch->offset + patch->search_len;
IA32_Jump_Imm32_Abs(&writer, target);
/* Assemble, if we can. */
if (writer.outbase == NULL)
{
length = writer.outptr - writer.outbase;
patch->detour = g_pSourcePawn->AllocatePageMemory(length);
if (patch->detour == NULL)
{
g_Logger.LogError("Ran out of memory!");
return;
}
g_pSourcePawn->SetReadWrite(patch->detour);
writer.outbase = (jitcode_t)patch->detour;
writer.outptr = writer.outbase;
}
else
{
break;
}
} while (true);
g_pSourcePawn->SetReadExecute(patch->detour);
unsigned char *source = (unsigned char *)patch->base + patch->offset;
Protect(source, 6, PAGE_READWRITE);
source[0] = 0xFF;
source[1] = 0x25;
*(void **)&source[2] = &patch->detour;
Protect(source, 6, PAGE_EXECUTE_READ);
patch->applied = true;
}
bool PrepPatch(const char *signature, const char *name, const char *platform, Patch *patch)
{
/* Get the base address of the function. */
if (!g_pGameConf->GetMemSig(signature, &patch->base) || patch->base == NULL)
{
g_Logger.LogError("Command filter could not find signature: %s", signature);
return false;
}
const char *value;
char keyname[255];
/* Get the verification bytes that will be written over. */
UTIL_Format(keyname, sizeof(keyname), "%s_Patch_%s", name, platform);
if ((value = g_pGameConf->GetKeyValue(keyname)) == NULL)
{
g_Logger.LogError("Command filter could not find key: %s", keyname);
return false;
}
patch->search_len = UTIL_DecodeHexString(patch->search, sizeof(patch->search), value);
if (patch->search_len < 6)
{
g_Logger.LogError("Error decoding %s value, or not long enough", keyname);
return false;
}
/* Get the offset into the function. */
UTIL_Format(keyname, sizeof(keyname), "%s_Offset_%s", name, platform);
if ((value = g_pGameConf->GetKeyValue(keyname)) == NULL)
{
g_Logger.LogError("Command filter could not find key: %s", keyname);
return false;
}
patch->offset = atoi(value);
if (patch->offset > 20000)
{
g_Logger.LogError("Command filter %s value is bogus", keyname);
return false;
}
/* Get the number of bytes to save from what was written over. */
patch->saveat = 0;
UTIL_Format(keyname, sizeof(keyname), "%s_Save_%s", name, platform);
if ((value = g_pGameConf->GetKeyValue(keyname)) != NULL)
{
patch->saveat = atoi(value);
if (patch->saveat >= patch->search_len)
{
g_Logger.LogError("Command filter %s value is too large", keyname);
return false;
}
}
/* Get register for parameter, if any. */
patch->regparam = -1;
UTIL_Format(keyname, sizeof(keyname), "%s_Reg_%s", name, platform);
if ((value = g_pGameConf->GetKeyValue(keyname)) != NULL)
{
patch->regparam = atoi(value);
}
/* Everything loaded from gamedata, make sure the patch will succeed. */
unsigned char *address = (unsigned char *)patch->base + patch->offset;
for (size_t i = 0; i < patch->search_len; i++)
{
if (address[i] != patch->search[i])
{
g_Logger.LogError("Command filter %s has changed (byte %x is not %x sub-offset %d)",
name, address[i], patch->search[i], i);
return false;
}
}
return true;
}
};
static bool dummy_hook_set = false;
void DummyHook()
{
if (dummy_hook_set)
{
dummy_hook_set = false;
RETURN_META(MRES_SUPERCEDE);
}
}
#endif
/** /**
* BEGIN THE ACTUALLY GENERIC CODE. * BEGIN THE ACTUALLY GENERIC CODE.
*/ */
@ -264,21 +574,23 @@ ConsoleDetours::ConsoleDetours() : status(FeatureStatus_Unknown)
void ConsoleDetours::OnSourceModAllInitialized() void ConsoleDetours::OnSourceModAllInitialized()
{ {
m_pForward = forwardsys->CreateForwardEx("OnAnyCommand", ET_Hook, 3, NULL, Param_Cell, m_pForward = g_Forwards.CreateForwardEx("OnAnyCommand", ET_Hook, 3, NULL, Param_Cell,
Param_String, Param_Cell); Param_String, Param_Cell);
sharesys->AddCapabilityProvider(NULL, this, FEATURECAP_COMMANDLISTENER); g_ShareSys.AddCapabilityProvider(NULL, this, FEATURECAP_COMMANDLISTENER);
} }
void ConsoleDetours::OnSourceModShutdown() void ConsoleDetours::OnSourceModShutdown()
{ {
for (StringHashMap<IChangeableForward *>::iterator iter = m_Listeners.iter(); List<Listener*>::iterator iter = m_Listeners.begin();
!iter.empty(); while (iter != m_Listeners.end())
iter.next())
{ {
forwardsys->ReleaseForward(iter->value); Listener *listener = (*iter);
g_Forwards.ReleaseForward(listener->forward);
delete listener;
iter = m_Listeners.erase(iter);
} }
forwardsys->ReleaseForward(m_pForward); g_Forwards.ReleaseForward(m_pForward);
s_GenericHooker.Disable(); s_GenericHooker.Disable();
} }
@ -307,15 +619,22 @@ bool ConsoleDetours::AddListener(IPluginFunction *fun, const char *command)
} }
else else
{ {
ke::AutoPtr<char[]> str(UTIL_ToLowerCase(command)); const char *str = UTIL_ToLowerCase(command);
IChangeableForward *forward; Listener *listener;
if (!m_Listeners.retrieve(str.get(), &forward)) Listener **plistener = m_CmdLookup.retrieve(str);
if (plistener == NULL)
{ {
forward = forwardsys->CreateForwardEx(NULL, ET_Hook, 3, NULL, Param_Cell, listener = new Listener;
Param_String, Param_Cell); listener->forward = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, NULL, Param_Cell,
m_Listeners.insert(str.get(), forward); Param_String, Param_Cell);
m_CmdLookup.insert(str, listener);
} }
forward->AddFunction(fun); else
{
listener = *plistener;
}
listener->forward->AddFunction(fun);
delete [] str;
} }
return true; return true;
@ -329,24 +648,22 @@ bool ConsoleDetours::RemoveListener(IPluginFunction *fun, const char *command)
} }
else else
{ {
ke::AutoPtr<char[]> str(UTIL_ToLowerCase(command)); const char *str = UTIL_ToLowerCase(command);
IChangeableForward *forward; Listener *listener;
if (!m_Listeners.retrieve(str.get(), &forward)) Listener **plistener = m_CmdLookup.retrieve(str);
delete [] str;
if (plistener == NULL)
return false; return false;
return forward->RemoveFunction(fun); listener = *plistener;
return listener->forward->RemoveFunction(fun);
} }
} }
cell_t ConsoleDetours::InternalDispatch(int client, const ICommandArgs *args) cell_t ConsoleDetours::InternalDispatch(int client, const CCommand& args)
{ {
char name[255]; char name[255];
const char *realname = args->Arg(0); const char *realname = args.Arg(0);
size_t len = strlen(realname); size_t len = strlen(realname);
// Disallow command strings that are too long, for now.
if (len >= sizeof(name) - 1)
return Pl_Continue;
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
{ {
if (realname[i] >= 'A' && realname[i] <= 'Z') if (realname[i] >= 'A' && realname[i] <= 'Z')
@ -359,27 +676,28 @@ cell_t ConsoleDetours::InternalDispatch(int client, const ICommandArgs *args)
cell_t result = Pl_Continue; cell_t result = Pl_Continue;
m_pForward->PushCell(client); m_pForward->PushCell(client);
m_pForward->PushString(name); m_pForward->PushString(name);
m_pForward->PushCell(args->ArgC() - 1); m_pForward->PushCell(args.ArgC() - 1);
m_pForward->Execute(&result, NULL); m_pForward->Execute(&result, NULL);
/* Don't let plugins block this. */ /* Don't let plugins block this. */
if (strcmp(name, "sm") == 0) if (strcmp(name, "sm") == 0)
result = Pl_Continue; result = Pl_Continue;
if (result >= Pl_Handled) if (result >= Pl_Stop)
return result; return result;
IChangeableForward *forward; Listener **plistener = m_CmdLookup.retrieve(name);
if (!m_Listeners.retrieve(name, &forward)) if (plistener == NULL)
return result; return result;
if (forward->GetFunctionCount() == 0) Listener *listener = *plistener;
if (listener->forward->GetFunctionCount() == 0)
return result; return result;
cell_t result2 = Pl_Continue; cell_t result2 = Pl_Continue;
forward->PushCell(client); listener->forward->PushCell(client);
forward->PushString(name); listener->forward->PushString(name);
forward->PushCell(args->ArgC() - 1); listener->forward->PushCell(args.ArgC() - 1);
forward->Execute(&result2, NULL); listener->forward->Execute(&result2, NULL);
if (result2 > result) if (result2 > result)
result = result2; result = result2;
@ -398,12 +716,27 @@ cell_t ConsoleDetours::Dispatch(ConCommand *pBase)
{ {
CCommand args; CCommand args;
#endif #endif
EngineArgs cargs(args); g_HL2.PushCommandStack(&args);
cell_t res; cell_t res = g_ConsoleDetours.InternalDispatch(g_ConCmds.GetCommandClient(), args);
g_HL2.PopCommandStack();
#if SH_IMPL_VERSION < 4
if (res >= Pl_Handled)
{ {
AutoEnterCommand autoEnterCommand(&cargs); /* See bug 4020 - we can't optimize this because looking at the vtable
res = g_ConsoleDetours.InternalDispatch(sCoreProviderImpl.CommandClient(), &cargs); * is probably more expensive, since there's no SH_GET_ORIG_VFNPTR_ENTRY.
*/
SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pBase, DummyHook, false);
dummy_hook_set = true;
pBase->Dispatch();
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, pBase, DummyHook, false);
} }
else
{
/* Make sure the command gets invoked. See bug 4019 on making this better. */
pBase->Dispatch();
}
#endif
return res; return res;
} }

View File

@ -33,12 +33,8 @@
#include "sm_globals.h" #include "sm_globals.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include <IForwardSys.h> #include "ForwardSys.h"
#include <sm_stringhashmap.h> #include <sm_trie_tpl.h>
namespace SourceMod {
class ICommandArgs;
} // namespace SourceMod
class ConsoleDetours : class ConsoleDetours :
public SMGlobalClass, public SMGlobalClass,
@ -47,6 +43,10 @@ class ConsoleDetours :
friend class PlayerManager; friend class PlayerManager;
friend class GenericCommandHooker; friend class GenericCommandHooker;
struct Listener
{
IChangeableForward *forward;
};
public: public:
ConsoleDetours(); ConsoleDetours();
public: //SMGlobalClass public: //SMGlobalClass
@ -58,7 +58,7 @@ public:
bool AddListener(IPluginFunction *fun, const char *command); bool AddListener(IPluginFunction *fun, const char *command);
bool RemoveListener(IPluginFunction *fun, const char *command); bool RemoveListener(IPluginFunction *fun, const char *command);
private: private:
cell_t InternalDispatch(int client, const SourceMod::ICommandArgs *args); cell_t InternalDispatch(int client, const CCommand& args);
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
static cell_t Dispatch(ConCommand *pBase, const CCommand& args); static cell_t Dispatch(ConCommand *pBase, const CCommand& args);
#else #else
@ -73,7 +73,8 @@ public:
private: private:
FeatureStatus status; FeatureStatus status;
IChangeableForward *m_pForward; IChangeableForward *m_pForward;
StringHashMap<IChangeableForward *> m_Listeners; KTrie<Listener*> m_CmdLookup;
List<Listener*> m_Listeners;
}; };
extern ConsoleDetours g_ConsoleDetours; extern ConsoleDetours g_ConsoleDetours;

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -33,20 +33,14 @@
#include "CoreConfig.h" #include "CoreConfig.h"
#include "sourcemod.h" #include "sourcemod.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "sm_stringutil.h" #include "sm_srvcmds.h"
#include "Logger.h"
#include "frame_hooks.h"
#include "logic_bridge.h"
#include "compat_wrappers.h"
#include <sourcemod_version.h> #include <sourcemod_version.h>
#include <amtl/os/am-path.h> #include "sm_stringutil.h"
#include <amtl/os/am-fsutil.h> #include "LibrarySys.h"
#include <sh_list.h> #include "Logger.h"
#include <IForwardSys.h> #include "PluginSys.h"
#include <bridge/include/IScriptManager.h> #include "ForwardSys.h"
#include <bridge/include/ILogger.h> #include "frame_hooks.h"
using namespace SourceHook;
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
ConVar sm_corecfgfile("sm_corecfgfile", "addons\\sourcemod\\configs\\core.cfg", 0, "SourceMod core configuration file"); ConVar sm_corecfgfile("sm_corecfgfile", "addons\\sourcemod\\configs\\core.cfg", 0, "SourceMod core configuration file");
@ -70,9 +64,17 @@ void CheckAndFinalizeConfigs();
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
void Hook_ExecDispatchPre(const CCommand &cmd) void Hook_ExecDispatchPre(const CCommand &cmd)
#else #elif SOURCE_ENGINE == SE_DARKMESSIAH
SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false); SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
void Hook_ExecDispatchPre() void Hook_ExecDispatchPre()
#else
# if SH_IMPL_VERSION >= 4
extern int __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
# else
extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
# endif
extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
void Hook_ExecDispatchPre()
#endif #endif
{ {
#if SOURCE_ENGINE <= SE_DARKMESSIAH #if SOURCE_ENGINE <= SE_DARKMESSIAH
@ -111,13 +113,13 @@ void CheckAndFinalizeConfigs()
void CoreConfig::OnSourceModAllInitialized() void CoreConfig::OnSourceModAllInitialized()
{ {
rootmenu->AddRootConsoleCommand3("config", "Set core configuration options", this); g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
g_pOnServerCfg = forwardsys->CreateForward("OnServerCfg", ET_Ignore, 0, NULL); g_pOnServerCfg = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
g_pOnConfigsExecuted = forwardsys->CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL); g_pOnConfigsExecuted = g_Forwards.CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
g_pOnAutoConfigsBuffered = forwardsys->CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL); g_pOnAutoConfigsBuffered = g_Forwards.CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL);
} }
CoreConfig::CoreConfig() CoreConfig::CoreConfig() : m_Strings(512)
{ {
} }
@ -127,15 +129,15 @@ CoreConfig::~CoreConfig()
void CoreConfig::OnSourceModShutdown() void CoreConfig::OnSourceModShutdown()
{ {
rootmenu->RemoveRootConsoleCommand("config", this); g_RootMenu.RemoveRootConsoleCommand("config", this);
forwardsys->ReleaseForward(g_pOnServerCfg); g_Forwards.ReleaseForward(g_pOnServerCfg);
forwardsys->ReleaseForward(g_pOnConfigsExecuted); g_Forwards.ReleaseForward(g_pOnConfigsExecuted);
forwardsys->ReleaseForward(g_pOnAutoConfigsBuffered); g_Forwards.ReleaseForward(g_pOnAutoConfigsBuffered);
if (g_pExecPtr != NULL) if (g_pExecPtr != NULL)
{ {
SH_REMOVE_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPre), false); SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
SH_REMOVE_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPost), true); SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
g_pExecPtr = NULL; g_pExecPtr = NULL;
} }
} }
@ -160,8 +162,8 @@ void CoreConfig::OnSourceModLevelChange(const char *mapName)
g_pExecPtr = FindCommand("exec"); g_pExecPtr = FindCommand("exec");
if (g_pExecPtr != NULL) if (g_pExecPtr != NULL)
{ {
SH_ADD_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPre), false); SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
SH_ADD_HOOK(ConCommand, Dispatch, g_pExecPtr, SH_STATIC(Hook_ExecDispatchPost), true); SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
} }
else else
{ {
@ -177,45 +179,31 @@ void CoreConfig::OnSourceModLevelChange(const char *mapName)
g_bGotTrigger = false; g_bGotTrigger = false;
} }
void CoreConfig::OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
{ {
int argcount = command->ArgC(); int argcount = command.ArgC();
if (argcount >= 4) if (argcount >= 4)
{ {
const char *option = command->Arg(2); const char *option = command.Arg(2);
const char *value = command->Arg(3); const char *value = command.Arg(3);
char error[255]; char error[255];
ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error)); ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
if (res == ConfigResult_Reject) if (res == ConfigResult_Reject)
{ {
UTIL_ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\". (%s)", option, value, error); g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\" (%s)", option, value, error);
} else if (res == ConfigResult_Ignore) {
g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
} else { } else {
if (res == ConfigResult_Ignore) { g_RootMenu.ConsolePrint("Config option \"%s\" successfully set to \"%s.\"", option, value);
UTIL_ConsolePrint("[SM] WARNING: Config option \"%s\" is not registered.", option);
}
UTIL_ConsolePrint("[SM] Config option \"%s\" set to \"%s\".", option, value);
} }
return;
} else if (argcount >= 3) {
const char *option = command->Arg(2);
const char *value = GetCoreConfigValue(option);
if (value == NULL)
{
UTIL_ConsolePrint("[SM] No such config option \"%s\" exists.", option);
} else {
UTIL_ConsolePrint("[SM] Config option \"%s\" is set to \"%s\".", option, value);
}
return; return;
} }
UTIL_ConsolePrint("[SM] Usage: sm config <option> [value]"); g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> <value>");
} }
void CoreConfig::Initialize() void CoreConfig::Initialize()
@ -232,7 +220,7 @@ void CoreConfig::Initialize()
*/ */
if (corecfg) if (corecfg)
{ {
ke::path::Format(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg); g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
} }
else else
{ {
@ -241,23 +229,24 @@ void CoreConfig::Initialize()
/* Format path to config file */ /* Format path to config file */
if (basepath) if (basepath)
{ {
ke::path::Format(filePath, sizeof(filePath), "%s/%s/%s", g_SourceMod.GetGamePath(), basepath, "configs/core.cfg"); g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s/%s", g_SourceMod.GetGamePath(), basepath, "configs/core.cfg");
} }
else else
{ {
ke::path::Format(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), sm_corecfgfile.GetDefault()); g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), sm_corecfgfile.GetDefault());
} }
} }
/* Reset cached key values */ /* Reset cached key values */
m_KeyValues.clear(); m_KeyValues.clear();
m_Strings.Reset();
/* Parse config file */ /* Parse config file */
if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay) if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
{ {
/* :TODO: This won't actually log or print anything :( - So fix that somehow */ /* :TODO: This won't actually log or print anything :( - So fix that somehow */
const char *error = textparsers->GetSMCErrorString(err); const char *error = textparsers->GetSMCErrorString(err);
logger->LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : ""); g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
} }
} }
@ -269,7 +258,7 @@ SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key,
if (err == ConfigResult_Reject) if (err == ConfigResult_Reject)
{ {
/* This is a fatal error */ /* This is a fatal error */
logger->LogFatal("Config error (key: %s) (value: %s) %s", key, value, error); g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
} }
return SMCResult_Continue; return SMCResult_Continue;
@ -277,7 +266,7 @@ SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key,
ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength) ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
{ {
ConfigResult result = ConfigResult_Ignore; ConfigResult result;
/* Notify! */ /* Notify! */
SMGlobalClass *pBase = SMGlobalClass::head; SMGlobalClass *pBase = SMGlobalClass::head;
@ -285,23 +274,24 @@ ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value,
{ {
if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore) if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
{ {
break; return result;
} }
pBase = pBase->m_pGlobalClassNext; pBase = pBase->m_pGlobalClassNext;
} }
ke::AString vstr(value); m_KeyValues.replace(option, m_Strings.AddString(value));
m_KeyValues.replace(option, ke::Move(vstr));
return result; return ConfigResult_Ignore;
} }
const char *CoreConfig::GetCoreConfigValue(const char *key) const char *CoreConfig::GetCoreConfigValue(const char *key)
{ {
StringHashMap<ke::AString>::Result r = m_KeyValues.find(key); int *pKey = m_KeyValues.retrieve(key);
if (!r.found()) if (pKey == NULL)
{
return NULL; return NULL;
return r->value.chars(); }
return m_Strings.GetString(*pKey);
} }
bool SM_AreConfigsExecuted() bool SM_AreConfigsExecuted()
@ -318,7 +308,7 @@ inline bool IsPathSepChar(char c)
#endif #endif
} }
bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create) bool SM_ExecuteConfig(CPlugin *pl, AutoConfig *cfg, bool can_create)
{ {
bool will_create = false; bool will_create = false;
@ -336,12 +326,12 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder); g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
if (!ke::file::IsDirectory(path)) if (!g_LibSys.IsPathDirectory(path))
{ {
char *cur_ptr = path; char *cur_ptr = path;
size_t len; size_t len;
ke::path::Format(path, sizeof(path), "%s", folder); g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg"); len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
do do
@ -362,12 +352,14 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
{ {
next_ptr = NULL; next_ptr = NULL;
} }
len += ke::path::Format(&build[len], len += g_LibSys.PathFormat(&build[len],
sizeof(build)-len, sizeof(build)-len,
"/%s", "/%s",
cur_ptr); cur_ptr);
if (!ke::file::CreateDirectory(build)) if (!g_LibSys.CreateFolder(build))
{
break; break;
}
cur_ptr = next_ptr; cur_ptr = next_ptr;
} while (cur_ptr); } while (cur_ptr);
} }
@ -379,13 +371,13 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
if (cfg->folder.size()) if (cfg->folder.size())
{ {
ke::path::Format(local, g_LibSys.PathFormat(local,
sizeof(local), sizeof(local),
"%s/%s.cfg", "%s/%s.cfg",
cfg->folder.c_str(), cfg->folder.c_str(),
cfg->autocfg.c_str()); cfg->autocfg.c_str());
} else { } else {
ke::path::Format(local, g_LibSys.PathFormat(local,
sizeof(local), sizeof(local),
"%s.cfg", "%s.cfg",
cfg->autocfg.c_str()); cfg->autocfg.c_str());
@ -393,7 +385,7 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local); g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
bool file_exists = ke::file::IsFile(file); bool file_exists = g_LibSys.IsPathFile(file);
if (!file_exists && will_create) if (!file_exists && will_create)
{ {
List<const ConVar *> *convars = NULL; List<const ConVar *> *convars = NULL;
@ -403,7 +395,7 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
FILE *fp = fopen(file, "wt"); FILE *fp = fopen(file, "wt");
if (fp) if (fp)
{ {
fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SOURCEMOD_VERSION); fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SM_FULL_VERSION);
fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename()); fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
fprintf(fp, "\n\n"); fprintf(fp, "\n\n");
@ -412,11 +404,8 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
for (iter = convars->begin(); iter != convars->end(); iter++) for (iter = convars->begin(); iter != convars->end(); iter++)
{ {
const ConVar *cvar = (*iter); const ConVar *cvar = (*iter);
#if SOURCE_ENGINE >= SE_ORANGEBOX
if (cvar->IsFlagSet(FCVAR_DONTRECORD)) if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
#else
if (cvar->IsBitSet(FCVAR_DONTRECORD))
#endif
{ {
continue; continue;
} }
@ -425,7 +414,7 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
char *dptr = descr; char *dptr = descr;
/* Print comments until there is no more */ /* Print comments until there is no more */
ke::SafeStrcpy(descr, sizeof(descr), cvar->GetHelpText()); strncopy(descr, cvar->GetHelpText(), sizeof(descr));
while (*dptr != '\0') while (*dptr != '\0')
{ {
/* Find the next line */ /* Find the next line */
@ -463,18 +452,13 @@ bool SM_ExecuteConfig(IPlugin *pl, AutoConfig *cfg, bool can_create)
can_create = false; can_create = false;
fclose(fp); fclose(fp);
} }
else
{
logger->LogError("Failed to auto generate config for %s, make sure the directory has write permission.", pl->GetFilename());
return can_create;
}
} }
} }
if (file_exists) if (file_exists)
{ {
char cmd[255]; char cmd[255];
ke::SafeSprintf(cmd, sizeof(cmd), "exec %s\n", local); UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
engine->ServerCommand(cmd); engine->ServerCommand(cmd);
} }
@ -498,10 +482,10 @@ void SM_DoSingleExecFwds(IPluginContext *ctx)
void SM_ConfigsExecuted_Plugin(unsigned int serial) void SM_ConfigsExecuted_Plugin(unsigned int serial)
{ {
IPluginIterator *iter = scripts->GetPluginIterator(); IPluginIterator *iter = g_PluginSys.GetPluginIterator();
while (iter->MorePlugins()) while (iter->MorePlugins())
{ {
IPlugin *plugin = iter->GetPlugin(); CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
if (plugin->GetSerial() == serial) if (plugin->GetSerial() == serial)
{ {
SM_DoSingleExecFwds(plugin->GetBaseContext()); SM_DoSingleExecFwds(plugin->GetBaseContext());
@ -514,7 +498,7 @@ void SM_ConfigsExecuted_Plugin(unsigned int serial)
void SM_ExecuteForPlugin(IPluginContext *ctx) void SM_ExecuteForPlugin(IPluginContext *ctx)
{ {
SMPlugin *plugin = scripts->FindPluginByContext(ctx->GetContext()); CPlugin *plugin = (CPlugin *)g_PluginSys.GetPluginByCtx(ctx->GetContext());
unsigned int num = plugin->GetConfigCount(); unsigned int num = plugin->GetConfigCount();
if (!num) if (!num)
@ -529,7 +513,7 @@ void SM_ExecuteForPlugin(IPluginContext *ctx)
can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create); can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
} }
char cmd[255]; char cmd[255];
ke::SafeSprintf(cmd, sizeof(cmd), "sm_internal 2 %d\n", plugin->GetSerial()); UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
engine->ServerCommand(cmd); engine->ServerCommand(cmd);
} }
} }
@ -543,17 +527,19 @@ void SM_ExecuteAllConfigs()
engine->ServerCommand("exec sourcemod/sourcemod.cfg\n"); engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
AutoPluginList plugins(scripts); IPluginIterator *iter = g_PluginSys.GetPluginIterator();
for (size_t i = 0; i < plugins->size(); i++) while (iter->MorePlugins())
{ {
SMPlugin *plugin = plugins->at(i); CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
unsigned int num = plugin->GetConfigCount(); unsigned int num = plugin->GetConfigCount();
bool can_create = true; bool can_create = true;
for (unsigned int i=0; i<num; i++) for (unsigned int i=0; i<num; i++)
{ {
can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create); can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
} }
iter->NextPlugin();
} }
iter->Release();
g_bGotServerStart = true; g_bGotServerStart = true;
CheckAndFinalizeConfigs(); CheckAndFinalizeConfigs();
@ -576,24 +562,7 @@ void SM_InternalCmdTrigger()
{ {
/* Order is important here. We need to buffer things before we send the command out. */ /* Order is important here. We need to buffer things before we send the command out. */
g_pOnAutoConfigsBuffered->Execute(NULL); g_pOnAutoConfigsBuffered->Execute(NULL);
engine->ServerCommand("sm_internal 1\n"); engine->ServerCommand("sm internal 1\n");
g_PendingInternalPush = false; g_PendingInternalPush = false;
} }
CON_COMMAND(sm_internal, "")
{
#if SOURCE_ENGINE <= SE_DARKMESSIAH
CCommand args;
#endif
if (args.ArgC() < 1)
return;
const char *arg = args.Arg(1);
if (strcmp(arg, "1") == 0) {
SM_ConfigsExecuted_Global();
} else if (strcmp(arg, "2") == 0) {
if (args.ArgC() >= 3)
SM_ConfigsExecuted_Plugin(atoi(args.Arg(2)));
}
}

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -35,8 +35,8 @@
#include "sm_globals.h" #include "sm_globals.h"
#include <ITextParsers.h> #include <ITextParsers.h>
#include <IRootConsoleMenu.h> #include <IRootConsoleMenu.h>
#include <am-string.h> #include <sm_trie_tpl.h>
#include <sm_stringhashmap.h> #include "sm_memtable.h"
using namespace SourceMod; using namespace SourceMod;
@ -55,7 +55,7 @@ public: // SMGlobalClass
public: // ITextListener_SMC public: // ITextListener_SMC
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
public: // IRootConsoleCommand public: // IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) override; void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public: public:
/** /**
* Initializes CoreConfig by reading from core.cfg file * Initializes CoreConfig by reading from core.cfg file
@ -68,7 +68,8 @@ private:
*/ */
ConfigResult SetConfigOption(const char *option, const char *value, ConfigSource, char *Error, size_t maxlength); ConfigResult SetConfigOption(const char *option, const char *value, ConfigSource, char *Error, size_t maxlength);
private: private:
StringHashMap<ke::AString> m_KeyValues; BaseStringTable m_Strings;
KTrie<int> m_KeyValues;
}; };
extern bool SM_AreConfigsExecuted(); extern bool SM_AreConfigsExecuted();

199
core/CrazyDebugger.cpp Normal file
View File

@ -0,0 +1,199 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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 "sm_globals.h"
#include "sourcemm_api.h"
#include "Tlhelp32.h"
#include "LibrarySys.h"
#include "minidump.h"
#include "sm_stringutil.h"
bool HookImportAddrTable(BYTE *base, const char *func, DWORD hookfunc, char *err, size_t maxlength)
{
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)base;
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
{
UTIL_Format(err, maxlength, "%s", "Could not detect valid DOS signature");
return false;
}
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE)
{
UTIL_Format(err, maxlength, "%s", "Could not detect valid NT signature");
return false;
}
IMAGE_IMPORT_DESCRIPTOR *desc =
(IMAGE_IMPORT_DESCRIPTOR *)
(base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (base == (BYTE *)desc)
{
UTIL_Format(err, maxlength, "%s", "Could not find IAT");
return false;
}
bool entryFound = false;
while (desc->Name)
{
if (desc->FirstThunk != 0)
{
IMAGE_THUNK_DATA *data = (IMAGE_THUNK_DATA *)(base + desc->OriginalFirstThunk);
DWORD *iat = (DWORD *)(base + desc->FirstThunk);
while (data->u1.Function)
{
if ((data->u1.Ordinal & IMAGE_ORDINAL_FLAG32) != IMAGE_ORDINAL_FLAG32)
{
IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME *)(base + data->u1.AddressOfData);
if (strcmp((char *)import->Name, func) == 0)
{
DWORD oldprot, oldprot2;
VirtualProtect(iat, 4, PAGE_READWRITE, &oldprot);
*iat = hookfunc;
VirtualProtect(iat, 4, oldprot, &oldprot2);
entryFound = true;
}
}
data++;
iat++;
}
}
desc++;
}
if (!entryFound)
{
UTIL_Format(err, maxlength, "Could not find IAT entry for %s", func);
return false;
}
return true;
}
BOOL
WINAPI
MiniDumpWriteDump2(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
)
{
DumpType = (MINIDUMP_TYPE)((int)DumpType|MiniDumpWithFullMemory|MiniDumpWithHandleData);
return MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, ExceptionParam, UserStreamParam, CallbackParam);
}
FARPROC WINAPI GetProcAddress2(HMODULE hModule, LPCSTR lpProcName)
{
if (strcmp(lpProcName, "MiniDumpWriteDump") == 0)
{
return (FARPROC)MiniDumpWriteDump2;
}
return GetProcAddress(hModule, lpProcName);
}
HMODULE WINAPI LoadLibraryEx2(LPCTSTR lpFileName, HANDLE hFile, DWORD dwFlags)
{
HMODULE lib = LoadLibraryEx(lpFileName, hFile, dwFlags);
/* Steam.dll seems to get loaded using an abs path, thus the use of stristr() */
if (stristr(lpFileName, "steam.dll"))
{
char err[64];
if (!HookImportAddrTable((BYTE *)lib, "GetProcAddress", (DWORD)GetProcAddress2, err, sizeof(err)))
{
Error("[SM] %s in steam.dll\n", err);
return NULL;
}
}
return lib;
}
class CrazyWindowsDebugger : public SMGlobalClass
{
public:
void OnSourceModAllInitialized()
{
HANDLE hModuleList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32 me32;
BYTE *engine = NULL;
BYTE *steam = NULL;
char err[64];
me32.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(hModuleList, &me32))
{
Error("Could not initialize crazy debugger!");
}
do
{
if (strcasecmp(me32.szModule, "engine.dll") == 0)
{
engine = me32.modBaseAddr;
}
else if (strcasecmp(me32.szModule, "steam.dll") == 0)
{
steam = me32.modBaseAddr;
}
} while (Module32Next(hModuleList, &me32));
CloseHandle(hModuleList);
/* This really should not happen, but ... */
if (!engine)
{
Error("[SM] Could not find engine.dll\n");
}
/* Steam.dll isn't loaded yet */
if (!steam)
{
if (!HookImportAddrTable(engine, "LoadLibraryExA", (DWORD)LoadLibraryEx2, err, sizeof(err)))
{
Error("[SM] %s in engine.dll)\n", err);
}
}
else
{
if (!HookImportAddrTable(steam, "GetProcAddress", (DWORD)GetProcAddress2, err, sizeof(err)))
{
Error("[SM] %s in steam.dll)\n", err);
}
}
}
} s_CrazyDebugger;

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -30,14 +30,14 @@
*/ */
#include "Database.h" #include "Database.h"
#include "ISourceMod.h"
#include "HandleSys.h" #include "HandleSys.h"
#include "ShareSys.h"
#include "sourcemod.h"
#include "sm_stringutil.h"
#include "Logger.h"
#include "ExtensionSys.h" #include "ExtensionSys.h"
#include "PluginSys.h"
#include <stdlib.h> #include <stdlib.h>
#include <IThreader.h> #include <IThreader.h>
#include <bridge/include/ILogger.h>
#include <bridge/include/CoreProvider.h>
#define DBPARSE_LEVEL_NONE 0 #define DBPARSE_LEVEL_NONE 0
#define DBPARSE_LEVEL_MAIN 1 #define DBPARSE_LEVEL_MAIN 1
@ -47,16 +47,10 @@ DBManager g_DBMan;
static bool s_OneTimeThreaderErrorMsg = false; static bool s_OneTimeThreaderErrorMsg = false;
DBManager::DBManager() DBManager::DBManager()
: m_Terminate(false), : m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
m_pDefault(NULL)
{ {
} }
static void FrameHook(bool simulating)
{
g_DBMan.RunFrame();
}
void DBManager::OnSourceModAllInitialized() void DBManager::OnSourceModAllInitialized()
{ {
HandleAccess sec; HandleAccess sec;
@ -64,36 +58,50 @@ void DBManager::OnSourceModAllInitialized()
g_HandleSys.InitAccessDefaults(NULL, &sec); g_HandleSys.InitAccessDefaults(NULL, &sec);
sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY; sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY; sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL); 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); m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_ShareSys.AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
g_pSM->BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg"); g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
m_Builder.SetPath(m_Filename);
m_pConfigLock = g_pThreader->MakeMutex();
m_pThinkLock = g_pThreader->MakeMutex();
m_pQueueLock = g_pThreader->MakeMutex();
g_PluginSys.AddPluginsListener(this); 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) void DBManager::OnSourceModLevelChange(const char *mapName)
{ {
m_Builder.StartParse(); 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.
*/
m_pConfigLock->Lock();
if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
{
g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
if (err != SMCError_Custom)
{
const char *txt = textparsers->GetSMCErrorString(err);
g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
}
}
m_pConfigLock->Unlock();
} }
void DBManager::OnSourceModShutdown() void DBManager::OnSourceModShutdown()
{ {
g_pSM->RemoveGameFrameHook(&FrameHook);
KillWorkerThread(); KillWorkerThread();
g_PluginSys.RemovePluginsListener(this); g_PluginSys.RemovePluginsListener(this);
m_pConfigLock->DestroyThis();
m_pThinkLock->DestroyThis();
m_pQueueLock->DestroyThis();
g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent); g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent); g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
} }
@ -123,10 +131,128 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
} }
} }
void DBManager::ReadSMC_ParseStart()
{
m_confs.clear();
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_StrTab.Reset();
m_DefDriver.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 = m_StrTab.AddString(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 = m_StrTab.AddString(value);
}
} else if (strcmp(key, "database") == 0) {
s_CurInfo.database = m_StrTab.AddString(value);
} else if (strcmp(key, "host") == 0) {
s_CurInfo.host = m_StrTab.AddString(value);
} else if (strcmp(key, "user") == 0) {
s_CurInfo.user = m_StrTab.AddString(value);
} else if (strcmp(key, "pass") == 0) {
s_CurInfo.pass = m_StrTab.AddString(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;
}
#define ASSIGN_VAR(var) \
if (s_CurInfo.var == -1) { \
s_CurInfo.info.var = ""; \
} else { \
s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
}
SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
{
if (m_ParseLevel)
{
m_ParseLevel--;
return SMCResult_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
{
/* Set all of the info members to either a blank string
* or the string pointer from the string table.
*/
ASSIGN_VAR(driver);
ASSIGN_VAR(database);
ASSIGN_VAR(host);
ASSIGN_VAR(user);
ASSIGN_VAR(pass);
/* Save it.. */
m_confs.push_back(s_CurInfo);
/* 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;
}
#undef ASSIGN_VAR
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) bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
{ {
ConfDbInfoList *list = m_Builder.GetConfigList(); ConfDbInfo *pInfo = GetDatabaseConf(name);
ke::RefPtr<ConfDbInfo> pInfo = list->GetDatabaseConf(name);
if (!pInfo) if (!pInfo)
{ {
@ -135,7 +261,7 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
*pdr = NULL; *pdr = NULL;
} }
*pdb = NULL; *pdb = NULL;
g_pSM->Format(error, maxlength, "Configuration \"%s\" not found", name); UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
return false; return false;
} }
@ -145,12 +271,11 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
/* Try to assign a real driver pointer */ /* Try to assign a real driver pointer */
if (pInfo->info.driver[0] == '\0') if (pInfo->info.driver[0] == '\0')
{ {
ke::AString defaultDriver = list->GetDefaultDriver(); if (!m_pDefault && m_DefDriver.size() > 0)
if (!m_pDefault && defaultDriver.length() > 0)
{ {
m_pDefault = FindOrLoadDriver(defaultDriver.chars()); m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
} }
dname = defaultDriver.length() ? defaultDriver.chars() : "default"; dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
pInfo->realDriver = m_pDefault; pInfo->realDriver = m_pDefault;
} else { } else {
pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver); pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
@ -173,7 +298,8 @@ bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool
} }
*pdb = NULL; *pdb = NULL;
g_pSM->Format(error, maxlength, "Driver \"%s\" not found", dname); UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
return false; return false;
} }
@ -186,7 +312,7 @@ void DBManager::AddDriver(IDBDriver *pDriver)
*/ */
KillWorkerThread(); KillWorkerThread();
m_drivers.push_back(pDriver); m_drivers.push_back(pDriver);
} }
void DBManager::RemoveDriver(IDBDriver *pDriver) void DBManager::RemoveDriver(IDBDriver *pDriver)
@ -206,23 +332,17 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
} }
} }
ConfDbInfoList *list = m_Builder.GetConfigList(); /* Make sure NOTHING references this! */
for (size_t i = 0; i < list->length(); i++) List<ConfDbInfo>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{ {
ke::RefPtr<ConfDbInfo> current = list->at(i); ConfDbInfo &db = (*iter);
if (current->realDriver == pDriver) if (db.realDriver == pDriver)
{ {
current->realDriver = NULL; db.realDriver = NULL;
} }
} }
/* Someone unloaded the default driver? Silly.. */
if (pDriver == m_pDefault)
{
m_pDefault = NULL;
}
/* Now that the driver is gone, we have to test the think queue. /* Now that the driver is gone, we have to test the think queue.
* Whatever happens therein is up to the db op! * Whatever happens therein is up to the db op!
*/ */
@ -252,11 +372,9 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
IDBDriver *DBManager::GetDefaultDriver() IDBDriver *DBManager::GetDefaultDriver()
{ {
ConfDbInfoList *list = m_Builder.GetConfigList(); if (!m_pDefault && m_DefDriver.size() > 0)
ke::AString defaultDriver = list->GetDefaultDriver();
if (!m_pDefault && defaultDriver.length() > 0)
{ {
m_pDefault = FindOrLoadDriver(defaultDriver.chars()); m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
} }
return m_pDefault; return m_pDefault;
@ -319,16 +437,10 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name) const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
{ {
ConfDbInfoList *list = m_Builder.GetConfigList(); ConfDbInfo *info = GetDatabaseConf(name);
ke::RefPtr<ConfDbInfo> info = list->GetDatabaseConf(name);
if (!info) if (!info)
{ {
// couldn't find requested conf, return default if exists return NULL;
info = list->GetDefaultConfiguration();
if (!info)
{
return NULL;
}
} }
return &info->info; return &info->info;
@ -336,9 +448,18 @@ const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
ConfDbInfo *DBManager::GetDatabaseConf(const char *name) ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
{ {
ConfDbInfoList *list = m_Builder.GetConfigList(); List<ConfDbInfo>::iterator iter;
ke::RefPtr<ConfDbInfo> info(list->GetDatabaseConf(name));
return info; for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{
ConfDbInfo &conf = (*iter);
if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
{
return &conf;
}
}
return NULL;
} }
IDBDriver *DBManager::FindOrLoadDriver(const char *name) IDBDriver *DBManager::FindOrLoadDriver(const char *name)
@ -353,7 +474,7 @@ IDBDriver *DBManager::FindOrLoadDriver(const char *name)
} }
char filename[PLATFORM_MAX_PATH]; char filename[PLATFORM_MAX_PATH];
g_pSM->Format(filename, sizeof(filename), "dbi.%s.ext", name); UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
IExtension *pExt = g_Extensions.LoadAutoExtension(filename); IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size) if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
@ -374,17 +495,12 @@ IDBDriver *DBManager::FindOrLoadDriver(const char *name)
void DBManager::KillWorkerThread() void DBManager::KillWorkerThread()
{ {
if (m_Worker) if (m_pWorker)
{ {
{ m_pWorker->Stop(false);
ke::AutoLock lock(&m_QueueEvent); g_pThreader->DestroyWorker(m_pWorker);
m_Terminate = true; m_pWorker = NULL;
m_QueueEvent.Notify();
}
m_Worker->Join();
m_Worker = nullptr;
s_OneTimeThreaderErrorMsg = false; s_OneTimeThreaderErrorMsg = false;
m_Terminate = false;
} }
} }
@ -397,108 +513,99 @@ bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
return false; return false;
} }
if (!m_Worker) if (!m_pWorker)
{ {
m_Worker = new ke::Thread([this]() -> void { m_pWorker = g_pThreader->MakeWorker(this, true);
Run(); if (!m_pWorker)
}, "SM SQL Worker");
if (!m_Worker->Succeeded())
{ {
if (!s_OneTimeThreaderErrorMsg) if (!s_OneTimeThreaderErrorMsg)
{ {
logger->LogError("[SM] Unable to create db threader (error unknown)"); g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
s_OneTimeThreaderErrorMsg = true; s_OneTimeThreaderErrorMsg = true;
} }
m_Worker = nullptr; return false;
}
if (!m_pWorker->Start())
{
if (!s_OneTimeThreaderErrorMsg)
{
g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
s_OneTimeThreaderErrorMsg = true;
}
g_pThreader->DestroyWorker(m_pWorker);
m_pWorker = NULL;
return false; return false;
} }
} }
/* Add to the queue */ /* Add to the queue */
{ {
ke::AutoLock lock(&m_QueueEvent); m_pQueueLock->Lock();
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio); Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
queue.push(op); queue.push(op);
m_QueueEvent.Notify(); m_pQueueLock->Unlock();
} }
/* Make the thread */
m_pWorker->MakeThread(this);
return true; return true;
} }
void DBManager::Run() void DBManager::OnWorkerStart(IThreadWorker *pWorker)
{ {
// Initialize DB threadsafety. m_drSafety.clear();
for (size_t i=0; i < m_drivers.size(); i++) for (size_t i=0; i<m_drivers.size(); i++)
{ {
if (m_drivers[i]->IsThreadSafe()) if (m_drivers[i]->IsThreadSafe())
{
m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety()); m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
else } else {
m_drSafety.push_back(false); m_drSafety.push_back(false);
}
} }
}
// Run actual worker thread logic. void DBManager::OnWorkerStop(IThreadWorker *pWorker)
ThreadMain(); {
// Shutdown DB threadsafety.
for (size_t i=0; i<m_drivers.size(); i++) for (size_t i=0; i<m_drivers.size(); i++)
{ {
if (m_drSafety[i]) if (m_drSafety[i])
{
m_drivers[i]->ShutdownThreadSafety(); m_drivers[i]->ShutdownThreadSafety();
}
} }
m_drSafety.clear(); m_drSafety.clear();
} }
void DBManager::ThreadMain() void DBManager::RunThread(IThreadHandle *pThread)
{ {
ke::AutoLock lock(&m_QueueEvent); IDBThreadOperation *op = NULL;
while (true) { /* Get something from the queue */
// The lock has been acquired. Grab everything we can out of the {
// queue. Since we want to flush the queue even if we're terminated, m_pQueueLock->Lock();
// we process all operations we can before checking to terminate. Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
// There's no risk of starvation since the main thread blocks on us if (!queue.empty())
// terminating.
while (true)
{ {
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue(); op = queue.first();
if (queue.empty())
break;
IDBThreadOperation *op = queue.first();
queue.pop(); 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
}
} }
m_pQueueLock->Unlock();
if (m_Terminate)
return;
// Release the lock and wait for a signal.
m_QueueEvent.Wait();
} }
/* whoa. hi. did we get something? we should have. */
if (!op)
{
/* wtf? */
return;
}
op->RunThreadPart();
m_pThinkLock->Lock();
m_ThinkQueue.push(op);
m_pThinkLock->Unlock();
} }
void DBManager::RunFrame() void DBManager::RunFrame()
@ -510,16 +617,19 @@ void DBManager::RunFrame()
} }
/* Dump one thing per-frame so the server stays sane. */ /* Dump one thing per-frame so the server stays sane. */
IDBThreadOperation *op; m_pThinkLock->Lock();
{ IDBThreadOperation *op = m_ThinkQueue.first();
ke::AutoLock lock(&m_ThinkLock); m_ThinkQueue.pop();
op = m_ThinkQueue.first(); m_pThinkLock->Unlock();
m_ThinkQueue.pop();
}
op->RunThinkPart(); op->RunThinkPart();
op->Destroy(); op->Destroy();
} }
void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
{
/* Do nothing */
}
void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken) void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
{ {
s_pAddBlock = pToken; s_pAddBlock = pToken;
@ -557,7 +667,7 @@ void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
s_pAddBlock = NULL; s_pAddBlock = NULL;
} }
void DBManager::OnPluginWillUnload(IPlugin *plugin) void DBManager::OnPluginUnloaded(IPlugin *plugin)
{ {
/* Kill the thread so we can flush everything into the think queue... */ /* Kill the thread so we can flush everything into the think queue... */
KillWorkerThread(); KillWorkerThread();
@ -583,7 +693,9 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
} }
} }
for (iter = templist.begin(); iter != templist.end(); iter++) for (iter = templist.begin();
iter != templist.end();
iter++)
{ {
IDBThreadOperation *op = (*iter); IDBThreadOperation *op = (*iter);
op->RunThinkPart(); op->RunThinkPart();
@ -591,13 +703,23 @@ void DBManager::OnPluginWillUnload(IPlugin *plugin)
} }
} }
ke::AString DBManager::GetDefaultDriverName() void DBManager::LockConfig()
{ {
ConfDbInfoList *list = m_Builder.GetConfigList(); m_pConfigLock->Lock();
return list->GetDefaultDriver(); }
void DBManager::UnlockConfig()
{
m_pConfigLock->Unlock();
}
const char *DBManager::GetDefaultDriverName()
{
return m_DefDriver.c_str();
} }
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver) void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
{ {
g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver); g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
} }

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -32,24 +32,42 @@
#ifndef _INCLUDE_DATABASE_MANAGER_H_ #ifndef _INCLUDE_DATABASE_MANAGER_H_
#define _INCLUDE_DATABASE_MANAGER_H_ #define _INCLUDE_DATABASE_MANAGER_H_
#include "common_logic.h" #include <IDBDriver.h>
#include "sm_globals.h"
#include <sh_vector.h> #include <sh_vector.h>
#include <am-string.h> #include <sh_string.h>
#include <sh_list.h> #include <sh_list.h>
#include <ITextParsers.h>
#include "sm_memtable.h"
#include <IThreader.h> #include <IThreader.h>
#include <IPluginSys.h>
#include <am-thread-utils.h>
#include "sm_simple_prioqueue.h" #include "sm_simple_prioqueue.h"
#include <am-refcounting.h> #include "PluginSys.h"
#include "DatabaseConfBuilder.h"
using namespace SourceHook; using namespace SourceHook;
struct ConfDbInfo
{
ConfDbInfo() : name(-1), driver(-1), host(-1), user(-1), pass(-1),
database(-1), realDriver(NULL)
{
}
int name;
int driver;
int host;
int user;
int pass;
int database;
IDBDriver *realDriver;
DatabaseInfo info;
};
class DBManager : class DBManager :
public IDBManager, public IDBManager,
public SMGlobalClass, public SMGlobalClass,
public IHandleTypeDispatch, public IHandleTypeDispatch,
public ITextListener_SMC,
public IThread,
public IThreadWorkerCallbacks,
public IPluginsListener public IPluginsListener
{ {
public: public:
@ -68,7 +86,6 @@ public: //IDBManager
void AddDriver(IDBDriver *pDrivera); void AddDriver(IDBDriver *pDrivera);
void RemoveDriver(IDBDriver *pDriver); void RemoveDriver(IDBDriver *pDriver);
const DatabaseInfo *FindDatabaseConf(const char *name); const DatabaseInfo *FindDatabaseConf(const char *name);
ConfDbInfo *GetDatabaseConf(const char *name);
bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength); bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength);
unsigned int GetDriverCount(); unsigned int GetDriverCount();
IDBDriver *GetDriver(unsigned int index); IDBDriver *GetDriver(unsigned int index);
@ -76,23 +93,34 @@ public: //IDBManager
HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr); HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr);
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token); HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
void AddDependency(IExtension *myself, IDBDriver *driver); void AddDependency(IExtension *myself, IDBDriver *driver);
public: //ke::IRunnable public: //ITextListener_SMC
void Run(); void ReadSMC_ParseStart();
void ThreadMain(); 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: //IThread
void RunThread(IThreadHandle *pThread);
void OnTerminate(IThreadHandle *pThread, bool cancel);
public: //IThreadWorkerCallbacks
void OnWorkerStart(IThreadWorker *pWorker);
void OnWorkerStop(IThreadWorker *pWorker);
public: //IPluginsListener public: //IPluginsListener
void OnPluginWillUnload(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin);
public: public:
ConfDbInfo *GetDatabaseConf(const char *name);
IDBDriver *FindOrLoadDriver(const char *name); IDBDriver *FindOrLoadDriver(const char *name);
IDBDriver *GetDefaultDriver(); IDBDriver *GetDefaultDriver();
ke::AString GetDefaultDriverName(); const char *GetDefaultDriverName();
bool AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio); bool AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio);
void LockConfig();
void UnlockConfig();
void RunFrame(); void RunFrame();
inline HandleType_t GetDatabaseType() inline HandleType_t GetDatabaseType()
{ {
return m_DatabaseType; return m_DatabaseType;
} }
private: private:
void ClearConfigs();
void KillWorkerThread(); void KillWorkerThread();
private: private:
CVector<IDBDriver *> m_drivers; CVector<IDBDriver *> m_drivers;
@ -101,15 +129,19 @@ private:
PrioQueue<IDBThreadOperation *> m_OpQueue; PrioQueue<IDBThreadOperation *> m_OpQueue;
Queue<IDBThreadOperation *> m_ThinkQueue; Queue<IDBThreadOperation *> m_ThinkQueue;
CVector<bool> m_drSafety; /* which drivers are safe? */ CVector<bool> m_drSafety; /* which drivers are safe? */
ke::AutoPtr<ke::Thread> m_Worker; IThreadWorker *m_pWorker; /* Worker thread object */
ke::ConditionVariable m_QueueEvent; IMutex *m_pConfigLock; /* Configuration lock */
ke::Mutex m_ThinkLock; IMutex *m_pQueueLock; /* Queue safety lock */
bool m_Terminate; IMutex *m_pThinkLock; /* Think-queue lock */
DatabaseConfBuilder m_Builder; List<ConfDbInfo> m_confs;
HandleType_t m_DriverType; HandleType_t m_DriverType;
HandleType_t m_DatabaseType; HandleType_t m_DatabaseType;
String m_DefDriver;
BaseStringTable m_StrTab;
char m_Filename[PLATFORM_MAX_PATH]; char m_Filename[PLATFORM_MAX_PATH];
unsigned int m_ParseLevel;
unsigned int m_ParseState;
IDBDriver *m_pDefault; IDBDriver *m_pDefault;
}; };

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -29,11 +29,10 @@
* Version: $Id$ * Version: $Id$
*/ */
#include <ISourceMod.h>
#include <IPluginSys.h>
#include <stdarg.h>
#include "DebugReporter.h" #include "DebugReporter.h"
#include "Logger.h" #include "Logger.h"
#include "PluginSys.h"
#include "sm_stringutil.h"
DebugReport g_DbgReporter; DebugReport g_DbgReporter;
@ -48,7 +47,7 @@ void DebugReport::OnDebugSpew(const char *msg, ...)
char buffer[512]; char buffer[512];
va_start(ap, msg); va_start(ap, msg);
ke::SafeVsprintf(buffer, sizeof(buffer), msg, ap); UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
va_end(ap); va_end(ap);
g_Logger.LogMessage("[SM] %s", buffer); g_Logger.LogMessage("[SM] %s", buffer);
@ -57,18 +56,13 @@ void DebugReport::OnDebugSpew(const char *msg, ...)
void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...) void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...)
{ {
va_list ap; va_list ap;
char buffer[512];
va_start(ap, message); va_start(ap, message);
GenerateErrorVA(ctx, func_idx, err, message, ap); UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
va_end(ap); va_end(ap);
}
void DebugReport::GenerateErrorVA(IPluginContext *ctx, cell_t func_idx, int err, const char *message, va_list ap) const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
{
char buffer[512];
ke::SafeVsprintf(buffer, sizeof(buffer), message, ap);
const char *plname = pluginsys->FindPluginByContext(ctx->GetContext())->GetFilename();
const char *error = g_pSourcePawn2->GetErrorString(err); const char *error = g_pSourcePawn2->GetErrorString(err);
if (error) if (error)
@ -100,10 +94,10 @@ void DebugReport::GenerateCodeError(IPluginContext *pContext, uint32_t code_addr
char buffer[512]; char buffer[512];
va_start(ap, message); va_start(ap, message);
ke::SafeVsprintf(buffer, sizeof(buffer), message, ap); UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
va_end(ap); va_end(ap);
const char *plname = pluginsys->FindPluginByContext(pContext->GetContext())->GetFilename(); const char *plname = g_PluginSys.FindPluginByContext(pContext->GetContext())->GetFilename();
const char *error = g_pSourcePawn2->GetErrorString(err); const char *error = g_pSourcePawn2->GetErrorString(err);
if (error) if (error)
@ -133,10 +127,55 @@ void DebugReport::GenerateCodeError(IPluginContext *pContext, uint32_t code_addr
} }
} }
void DebugReport::OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
{
const char *lastname;
const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
int n_err = error->GetErrorCode();
if (n_err != SP_ERROR_NATIVE)
{
g_Logger.LogError("[SM] Plugin encountered error %d: %s",
n_err,
error->GetErrorString());
}
if ((lastname=error->GetLastNative(NULL)) != NULL)
{
const char *custerr;
if ((custerr=error->GetCustomErrorString()) != NULL)
{
g_Logger.LogError("[SM] Native \"%s\" reported: %s", lastname, custerr);
} else {
g_Logger.LogError("[SM] Native \"%s\" encountered a generic error.", lastname);
}
}
if (!error->DebugInfoAvailable())
{
g_Logger.LogError("[SM] Debug mode is not enabled for \"%s\"", plname);
g_Logger.LogError("[SM] To enable debug mode, edit plugin_settings.cfg, or type: sm plugins debug %d on",
_GetPluginIndex(ctx));
return;
}
CallStackInfo stk_info;
int i = 0;
g_Logger.LogError("[SM] Displaying call stack trace for plugin \"%s\":", plname);
while (error->GetTraceInfo(&stk_info))
{
g_Logger.LogError("[SM] [%d] Line %d, %s::%s()",
i++,
stk_info.line,
stk_info.filename,
stk_info.function);
}
}
int DebugReport::_GetPluginIndex(IPluginContext *ctx) int DebugReport::_GetPluginIndex(IPluginContext *ctx)
{ {
int id = 1; int id = 1;
IPluginIterator *iter = pluginsys->GetPluginIterator(); IPluginIterator *iter = g_PluginSys.GetPluginIterator();
for (; iter->MorePlugins(); iter->NextPlugin(), id++) for (; iter->MorePlugins(); iter->NextPlugin(), id++)
{ {
@ -152,95 +191,6 @@ int DebugReport::_GetPluginIndex(IPluginContext *ctx)
/* If we don't know which plugin this is, it's one being loaded. Fake its index for now. */ /* If we don't know which plugin this is, it's one being loaded. Fake its index for now. */
return pluginsys->GetPluginCount() + 1; return g_PluginSys.GetPluginCount() + 1;
} }
void DebugReport::ReportError(const IErrorReport &report, IFrameIterator &iter)
{
// Don't log an error if a function wasn't runnable.
// This is necassary due to the way SM is handling and exposing
// scripted functions. It's too late to change that now.
if (report.Code() == SP_ERROR_NOT_RUNNABLE)
return;
const char *blame = nullptr;
if (report.Blame())
{
blame = report.Blame()->DebugName();
} else {
// Find the nearest plugin to blame.
for (; !iter.Done(); iter.Next())
{
if (iter.IsScriptedFrame())
{
IPlugin *plugin = pluginsys->FindPluginByContext(iter.Context()->GetContext());
if (plugin)
{
blame = plugin->GetFilename();
} else {
blame = iter.Context()->GetRuntime()->GetFilename();
}
break;
}
}
}
iter.Reset();
g_Logger.LogError("[SM] Exception reported: %s", report.Message());
if (blame)
{
g_Logger.LogError("[SM] Blaming: %s", blame);
}
ke::Vector<ke::AString> arr = GetStackTrace(&iter);
for (size_t i = 0; i < arr.length(); i++)
{
g_Logger.LogError("%s", arr[i].chars());
}
}
ke::Vector<ke::AString> DebugReport::GetStackTrace(IFrameIterator *iter)
{
char temp[3072];
ke::Vector<ke::AString> trace;
iter->Reset();
if (!iter->Done())
{
trace.append("[SM] Call stack trace:");
for (int index = 0; !iter->Done(); iter->Next(), index++)
{
const char *fn = iter->FunctionName();
if (!fn)
{
fn = "<unknown function>";
}
if (iter->IsNativeFrame())
{
g_pSM->Format(temp, sizeof(temp), "[SM] [%d] %s", index, fn);
trace.append(temp);
continue;
}
if (iter->IsScriptedFrame())
{
const char *file = iter->FilePath();
if (!file)
{
file = "<unknown>";
}
g_pSM->Format(temp, sizeof(temp), "[SM] [%d] Line %d, %s::%s",
index,
iter->LineNumber(),
file,
fn);
trace.append(temp);
}
}
}
return trace;
}

View File

@ -33,9 +33,7 @@
#define _INCLUDE_SOURCEMOD_CDBGREPORTER_H_ #define _INCLUDE_SOURCEMOD_CDBGREPORTER_H_
#include "sp_vm_api.h" #include "sp_vm_api.h"
#include "common_logic.h" #include "sm_globals.h"
#include <am-vector.h>
#include <am-string.h>
class DebugReport : class DebugReport :
public SMGlobalClass, public SMGlobalClass,
@ -44,13 +42,11 @@ class DebugReport :
public: // SMGlobalClass public: // SMGlobalClass
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
public: // IDebugListener public: // IDebugListener
void ReportError(const IErrorReport &report, IFrameIterator &iter); void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error);
void OnDebugSpew(const char *msg, ...); void OnDebugSpew(const char *msg, ...);
public: public:
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...); 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, ...); void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...);
ke::Vector<ke::AString> GetStackTrace(IFrameIterator *iter);
private: private:
int _GetPluginIndex(IPluginContext *ctx); int _GetPluginIndex(IPluginContext *ctx);
}; };

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -30,11 +30,10 @@
*/ */
#include "EventManager.h" #include "EventManager.h"
#include "ForwardSys.h"
#include "HandleSys.h"
#include "PluginSys.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "PlayerManager.h"
#include "logic_bridge.h"
#include <bridge/include/IScriptManager.h>
EventManager g_EventManager; EventManager g_EventManager;
@ -59,10 +58,14 @@ public:
EventManager::EventManager() : m_EventType(0) EventManager::EventManager() : m_EventType(0)
{ {
/* Create an event lookup trie */
m_EventHooks = sm_trie_create();
} }
EventManager::~EventManager() EventManager::~EventManager()
{ {
sm_trie_destroy(m_EventHooks);
/* Free memory used by EventInfo structs if any */ /* Free memory used by EventInfo structs if any */
CStack<EventInfo *>::iterator iter; CStack<EventInfo *>::iterator iter;
for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++) for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++)
@ -76,8 +79,8 @@ EventManager::~EventManager()
void EventManager::OnSourceModAllInitialized() void EventManager::OnSourceModAllInitialized()
{ {
/* Add a hook for IGameEventManager2::FireEvent() */ /* Add a hook for IGameEventManager2::FireEvent() */
SH_ADD_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent), false); SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
SH_ADD_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent_Post), true); SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
HandleAccess sec; HandleAccess sec;
@ -87,17 +90,17 @@ void EventManager::OnSourceModAllInitialized()
sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER; sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
/* Create the 'GameEvent' handle type */ /* Create the 'GameEvent' handle type */
m_EventType = handlesys->CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL); m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL);
} }
void EventManager::OnSourceModShutdown() void EventManager::OnSourceModShutdown()
{ {
/* Remove hook for IGameEventManager2::FireEvent() */ /* Remove hook for IGameEventManager2::FireEvent() */
SH_REMOVE_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent), false); SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
SH_REMOVE_HOOK(IGameEventManager2, FireEvent, gameevents, SH_MEMBER(this, &EventManager::OnFireEvent_Post), true); SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
/* Remove the 'GameEvent' handle type */ /* Remove the 'GameEvent' handle type */
handlesys->RemoveType(m_EventType, g_pCoreIdent); g_HandleSys.RemoveType(m_EventType, g_pCoreIdent);
/* Remove ourselves as listener for events */ /* Remove ourselves as listener for events */
gameevents->RemoveListener(this); gameevents->RemoveListener(this);
@ -135,12 +138,12 @@ void EventManager::OnPluginUnloaded(IPlugin *plugin)
{ {
if (pHook->pPreHook) if (pHook->pPreHook)
{ {
forwardsys->ReleaseForward(pHook->pPreHook); g_Forwards.ReleaseForward(pHook->pPreHook);
} }
if (pHook->pPostHook) if (pHook->pPostHook)
{ {
forwardsys->ReleaseForward(pHook->pPostHook); g_Forwards.ReleaseForward(pHook->pPostHook);
} }
delete pHook; delete pHook;
@ -158,13 +161,6 @@ void EventManager::FireGameEvent(IGameEvent *pEvent)
Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */ Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */
} }
#if SOURCE_ENGINE >= SE_LEFT4DEAD
int EventManager::GetEventDebugID()
{
return EVENT_DEBUG_ID_INIT;
}
#endif
EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode) EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
{ {
EventHook *pHook; EventHook *pHook;
@ -181,10 +177,10 @@ EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunct
} }
/* If a hook structure does not exist... */ /* If a hook structure does not exist... */
if (!m_EventHooks.retrieve(name, &pHook)) if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
{ {
EventHookList *pHookList; EventHookList *pHookList;
IPlugin *plugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext()); IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
/* Check plugin for an existing EventHook list */ /* Check plugin for an existing EventHook list */
if (!plugin->GetProperty("EventHooks", (void **)&pHookList)) if (!plugin->GetProperty("EventHooks", (void **)&pHookList))
@ -199,12 +195,12 @@ EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunct
if (mode == EventHookMode_Pre) if (mode == EventHookMode_Pre)
{ {
/* Create forward for a pre hook */ /* Create forward for a pre hook */
pHook->pPreHook = forwardsys->CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS); pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS);
/* Add to forward list */ /* Add to forward list */
pHook->pPreHook->AddFunction(pFunction); pHook->pPreHook->AddFunction(pFunction);
} else { } else {
/* Create forward for a post hook */ /* Create forward for a post hook */
pHook->pPostHook = forwardsys->CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
/* Should we copy data from a pre hook to the post hook? */ /* Should we copy data from a pre hook to the post hook? */
pHook->postCopy = (mode == EventHookMode_Post); pHook->postCopy = (mode == EventHookMode_Post);
/* Add to forward list */ /* Add to forward list */
@ -212,14 +208,14 @@ EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunct
} }
/* Cache the name for post hooks */ /* Cache the name for post hooks */
pHook->name = name; pHook->name = sm_strdup(name);
/* Increase reference count */ /* Increase reference count */
pHook->refCount++; pHook->refCount++;
/* Add hook structure to hook lists */ /* Add hook structure to hook lists */
pHookList->push_back(pHook); pHookList->push_back(pHook);
m_EventHooks.insert(name, pHook); sm_trie_insert(m_EventHooks, name, pHook);
return EventHookErr_Okay; return EventHookErr_Okay;
} }
@ -231,7 +227,7 @@ EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunct
/* Create pre hook forward if necessary */ /* Create pre hook forward if necessary */
if (!pHook->pPreHook) if (!pHook->pPreHook)
{ {
pHook->pPreHook = forwardsys->CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS); pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS);
} }
/* Add plugin function to forward list */ /* Add plugin function to forward list */
@ -240,7 +236,7 @@ EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunct
/* Create post hook forward if necessary */ /* Create post hook forward if necessary */
if (!pHook->pPostHook) if (!pHook->pPostHook)
{ {
pHook->pPostHook = forwardsys->CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS); pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
} }
/* If postCopy is false, then we may want to set it to true */ /* If postCopy is false, then we may want to set it to true */
@ -265,7 +261,7 @@ EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFun
IChangeableForward **pEventForward; IChangeableForward **pEventForward;
/* If hook does not exist at all */ /* If hook does not exist at all */
if (!m_EventHooks.retrieve(name, &pHook)) if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
{ {
return EventHookErr_NotActive; return EventHookErr_NotActive;
} }
@ -287,7 +283,7 @@ EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFun
/* If forward's list contains 0 functions now, free it */ /* If forward's list contains 0 functions now, free it */
if ((*pEventForward)->GetFunctionCount() == 0) if ((*pEventForward)->GetFunctionCount() == 0)
{ {
forwardsys->ReleaseForward(*pEventForward); g_Forwards.ReleaseForward(*pEventForward);
*pEventForward = NULL; *pEventForward = NULL;
} }
@ -297,7 +293,7 @@ EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFun
/* If reference count is now 0, free hook structure */ /* If reference count is now 0, free hook structure */
EventHookList *pHookList; EventHookList *pHookList;
IPlugin *plugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext()); IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
/* Get plugin's event hook list */ /* Get plugin's event hook list */
if (!plugin->GetProperty("EventHooks", (void**)&pHookList)) if (!plugin->GetProperty("EventHooks", (void**)&pHookList))
@ -315,7 +311,10 @@ EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFun
pHookList->remove(pHook); pHookList->remove(pHook);
/* Delete entry in trie */ /* Delete entry in trie */
m_EventHooks.remove(name); sm_trie_delete(m_EventHooks, name);
/* Free the cached name */
delete pHook->name;
/* And finally free structure memory */ /* And finally free structure memory */
delete pHook; delete pHook;
@ -362,13 +361,6 @@ void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
m_FreeEvents.push(pInfo); m_FreeEvents.push(pInfo);
} }
void EventManager::FireEventToClient(EventInfo *pInfo, IClient *pClient)
{
// 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);
}
void EventManager::CancelCreatedEvent(EventInfo *pInfo) void EventManager::CancelCreatedEvent(EventInfo *pInfo)
{ {
/* Free event from IGameEventManager2 */ /* Free event from IGameEventManager2 */
@ -398,7 +390,7 @@ bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
name = pEvent->GetName(); name = pEvent->GetName();
if (m_EventHooks.retrieve(name, &pHook)) if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
{ {
/* Push the event onto the event stack. The reference count is increased to make sure /* Push the event onto the event stack. The reference count is increased to make sure
* the structure is not garbage collected in between now and the post hook. * the structure is not garbage collected in between now and the post hook.
@ -412,7 +404,7 @@ bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
{ {
EventInfo info(pEvent, NULL); EventInfo info(pEvent, NULL);
HandleSecurity sec(NULL, g_pCoreIdent); HandleSecurity sec(NULL, g_pCoreIdent);
Handle_t hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
info.bDontBroadcast = bDontBroadcast; info.bDontBroadcast = bDontBroadcast;
@ -425,7 +417,7 @@ bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
broadcast = info.bDontBroadcast; broadcast = info.bDontBroadcast;
handlesys->FreeHandle(hndl, &sec); g_HandleSys.FreeHandle(hndl, &sec);
} }
if (pHook->postCopy) if (pHook->postCopy)
@ -433,7 +425,7 @@ bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
m_EventCopies.push(gameevents->DuplicateEvent(pEvent)); m_EventCopies.push(gameevents->DuplicateEvent(pEvent));
} }
if (res >= Pl_Handled) if (res)
{ {
gameevents->FreeEvent(pEvent); gameevents->FreeEvent(pEvent);
RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_SUPERCEDE, false);
@ -477,14 +469,14 @@ bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
info.bDontBroadcast = bDontBroadcast; info.bDontBroadcast = bDontBroadcast;
info.pEvent = m_EventCopies.front(); info.pEvent = m_EventCopies.front();
info.pOwner = NULL; info.pOwner = NULL;
hndl = handlesys->CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL); hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
pForward->PushCell(hndl); pForward->PushCell(hndl);
} else { } else {
pForward->PushCell(BAD_HANDLE); pForward->PushCell(BAD_HANDLE);
} }
pForward->PushString(pHook->name.chars()); pForward->PushString(pHook->name);
pForward->PushCell(bDontBroadcast); pForward->PushCell(bDontBroadcast);
pForward->Execute(NULL); pForward->Execute(NULL);
@ -492,7 +484,7 @@ bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
{ {
/* Free handle */ /* Free handle */
HandleSecurity sec(NULL, g_pCoreIdent); HandleSecurity sec(NULL, g_pCoreIdent);
handlesys->FreeHandle(hndl, &sec); g_HandleSys.FreeHandle(hndl, &sec);
/* Free event structure */ /* Free event structure */
gameevents->FreeEvent(info.pEvent); gameevents->FreeEvent(info.pEvent);
@ -505,7 +497,8 @@ bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
{ {
assert(pHook->pPostHook == NULL); assert(pHook->pPostHook == NULL);
assert(pHook->pPreHook == NULL); assert(pHook->pPreHook == NULL);
m_EventHooks.remove(pHook->name.chars()); sm_trie_delete(m_EventHooks, pHook->name);
delete pHook->name;
delete pHook; delete pHook;
} }
} }

View File

@ -1,8 +1,8 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -34,15 +34,13 @@
#include "sm_globals.h" #include "sm_globals.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include <sm_namehashset.h> #include "sm_trie.h"
#include <sh_list.h> #include <sh_list.h>
#include <sh_stack.h> #include <sh_stack.h>
#include <IHandleSys.h> #include <IHandleSys.h>
#include <IForwardSys.h> #include <IForwardSys.h>
#include <IPluginSys.h> #include <IPluginSys.h>
class IClient;
using namespace SourceHook; using namespace SourceHook;
struct EventInfo struct EventInfo
@ -71,16 +69,7 @@ struct EventHook
IChangeableForward *pPostHook; IChangeableForward *pPostHook;
bool postCopy; bool postCopy;
unsigned int refCount; unsigned int refCount;
ke::AString name; char *name;
static inline bool matches(const char *name, const EventHook *hook)
{
return strcmp(name, hook->name.chars()) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
}; };
enum EventHookMode enum EventHookMode
@ -116,9 +105,6 @@ public: // IPluginsListener
void OnPluginUnloaded(IPlugin *plugin); void OnPluginUnloaded(IPlugin *plugin);
public: // IGameEventListener2 public: // IGameEventListener2
void FireGameEvent(IGameEvent *pEvent); void FireGameEvent(IGameEvent *pEvent);
#if SOURCE_ENGINE >= SE_LEFT4DEAD
int GetEventDebugID();
#endif
public: public:
/** /**
* Get the 'GameEvent' handle type ID. * Get the 'GameEvent' handle type ID.
@ -132,14 +118,13 @@ public:
EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post); EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post);
EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false); EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false);
void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false); void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false);
void FireEventToClient(EventInfo *pInfo, IClient *pClient);
void CancelCreatedEvent(EventInfo *pInfo); void CancelCreatedEvent(EventInfo *pInfo);
private: // IGameEventManager2 hooks private: // IGameEventManager2 hooks
bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast); bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast);
bool OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast); bool OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast);
private: private:
HandleType_t m_EventType; HandleType_t m_EventType;
NameHashSet<EventHook *> m_EventHooks; Trie *m_EventHooks;
CStack<EventInfo *> m_FreeEvents; CStack<EventInfo *> m_FreeEvents;
CStack<EventHook *> m_EventStack; CStack<EventHook *> m_EventStack;
CStack<IGameEvent *> m_EventCopies; CStack<IGameEvent *> m_EventCopies;

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -31,21 +31,18 @@
#include <stdlib.h> #include <stdlib.h>
#include "ExtensionSys.h" #include "ExtensionSys.h"
#include <ILibrarySys.h> #include "LibrarySys.h"
#include <ISourceMod.h> #include "ShareSys.h"
#include "common_logic.h" #include "Logger.h"
#include "PluginSys.h" #include "sourcemm_api.h"
#include <am-utility.h> #include "sm_srvcmds.h"
#include <am-string.h> #include "sm_stringutil.h"
#include <bridge/include/CoreProvider.h>
#include <bridge/include/ILogger.h>
CExtensionManager g_Extensions; CExtensionManager g_Extensions;
IdentityType_t g_ExtType; IdentityType_t g_ExtType;
void CExtension::Initialize(const char *filename, const char *path, bool bRequired) void CExtension::Initialize(const char *filename, const char *path)
{ {
m_bRequired = bRequired;
m_pAPI = NULL; m_pAPI = NULL;
m_pIdentToken = NULL; m_pIdentToken = NULL;
unload_code = 0; unload_code = 0;
@ -53,7 +50,7 @@ void CExtension::Initialize(const char *filename, const char *path, bool bRequir
m_File.assign(filename); m_File.assign(filename);
m_Path.assign(path); m_Path.assign(path);
char real_name[PLATFORM_MAX_PATH]; char real_name[PLATFORM_MAX_PATH];
libsys->GetFileFromPath(real_name, sizeof(real_name), m_Path.c_str()); g_LibSys.GetFileFromPath(real_name, sizeof(real_name), m_Path.c_str());
m_RealFile.assign(real_name); m_RealFile.assign(real_name);
} }
@ -63,79 +60,62 @@ CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filena
m_pAPI = pAPI; m_pAPI = pAPI;
} }
CLocalExtension::CLocalExtension(const char *filename, bool bRequired) #if defined METAMOD_PLAPI_VERSION
#if SOURCE_ENGINE == SE_LEFT4DEAD
#define GAMEFIX "2.l4d"
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
#define GAMEFIX "2.l4d2"
#elif SOURCE_ENGINE == SE_ORANGEBOX
#define GAMEFIX "2.ep2"
#elif SOURCE_ENGINE == SE_ORANGEBOXVALVE
#define GAMEFIX "2.ep2v"
#elif SOURCE_ENGINE == SE_DARKMESSIAH
#define GAMEFIX "2.darkm"
#else
#define GAMEFIX "2.ep1"
#endif //(SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
#else //METAMOD_PLAPI_VERSION
#define GAMEFIX "1.ep1"
#endif //METAMOD_PLAPI_VERSION
CLocalExtension::CLocalExtension(const char *filename)
{ {
m_PlId = 0; m_PlId = 0;
m_pLib = NULL; m_pLib = NULL;
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
/* Special case for new bintools binary */ /* Zeroth, see if there is an engine specific build in the new place. */
if (strcmp(filename, "bintools.ext") == 0) g_SourceMod.BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/%s." GAMEFIX "." PLATFORM_LIB_EXT,
filename);
if (g_LibSys.IsPathFile(path))
{ {
g_pSM->BuildPath(Path_SM, goto found;
}
/* First see if there is an engine specific build! */
g_SourceMod.BuildPath(Path_SM,
path,
PLATFORM_MAX_PATH,
"extensions/auto." GAMEFIX "/%s." PLATFORM_LIB_EXT,
filename);
/* Try the "normal" version */
if (!g_LibSys.IsPathFile(path))
{
g_SourceMod.BuildPath(Path_SM,
path, path,
PLATFORM_MAX_PATH, PLATFORM_MAX_PATH,
"extensions/%s." PLATFORM_LIB_EXT, "extensions/%s." PLATFORM_LIB_EXT,
filename); 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/%s.%s." PLATFORM_LIB_EXT,
filename,
bridge->gamesuffix);
if (!libsys->IsPathFile(path)) found:
{ Initialize(filename, path);
/* 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,
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);
}
//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/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/%s." PLATFORM_LIB_EXT,
filename);
}
}
}
}
Initialize(filename, path, bRequired);
} }
bool CRemoteExtension::Load(char *error, size_t maxlength) bool CRemoteExtension::Load(char *error, size_t maxlength)
@ -157,7 +137,7 @@ bool CRemoteExtension::Load(char *error, size_t maxlength)
bool CLocalExtension::Load(char *error, size_t maxlength) bool CLocalExtension::Load(char *error, size_t maxlength)
{ {
m_pLib = libsys->OpenLibrary(m_Path.c_str(), error, maxlength); m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
if (m_pLib == NULL) if (m_pLib == NULL)
{ {
@ -171,7 +151,7 @@ bool CLocalExtension::Load(char *error, size_t maxlength)
{ {
m_pLib->CloseLibrary(); m_pLib->CloseLibrary();
m_pLib = NULL; m_pLib = NULL;
ke::SafeStrcpy(error, maxlength, "Unable to find extension entry point"); snprintf(error, maxlength, "Unable to find extension entry point");
return false; return false;
} }
@ -189,16 +169,8 @@ bool CLocalExtension::Load(char *error, size_t maxlength)
/* Load as MM:S */ /* Load as MM:S */
if (m_pAPI->IsMetamodExtension()) if (m_pAPI->IsMetamodExtension())
{ {
bool ok; bool already;
m_PlId = bridge->LoadMMSPlugin(m_Path.c_str(), &ok, error, maxlength); m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
if (!m_PlId || !ok)
{
m_pLib->CloseLibrary();
m_pLib = NULL;
m_pAPI = NULL;
return false;
}
} }
if (!CExtension::Load(error, maxlength)) if (!CExtension::Load(error, maxlength))
@ -207,7 +179,8 @@ bool CLocalExtension::Load(char *error, size_t maxlength)
{ {
if (m_PlId) if (m_PlId)
{ {
bridge->UnloadMMSPlugin(m_PlId); char dummy[255];
g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
m_PlId = 0; m_PlId = 0;
} }
} }
@ -228,7 +201,8 @@ void CLocalExtension::Unload()
{ {
if (m_pAPI != NULL && m_PlId) if (m_pAPI != NULL && m_PlId)
{ {
bridge->UnloadMMSPlugin(m_PlId); char temp_buffer[255];
g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
m_PlId = 0; m_PlId = 0;
} }
@ -241,7 +215,7 @@ void CLocalExtension::Unload()
bool CRemoteExtension::Reload(char *error, size_t maxlength) bool CRemoteExtension::Reload(char *error, size_t maxlength)
{ {
ke::SafeStrcpy(error, maxlength, "Remote extensions do not support reloading"); snprintf(error, maxlength, "Remote extensions do not support reloading");
return false; return false;
} }
@ -275,13 +249,13 @@ bool CExtension::PerformAPICheck(char *error, size_t maxlength)
{ {
if (!m_pAPI) if (!m_pAPI)
{ {
ke::SafeStrcpy(error, maxlength, "No IExtensionInterface instance provided"); snprintf(error, maxlength, "No IExtensionInterface instance provided");
return false; return false;
} }
if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) if (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); snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
return false; return false;
} }
@ -291,17 +265,18 @@ bool CExtension::PerformAPICheck(char *error, size_t maxlength)
bool CExtension::Load(char *error, size_t maxlength) bool CExtension::Load(char *error, size_t maxlength)
{ {
CreateIdentity(); CreateIdentity();
if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !bridge->IsMapLoading())) if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
{ {
g_ShareSys.RemoveInterfaces(this);
DestroyIdentity(); DestroyIdentity();
return false; return false;
} }
else
/* Check if we're past load time */
if (!bridge->IsMapLoading())
{ {
m_pAPI->OnExtensionsAllLoaded(); /* Check if we're past load time */
if (!g_SourceMod.IsMapLoading())
{
m_pAPI->OnExtensionsAllLoaded();
}
} }
return true; return true;
@ -341,7 +316,7 @@ void CExtension::MarkAllLoaded()
void CExtension::AddPlugin(CPlugin *pPlugin) void CExtension::AddPlugin(CPlugin *pPlugin)
{ {
/* Unfortunately we have to do this :( */ /* Unfortunately we have to do this :( */
if (m_Dependents.find(pPlugin) == m_Dependents.end()) if (m_Dependents.find(pPlugin) != m_Dependents.end())
{ {
m_Dependents.push_back(pPlugin); m_Dependents.push_back(pPlugin);
} }
@ -362,11 +337,6 @@ const char *CExtension::GetFilename()
return m_RealFile.c_str(); return m_RealFile.c_str();
} }
const char *CExtension::GetPath() const
{
return m_Path.c_str();
}
IdentityToken_t *CExtension::GetIdentity() IdentityToken_t *CExtension::GetIdentity()
{ {
return m_pIdentToken; return m_pIdentToken;
@ -416,20 +386,65 @@ void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
m_ChildDeps.push_back(info); m_ChildDeps.push_back(info);
} }
// note: dependency iteration deprecated since 1.10
ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
{ {
return nullptr; 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;
} }
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
{ {
return false; 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;
} }
void CExtension::FreeDependencyIterator(ITERATOR *iter) void CExtension::FreeDependencyIterator(ITERATOR *iter)
{ {
List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
delete pIter;
} }
void CExtension::AddInterface(SMInterface *pInterface) void CExtension::AddInterface(SMInterface *pInterface)
@ -443,7 +458,7 @@ bool CExtension::IsRunning(char *error, size_t maxlength)
{ {
if (error) if (error)
{ {
ke::SafeStrcpy(error, maxlength, m_Error.c_str()); snprintf(error, maxlength, "%s", m_Error.c_str());
} }
return false; return false;
} }
@ -456,11 +471,6 @@ void CExtension::AddLibrary(const char *library)
m_Libraries.push_back(library); m_Libraries.push_back(library);
} }
bool CExtension::IsRequired()
{
return m_bRequired;
}
/********************* /*********************
* EXTENSION MANAGER * * EXTENSION MANAGER *
*********************/ *********************/
@ -468,15 +478,14 @@ bool CExtension::IsRequired()
void CExtensionManager::OnSourceModAllInitialized() void CExtensionManager::OnSourceModAllInitialized()
{ {
g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
pluginsys->AddPluginsListener(this); g_PluginSys.AddPluginsListener(this);
rootmenu->AddRootConsoleCommand3("exts", "Manage extensions", this); g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
g_ShareSys.AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
} }
void CExtensionManager::OnSourceModShutdown() void CExtensionManager::OnSourceModShutdown()
{ {
rootmenu->RemoveRootConsoleCommand("exts", this); g_RootMenu.RemoveRootConsoleCommand("exts", this);
pluginsys->RemovePluginsListener(this);
g_ShareSys.DestroyIdentType(g_ExtType); g_ShareSys.DestroyIdentType(g_ExtType);
} }
@ -494,11 +503,13 @@ void CExtensionManager::TryAutoload()
{ {
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
g_pSM->BuildPath(Path_SM, path, sizeof(path), "extensions"); g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
ke::AutoPtr<IDirectory> pDir(libsys->OpenDirectory(path)); IDirectory *pDir = g_LibSys.OpenDirectory(path);
if (!pDir) if (!pDir)
{
return; return;
}
const char *lfile; const char *lfile;
size_t len; size_t len;
@ -525,7 +536,7 @@ void CExtensionManager::TryAutoload()
} }
char file[PLATFORM_MAX_PATH]; char file[PLATFORM_MAX_PATH];
len = ke::SafeStrcpy(file, sizeof(file), lfile); len = UTIL_Format(file, sizeof(file), "%s", lfile);
strcpy(&file[len - 9], ".ext"); strcpy(&file[len - 9], ".ext");
LoadAutoExtension(file); LoadAutoExtension(file);
@ -534,16 +545,16 @@ void CExtensionManager::TryAutoload()
} }
} }
IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOnMissing) IExtension *CExtensionManager::LoadAutoExtension(const char *path)
{ {
/* Remove platform extension if it's there. Compat hack. */ /* Remove platform extension if it's there. Compat hack. */
const char *ext = libsys->GetFileExtension(path); const char *ext = g_LibSys.GetFileExtension(path);
if (strcmp(ext, PLATFORM_LIB_EXT) == 0) if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
{ {
char path2[PLATFORM_MAX_PATH]; char path2[PLATFORM_MAX_PATH];
ke::SafeStrcpy(path2, sizeof(path2), path); UTIL_Format(path2, sizeof(path2), "%s", path);
path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
return LoadAutoExtension(path2, bErrorOnMissing); return LoadAutoExtension(path2);
} }
IExtension *pAlready; IExtension *pAlready;
@ -553,7 +564,7 @@ IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOn
} }
char error[256]; char error[256];
CExtension *p = new CLocalExtension(path, bErrorOnMissing); CExtension *p = new CLocalExtension(path);
/* We put us in the list beforehand so extensions that check for each other /* We put us in the list beforehand so extensions that check for each other
* won't recursively load each other. * won't recursively load each other.
@ -562,11 +573,7 @@ IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOn
if (!p->Load(error, sizeof(error)) || !p->IsLoaded()) if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
{ {
if (bErrorOnMissing || libsys->IsPathFile(p->GetPath())) g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
{
logger->LogError("[SM] Unable to load extension \"%s\": %s", path, error);
}
p->SetError(error); p->SetError(error);
} }
@ -580,7 +587,7 @@ IExtension *CExtensionManager::FindExtensionByFile(const char *file)
/* Chomp off the path */ /* Chomp off the path */
char lookup[PLATFORM_MAX_PATH]; char lookup[PLATFORM_MAX_PATH];
libsys->GetFileFromPath(lookup, sizeof(lookup), file); g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
{ {
@ -629,11 +636,11 @@ IExtension *CExtensionManager::FindExtensionByName(const char *ext)
IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength) IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
{ {
/* Remove platform extension if it's there. Compat hack. */ /* Remove platform extension if it's there. Compat hack. */
const char *ext = libsys->GetFileExtension(file); const char *ext = g_LibSys.GetFileExtension(file);
if (strcmp(ext, PLATFORM_LIB_EXT) == 0) if (strcmp(ext, PLATFORM_LIB_EXT) == 0)
{ {
char path2[PLATFORM_MAX_PATH]; char path2[PLATFORM_MAX_PATH];
ke::SafeStrcpy(path2, sizeof(path2), file); UTIL_Format(path2, sizeof(path2), "%s", file);
path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0';
return LoadExtension(path2, error, maxlength); return LoadExtension(path2, error, maxlength);
} }
@ -695,11 +702,11 @@ void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface
pExt->AddInterface(pInterface); pExt->AddInterface(pInterface);
} }
void CExtensionManager::BindChildPlugin(IExtension *pParent, SMPlugin *pPlugin) void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin )
{ {
CExtension *pExt = (CExtension *)pParent; CExtension *pExt = (CExtension *)pParent;
pExt->AddPlugin(static_cast<CPlugin *>(pPlugin)); pExt->AddPlugin(pPlugin);
} }
void CExtensionManager::OnPluginDestroyed(IPlugin *plugin) void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
@ -708,7 +715,7 @@ void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
{ {
(*iter)->DropRefsTo(static_cast<CPlugin *>(plugin)); (*iter)->DropRefsTo((CPlugin *)plugin);
} }
} }
@ -736,21 +743,18 @@ CExtension *CExtensionManager::FindByOrder(unsigned int num)
bool CExtensionManager::UnloadExtension(IExtension *_pExt) bool CExtensionManager::UnloadExtension(IExtension *_pExt)
{ {
if (!_pExt) if (!_pExt)
{
return false; return false;
}
CExtension *pExt = (CExtension *)_pExt; CExtension *pExt = (CExtension *)_pExt;
if (m_Libs.find(pExt) == m_Libs.end()) if (m_Libs.find(pExt) == m_Libs.end())
{
return false; return false;
}
/* Tell it to unload */ /* First remove us from internal lists */
if (pExt->IsLoaded())
pExt->GetAPI()->OnExtensionUnload();
// Remove us from internal lists. Note that because we do this, it's
// possible that our extension could be added back if another plugin
// tries to load during this process. If we ever find this to happen,
// we can just block plugin loading.
g_ShareSys.RemoveInterfaces(_pExt); g_ShareSys.RemoveInterfaces(_pExt);
m_Libs.remove(pExt); m_Libs.remove(pExt);
@ -764,7 +768,7 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
while (p_iter != pExt->m_Dependents.end()) while (p_iter != pExt->m_Dependents.end())
{ {
/* We have to manually unlink ourselves here, since we're no longer being managed */ /* We have to manually unlink ourselves here, since we're no longer being managed */
scripts->UnloadPlugin((*p_iter)); g_PluginSys.UnloadPlugin((*p_iter));
p_iter = pExt->m_Dependents.erase(p_iter); p_iter = pExt->m_Dependents.erase(p_iter);
} }
@ -773,7 +777,7 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
s_iter != pExt->m_Libraries.end(); s_iter != pExt->m_Libraries.end();
s_iter++) s_iter++)
{ {
scripts->OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed); g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
} }
/* Notify and/or unload all dependencies */ /* Notify and/or unload all dependencies */
@ -784,9 +788,13 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
{ {
pDep = (*c_iter); pDep = (*c_iter);
if ((pAPI=pDep->GetAPI()) == NULL) if ((pAPI=pDep->GetAPI()) == NULL)
{
continue; continue;
}
if (pDep == pExt) if (pDep == pExt)
{
continue; continue;
}
/* Now, get its dependency list */ /* Now, get its dependency list */
bool dropped = false; bool dropped = false;
List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin(); List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
@ -815,9 +823,13 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
while (i_iter != pDep->m_ChildDeps.end()) while (i_iter != pDep->m_ChildDeps.end())
{ {
if ((*i_iter).owner == pExt) if ((*i_iter).owner == pExt)
{
i_iter = pDep->m_ChildDeps.erase(i_iter); i_iter = pDep->m_ChildDeps.erase(i_iter);
}
else else
{
i_iter++; i_iter++;
}
} }
} }
@ -836,10 +848,12 @@ bool CExtensionManager::UnloadExtension(IExtension *_pExt)
} }
} }
// Everything has been informed that we're unloading, so give the /* Tell it to unload */
// extension one last notification. if (pExt->IsLoaded())
if (pExt->IsLoaded() && pExt->GetAPI()->GetExtensionVersion() >= 7) {
pExt->GetAPI()->OnDependenciesDropped(); IExtensionInterface *pAPI = pExt->GetAPI();
pAPI->OnExtensionUnload();
}
pExt->Unload(); pExt->Unload();
delete pExt; delete pExt;
@ -886,37 +900,36 @@ void CExtensionManager::AddDependency(IExtension *pSource, const char *file, boo
} }
} }
void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
{ {
int argcount = command->ArgC(); int argcount = command.ArgC();
if (argcount >= 3) if (argcount >= 3)
{ {
const char *cmd = command->Arg(2); const char *cmd = command.Arg(2);
if (strcmp(cmd, "list") == 0) if (strcmp(cmd, "list") == 0)
{ {
List<CExtension *>::iterator iter; List<CExtension *>::iterator iter;
CExtension *pExt; CExtension *pExt;
unsigned int num = 1; unsigned int num = 1;
switch (m_Libs.size()) switch (m_Libs.size())
{ {
case 1: case 1:
{ {
rootmenu->ConsolePrint("[SM] Displaying 1 extension:"); g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
break; break;
} }
case 0: case 0:
{ {
rootmenu->ConsolePrint("[SM] No extensions are loaded."); g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
break; break;
} }
default: default:
{ {
rootmenu->ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size()); g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
break; break;
} }
} }
for (iter = m_Libs.begin(); iter != m_Libs.end(); iter++,num++) for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
{ {
pExt = (*iter); pExt = (*iter);
if (pExt->IsLoaded()) if (pExt->IsLoaded())
@ -924,7 +937,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
char error[255]; char error[255];
if (!pExt->IsRunning(error, sizeof(error))) if (!pExt->IsRunning(error, sizeof(error)))
{ {
rootmenu->ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error); g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
} }
else else
{ {
@ -932,16 +945,10 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
const char *name = pAPI->GetExtensionName(); const char *name = pAPI->GetExtensionName();
const char *version = pAPI->GetExtensionVerString(); const char *version = pAPI->GetExtensionVerString();
const char *descr = pAPI->GetExtensionDescription(); const char *descr = pAPI->GetExtensionDescription();
rootmenu->ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
} }
} } else {
else if(pExt->IsRequired() || libsys->PathExists(pExt->GetPath())) g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
{
rootmenu->ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
}
else
{
rootmenu->ConsolePrint("[%02d] <OPTIONAL> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
} }
} }
return; return;
@ -950,29 +957,29 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
{ {
if (argcount < 4) if (argcount < 4)
{ {
rootmenu->ConsolePrint("[SM] Usage: sm exts load <file>"); g_RootMenu.ConsolePrint("[SM] Usage: sm exts load <file>");
return; return;
} }
const char *filename = command->Arg(3); const char *filename = command.Arg(3);
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
char error[256]; char error[256];
ke::SafeSprintf(path, sizeof(path), "%s%s%s", filename, !strstr(filename, ".ext") ? ".ext" : "", UTIL_Format(path, sizeof(path), "%s%s%s", filename, !strstr(filename, ".ext") ? ".ext" : "",
!strstr(filename, "." PLATFORM_LIB_EXT) ? "." PLATFORM_LIB_EXT : ""); !strstr(filename, "." PLATFORM_LIB_EXT) ? "." PLATFORM_LIB_EXT : "");
if (FindExtensionByFile(path) != NULL) if (FindExtensionByFile(path) != NULL)
{ {
rootmenu->ConsolePrint("[SM] Extension %s is already loaded.", path); g_RootMenu.ConsolePrint("[SM] Extension %s is already loaded.", path);
return; return;
} }
if (LoadExtension(path, error, sizeof(error))) if (LoadExtension(path, error, sizeof(error)))
{ {
rootmenu->ConsolePrint("[SM] Loaded extension %s successfully.", path); g_RootMenu.ConsolePrint("[SM] Loaded extension %s successfully.", path);
} else } else
{ {
rootmenu->ConsolePrint("[SM] Extension %s failed to load: %s", path, error); g_RootMenu.ConsolePrint("[SM] Extension %s failed to load: %s", path, error);
} }
return; return;
@ -981,27 +988,27 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
{ {
if (argcount < 4) if (argcount < 4)
{ {
rootmenu->ConsolePrint("[SM] Usage: sm exts info <#>"); g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
return; return;
} }
const char *sId = command->Arg(3); const char *sId = command.Arg(3);
unsigned int id = atoi(sId); unsigned int id = atoi(sId);
if (id <= 0) if (id <= 0)
{ {
rootmenu->ConsolePrint("[SM] Usage: sm exts info <#>"); g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
return; return;
} }
if (m_Libs.size() == 0) if (m_Libs.size() == 0)
{ {
rootmenu->ConsolePrint("[SM] No extensions are loaded."); g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
return; return;
} }
if (id > m_Libs.size()) if (id > m_Libs.size())
{ {
rootmenu->ConsolePrint("[SM] No extension was found with id %d.", id); g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
return; return;
} }
@ -1019,44 +1026,44 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
/* This should never happen */ /* This should never happen */
if (!pExt) if (!pExt)
{ {
rootmenu->ConsolePrint("[SM] No extension was found with id %d.", id); g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
return; return;
} }
if (!pExt->IsLoaded()) if (!pExt->IsLoaded())
{ {
rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
rootmenu->ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str()); g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
} }
else else
{ {
char error[255]; char error[255];
if (!pExt->IsRunning(error, sizeof(error))) if (!pExt->IsRunning(error, sizeof(error)))
{ {
rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
rootmenu->ConsolePrint(" Loaded: Yes"); g_RootMenu.ConsolePrint(" Loaded: Yes");
rootmenu->ConsolePrint(" Running: No (%s)", error); g_RootMenu.ConsolePrint(" Running: No (%s)", error);
} }
else else
{ {
IExtensionInterface *pAPI = pExt->GetAPI(); IExtensionInterface *pAPI = pExt->GetAPI();
rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
rootmenu->ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
rootmenu->ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
rootmenu->ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
rootmenu->ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
if (pExt->IsExternal()) if (pExt->IsExternal())
{ {
rootmenu->ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod"); g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
} }
else if (pAPI->IsMetamodExtension()) else if (pAPI->IsMetamodExtension())
{ {
rootmenu->ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source"); g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
} }
else else
{ {
rootmenu->ConsolePrint(" Method: Loaded by SourceMod"); g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
} }
} }
} }
@ -1066,33 +1073,33 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
{ {
if (argcount < 4) if (argcount < 4)
{ {
rootmenu->ConsolePrint("[SM] Usage: sm exts unload <#> [code]"); g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
return; return;
} }
const char *arg = command->Arg(3); const char *arg = command.Arg(3);
unsigned int num = atoi(arg); unsigned int num = atoi(arg);
CExtension *pExt = FindByOrder(num); CExtension *pExt = FindByOrder(num);
if (!pExt) if (!pExt)
{ {
rootmenu->ConsolePrint("[SM] Extension number %d was not found.", num); g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
return; return;
} }
if (argcount > 4 && pExt->unload_code) if (argcount > 4 && pExt->unload_code)
{ {
const char *unload = command->Arg(4); const char *unload = command.Arg(4);
if (pExt->unload_code == (unsigned)atoi(unload)) if (pExt->unload_code == (unsigned)atoi(unload))
{ {
char filename[PLATFORM_MAX_PATH]; char filename[PLATFORM_MAX_PATH];
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename()); snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
UnloadExtension(pExt); UnloadExtension(pExt);
rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename); g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
} }
else else
{ {
rootmenu->ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code); g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
} }
return; return;
} }
@ -1101,9 +1108,9 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size())) || (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
{ {
char filename[PLATFORM_MAX_PATH]; char filename[PLATFORM_MAX_PATH];
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename()); snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
UnloadExtension(pExt); UnloadExtension(pExt);
rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename); g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
return; return;
} }
else else
@ -1111,7 +1118,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
List<CPlugin *> plugins; List<CPlugin *> plugins;
if (pExt->m_ChildDeps.size()) if (pExt->m_ChildDeps.size())
{ {
rootmenu->ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename()); g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
List<CExtension *>::iterator iter; List<CExtension *>::iterator iter;
CExtension *pOther; CExtension *pOther;
/* Get list of all extensions */ /* Get list of all extensions */
@ -1136,7 +1143,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
/* Will our dependent care? */ /* Will our dependent care? */
if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface)) if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
{ {
rootmenu->ConsolePrint(" -> %s", pOther->GetFilename()); g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
/* Add to plugin unload list */ /* Add to plugin unload list */
List<CPlugin *>::iterator p_iter; List<CPlugin *>::iterator p_iter;
for (p_iter=pOther->m_Dependents.begin(); for (p_iter=pOther->m_Dependents.begin();
@ -1154,7 +1161,7 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
} }
if (pExt->m_Dependents.size()) if (pExt->m_Dependents.size())
{ {
rootmenu->ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
List<CPlugin *>::iterator iter; List<CPlugin *>::iterator iter;
CPlugin *pPlugin; CPlugin *pPlugin;
for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++) for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++)
@ -1168,13 +1175,13 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
for (iter = plugins.begin(); iter != plugins.end(); iter++) for (iter = plugins.begin(); iter != plugins.end(); iter++)
{ {
pPlugin = (*iter); pPlugin = (*iter);
rootmenu->ConsolePrint(" -> %s", pPlugin->GetFilename()); g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
} }
} }
srand(static_cast<int>(time(NULL))); srand(static_cast<int>(time(NULL)));
pExt->unload_code = (rand() % 877) + 123; //123 to 999 pExt->unload_code = (rand() % 877) + 123; //123 to 999
rootmenu->ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename()); g_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); g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
return; return;
} }
@ -1183,17 +1190,17 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
{ {
if (argcount < 4) if (argcount < 4)
{ {
rootmenu->ConsolePrint("[SM] Usage: sm exts reload <#>"); g_RootMenu.ConsolePrint("[SM] Usage: sm exts reload <#>");
return; return;
} }
const char *arg = command->Arg(3); const char *arg = command.Arg(3);
unsigned int num = atoi(arg); unsigned int num = atoi(arg);
CExtension *pExt = FindByOrder(num); CExtension *pExt = FindByOrder(num);
if (!pExt) if (!pExt)
{ {
rootmenu->ConsolePrint("[SM] Extension number %d was not found.", num); g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
return; return;
} }
@ -1202,22 +1209,22 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
char filename[PLATFORM_MAX_PATH]; char filename[PLATFORM_MAX_PATH];
char error[255]; char error[255];
ke::SafeStrcpy(filename, PLATFORM_MAX_PATH, pExt->GetFilename()); snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
if (pExt->Reload(error, sizeof(error))) if (pExt->Reload(error, sizeof(error)))
{ {
rootmenu->ConsolePrint("[SM] Extension %s is now reloaded.", filename); g_RootMenu.ConsolePrint("[SM] Extension %s is now reloaded.", filename);
} }
else else
{ {
rootmenu->ConsolePrint("[SM] Extension %s failed to reload: %s", filename, error); g_RootMenu.ConsolePrint("[SM] Extension %s failed to reload: %s", filename, error);
} }
return; return;
} }
else else
{ {
rootmenu->ConsolePrint("[SM] Extension %s is not loaded.", pExt->GetFilename()); g_RootMenu.ConsolePrint("[SM] Extension %s is not loaded.", pExt->GetFilename());
return; return;
} }
@ -1225,12 +1232,12 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const ICommand
} }
} }
rootmenu->ConsolePrint("SourceMod Extensions Menu:"); g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
rootmenu->DrawGenericOption("info", "Extra extension information"); g_RootMenu.DrawGenericOption("info", "Extra extension information");
rootmenu->DrawGenericOption("list", "List extensions"); g_RootMenu.DrawGenericOption("list", "List extensions");
rootmenu->DrawGenericOption("load", "Load an extension"); g_RootMenu.DrawGenericOption("load", "Load an extension");
rootmenu->DrawGenericOption("reload", "Reload an extension"); g_RootMenu.DrawGenericOption("reload", "Reload an extension");
rootmenu->DrawGenericOption("unload", "Unload an extension"); g_RootMenu.DrawGenericOption("unload", "Unload an extension");
} }
CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr) CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
@ -1255,7 +1262,7 @@ void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
{ {
CExtension *pExt = (CExtension *)pSource; CExtension *pExt = (CExtension *)pSource;
pExt->AddLibrary(library); pExt->AddLibrary(library);
scripts->OnLibraryAction(library, LibraryAction_Added); g_PluginSys.OnLibraryAction(library, false, false);
} }
bool CExtensionManager::LibraryExists(const char *library) bool CExtensionManager::LibraryExists(const char *library)
@ -1325,37 +1332,6 @@ void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount,
} }
} }
void CExtensionManager::CallOnCoreMapEnd()
{
IExtensionInterface *pAPI;
List<CExtension *>::iterator iter;
for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
{
if ((pAPI = (*iter)->GetAPI()) == NULL)
{
continue;
}
if (pAPI->GetExtensionVersion() > 7)
{
pAPI->OnCoreMapEnd();
}
}
}
const CVector<IExtension *> *CExtensionManager::ListExtensions()
{
CVector<IExtension *> *list = new CVector<IExtension *>();
for (List<CExtension *>::iterator iter = m_Libs.begin(); iter != m_Libs.end(); iter++)
list->push_back(*iter);
return list;
}
void CExtensionManager::FreeExtensionList(const CVector<IExtension *> *list)
{
delete const_cast<CVector<IExtension *> *>(list);
}
bool CLocalExtension::IsSameFile(const char *file) bool CLocalExtension::IsSameFile(const char *file)
{ {
/* Only care about the shortened name. */ /* Only care about the shortened name. */

View File

@ -36,14 +36,14 @@
#include <ILibrarySys.h> #include <ILibrarySys.h>
#include <sh_list.h> #include <sh_list.h>
#include <sh_string.h> #include <sh_string.h>
#include "common_logic.h" #include <sm_trie_tpl.h>
#include "sm_globals.h"
#include "ShareSys.h"
#include <ISmmAPI.h>
#include <IPluginSys.h> #include <IPluginSys.h>
#include <IRootConsoleMenu.h> #include <IRootConsoleMenu.h>
#include "PluginSys.h"
#include "NativeOwner.h" #include "NativeOwner.h"
#include "ShareSys.h"
#include <bridge/include/IExtensionBridge.h>
class CPlayer;
using namespace SourceMod; using namespace SourceMod;
using namespace SourceHook; using namespace SourceHook;
@ -66,7 +66,6 @@ public:
public: //IExtension public: //IExtension
IExtensionInterface *GetAPI(); IExtensionInterface *GetAPI();
const char *GetFilename(); const char *GetFilename();
const char *GetPath() const;
IdentityToken_t *GetIdentity(); IdentityToken_t *GetIdentity();
ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface); ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface);
bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface); bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface);
@ -80,7 +79,6 @@ public:
void AddPlugin(CPlugin *pPlugin); void AddPlugin(CPlugin *pPlugin);
void MarkAllLoaded(); void MarkAllLoaded();
void AddLibrary(const char *library); void AddLibrary(const char *library);
bool IsRequired();
public: public:
virtual bool Load(char *error, size_t maxlength); virtual bool Load(char *error, size_t maxlength);
virtual bool IsLoaded() =0; virtual bool IsLoaded() =0;
@ -88,7 +86,7 @@ public:
virtual bool Reload(char *error, size_t maxlength) =0; virtual bool Reload(char *error, size_t maxlength) =0;
virtual bool IsSameFile(const char* file) =0; virtual bool IsSameFile(const char* file) =0;
protected: protected:
void Initialize(const char *filename, const char *path, bool bRequired = true); void Initialize(const char *filename, const char *path);
bool PerformAPICheck(char *error, size_t maxlength); bool PerformAPICheck(char *error, size_t maxlength);
void CreateIdentity(); void CreateIdentity();
void DestroyIdentity(); void DestroyIdentity();
@ -105,13 +103,12 @@ protected:
List<String> m_Libraries; List<String> m_Libraries;
unsigned int unload_code; unsigned int unload_code;
bool m_bFullyLoaded; bool m_bFullyLoaded;
bool m_bRequired;
}; };
class CLocalExtension : public CExtension class CLocalExtension : public CExtension
{ {
public: public:
CLocalExtension(const char *filename, bool bRequired = true); CLocalExtension(const char *filename);
public: public:
bool Load(char *error, size_t maxlength); bool Load(char *error, size_t maxlength);
bool IsLoaded(); bool IsLoaded();
@ -120,7 +117,7 @@ public:
bool IsExternal(); bool IsExternal();
bool IsSameFile(const char *file); bool IsSameFile(const char *file);
private: private:
int m_PlId; PluginId m_PlId;
ILibrary *m_pLib; ILibrary *m_pLib;
}; };
@ -138,7 +135,7 @@ public:
}; };
class CExtensionManager : class CExtensionManager :
public IExtensionSys, public IExtensionManager,
public SMGlobalClass, public SMGlobalClass,
public IPluginsListener, public IPluginsListener,
public IRootConsoleCommand public IRootConsoleCommand
@ -164,22 +161,19 @@ public: //IExtensionManager
public: //IPluginsListener public: //IPluginsListener
void OnPluginDestroyed(IPlugin *plugin); void OnPluginDestroyed(IPlugin *plugin);
public: //IRootConsoleCommand public: //IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) override; void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public: public:
IExtension *LoadAutoExtension(const char *path, bool bErrorOnMissing=true); IExtension *LoadAutoExtension(const char *path);
void BindDependency(IExtension *pOwner, IfaceInfo *pInfo); void BindDependency(IExtension *pOwner, IfaceInfo *pInfo);
void AddInterface(IExtension *pOwner, SMInterface *pInterface); void AddInterface(IExtension *pOwner, SMInterface *pInterface);
void BindChildPlugin(IExtension *pParent, SMPlugin *pPlugin); void BindChildPlugin(IExtension *pParent, CPlugin *pPlugin);
void MarkAllLoaded(); void MarkAllLoaded();
void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload); void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload);
void TryAutoload(); void TryAutoload();
void AddLibrary(IExtension *pSource, const char *library); void AddLibrary(IExtension *pSource, const char *library);
bool LibraryExists(const char *library); bool LibraryExists(const char *library);
void CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax); void CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax);
void CallOnCoreMapEnd();
void AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface); void AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface);
const CVector<IExtension *> *ListExtensions();
void FreeExtensionList(const CVector<IExtension *> *list);
public: public:
CExtension *GetExtensionFromIdent(IdentityToken_t *ptr); CExtension *GetExtensionFromIdent(IdentityToken_t *ptr);
void Shutdown(); void Shutdown();

View File

@ -1,67 +1,87 @@
// vim: set ts=4 sw=4 tw=99 noet : /**
// ============================================================================= * vim: set ts=4 sw=4 tw=99 noet :
// SourceMod * =============================================================================
// Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * SourceMod
// ============================================================================= * Copyright (C) 2004-2008 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 * This program is free software; you can redistribute it and/or modify it under
// Free Software Foundation. * 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 * This program is distributed in the hope that it will be useful, but WITHOUT
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// details. * 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/>. * 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 * As a special exception, AlliedModders LLC gives you permission to link the
// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software * code of this program (as well as its derivative works) to "Half-Life 2," the
// by the Valve Corporation. You must obey the GNU General Public License in * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
// all respects for all other code used. Additionally, AlliedModders LLC grants * by the Valve Corporation. You must obey the GNU General Public License in
// this exception to all derivative works. AlliedModders LLC defines further * all respects for all other code used. Additionally, AlliedModders LLC grants
// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * this exception to all derivative works. AlliedModders LLC defines further
// or <http://www.sourcemod.net/license.php>. * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include "ForwardSys.h" #include "ForwardSys.h"
#include "PluginSys.h"
#include "ShareSys.h"
#include "DebugReporter.h" #include "DebugReporter.h"
#include "common_logic.h"
#include <bridge/include/IScriptManager.h>
#include <amtl/am-string.h>
#include <ReentrantList.h>
using namespace ke;
CForwardManager g_Forwards; CForwardManager g_Forwards;
// Genesis turns to its source, reduction occurs stepwise although the essence /**
// is all one. End of line. FTL system check. * Genesis turns to its source, reduction occurs stepwise although the essence is all one.
* End of line. FTL system check.
*
* :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST
* VARARG FUNCTIONS:
* - Pushing no varargs
*/
// :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails
// so later evaluation of this result may cause problems on higher levels of abstraction. DOCUMENT OR FIX ALL FORWARDS!
CForwardManager::~CForwardManager()
{
CStack<CForward *>::iterator iter;
for (iter=m_FreeForwards.begin(); iter!=m_FreeForwards.end(); iter++)
{
delete (*iter);
}
m_FreeForwards.popall();
}
void CForwardManager::OnSourceModAllInitialized() void CForwardManager::OnSourceModAllInitialized()
{ {
scripts->AddPluginsListener(this); g_PluginSys.AddPluginsListener(this);
sharesys->AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
} }
IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...) IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...)
{ {
CForward *fwd;
va_list ap; va_list ap;
va_start(ap, types); va_start(ap, types);
CForward *fwd = CForward::CreateForward(name, et, num_params, types, ap); fwd = CForward::CreateForward(name, et, num_params, types, ap);
va_end(ap); va_end(ap);
if (fwd) if (fwd)
{ {
scripts->AddFunctionsToForward(name, fwd); g_PluginSys.AddFunctionsToForward(name, fwd);
m_managed.append(fwd); m_managed.push_back(fwd);
} }
return fwd; return fwd;
@ -69,16 +89,17 @@ IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned
IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...) IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...)
{ {
CForward *fwd;
va_list ap; va_list ap;
va_start(ap, types); va_start(ap, types);
CForward *fwd = CForward::CreateForward(name, et, num_params, types, ap); fwd = CForward::CreateForward(name, et, num_params, types, ap);
va_end(ap); va_end(ap);
if (fwd) if (fwd)
{ {
m_unmanaged.append(fwd); m_unmanaged.push_back(fwd);
} }
return fwd; return fwd;
@ -87,103 +108,115 @@ IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType
void CForwardManager::OnPluginLoaded(IPlugin *plugin) void CForwardManager::OnPluginLoaded(IPlugin *plugin)
{ {
/* Attach any globally managed forwards */ /* Attach any globally managed forwards */
for (ForwardIter iter(m_managed); !iter.done(); iter.next()) { List<CForward *>::iterator iter;
CForward *fwd = (*iter); CForward *fwd;
for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
{
fwd = (*iter);
IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName()); IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName());
if (pFunc) if (pFunc)
fwd->AddFunction(pFunc);
}
}
void CForwardManager::OnPluginUnloaded(IPlugin *plugin)
{
for (ForwardIter iter(m_managed); !iter.done(); iter.next()) {
CForward *fwd = (*iter);
fwd->RemoveFunctionsOfPlugin(plugin);
}
for (ForwardIter iter(m_unmanaged); !iter.done(); iter.next()) {
CForward *fwd = (*iter);
fwd->RemoveFunctionsOfPlugin(plugin);
}
}
IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng)
{
for (ForwardIter iter(m_managed); !iter.done(); iter.next()) {
CForward *fwd = (*iter);
if (strcmp(fwd->GetForwardName(), name) == 0) {
if (ifchng)
*ifchng = NULL;
return fwd;
}
}
for (ForwardIter iter(m_unmanaged); !iter.done(); iter.next()) {
CForward *fwd = (*iter);
if (strcmp(fwd->GetForwardName(), name) == 0) {
if (ifchng)
*ifchng = fwd;
return fwd;
}
}
if (ifchng)
*ifchng = NULL;
return NULL;
}
void CForwardManager::ReleaseForward(IForward *aForward)
{
CForward *fwd = static_cast<CForward *>(aForward);
m_managed.remove(fwd);
m_unmanaged.remove(fwd);
delete fwd;
}
void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused)
{
if (paused)
return;
/* Attach any globally managed forwards */
for (ForwardIter iter(m_managed); !iter.done(); iter.next()) {
CForward *fwd = (*iter);
IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName());
// Only add functions, if they aren't registered yet!
if (pFunc && !fwd->IsFunctionRegistered(pFunc))
{ {
fwd->AddFunction(pFunc); fwd->AddFunction(pFunc);
} }
} }
} }
void CForwardManager::OnPluginUnloaded(IPlugin *plugin)
{
List<CForward *>::iterator iter;
CForward *fwd;
for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
{
fwd = (*iter);
fwd->RemoveFunctionsOfPlugin(plugin);
}
for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++)
{
fwd = (*iter);
fwd->RemoveFunctionsOfPlugin(plugin);
}
}
IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng)
{
List<CForward *>::iterator iter;
CForward *fwd;
for (iter=m_managed.begin(); iter!=m_managed.end(); iter++)
{
fwd = (*iter);
if (strcmp(fwd->GetForwardName(), name) == 0)
{
if (ifchng)
{
*ifchng = NULL;
}
return fwd;
}
}
for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++)
{
fwd = (*iter);
if (strcmp(fwd->GetForwardName(), name) == 0)
{
if (ifchng)
{
*ifchng = fwd;
}
return fwd;
}
}
if (ifchng)
{
*ifchng = NULL;
}
return NULL;
}
void CForwardManager::ReleaseForward(IForward *forward)
{
ForwardFree(static_cast<CForward *>(forward));
}
void CForwardManager::ForwardFree(CForward *fwd)
{
if (fwd == NULL)
{
return;
}
m_FreeForwards.push(fwd);
m_managed.remove(fwd);
}
CForward *CForwardManager::ForwardMake()
{
CForward *fwd;
if (m_FreeForwards.empty())
{
fwd = new CForward;
} else {
fwd = m_FreeForwards.front();
m_FreeForwards.pop();
}
return fwd;
}
void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused)
{
/* No longer used */
}
/************************************* /*************************************
* ACTUAL FORWARD API IMPLEMENTATION * * ACTUAL FORWARD API IMPLEMENTATION *
*************************************/ *************************************/
CForward::CForward(ExecType et, const char *name, const ParamType *types, unsigned num_params)
: m_numparams(0),
m_varargs(0),
m_ExecType(et),
m_curparam(0),
m_errstate(SP_ERROR_NONE)
{
ke::SafeStrcpy(m_name, sizeof(m_name), name ? name : "");
for (unsigned i = 0; i < num_params; i++)
m_types[i] = types[i];
if (num_params && types[num_params - 1] == Param_VarArgs) {
m_varargs = num_params;
m_numparams = num_params - 1;
} else {
m_varargs = 0;
m_numparams = num_params;
}
}
CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, va_list ap) CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, va_list ap)
{ {
ParamType _types[SP_MAX_EXEC_PARAMS]; ParamType _types[SP_MAX_EXEC_PARAMS];
@ -221,7 +254,30 @@ CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int nu
return NULL; return NULL;
} }
return new CForward(et, name, _types, num_params); CForward *pForward = g_Forwards.ForwardMake();
pForward->m_IterGuard = NULL;
pForward->m_curparam = 0;
pForward->m_ExecType = et;
snprintf(pForward->m_name, FORWARDS_NAME_MAX, "%s", name ? name : "");
for (unsigned int i=0; i<num_params; i++)
{
pForward->m_types[i] = _types[i];
}
if (num_params && _types[num_params-1] == Param_VarArgs)
{
pForward->m_varargs = num_params--;
} else {
pForward->m_varargs = false;
}
pForward->m_numparams = num_params;
pForward->m_errstate = SP_ERROR_NONE;
pForward->m_functions.clear();
return pForward;
} }
int CForward::Execute(cell_t *result, IForwardFilter *filter) int CForward::Execute(cell_t *result, IForwardFilter *filter)
@ -233,6 +289,8 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
return err; return err;
} }
FuncIter iter = m_functions.begin();
IPluginFunction *func;
cell_t cur_result = 0; cell_t cur_result = 0;
cell_t high_result = 0; cell_t high_result = 0;
cell_t low_result = 0; cell_t low_result = 0;
@ -240,38 +298,54 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
unsigned int success=0; unsigned int success=0;
unsigned int num_params = m_curparam; unsigned int num_params = m_curparam;
FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS]; FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS];
FwdParamInfo *param;
ParamType type;
/* Save local, reset */ /* Save local, reset */
memcpy(temp_info, m_params, sizeof(m_params)); memcpy(temp_info, m_params, sizeof(m_params));
m_curparam = 0; m_curparam = 0;
for (FuncIter iter(m_functions); !iter.done(); iter.next()) FuncIteratorGuard guard(&m_IterGuard, &iter);
while (iter != m_functions.end())
{ {
IPluginFunction *func = (*iter); func = (*iter);
if (filter) if (filter)
filter->Preprocess(func, temp_info); filter->Preprocess(func, temp_info);
if (func->GetParentRuntime()->IsPaused())
continue;
for (unsigned int i=0; i<num_params; i++) for (unsigned int i=0; i<num_params; i++)
{ {
int err = SP_ERROR_PARAM; int err = SP_ERROR_PARAM;
FwdParamInfo *param = &temp_info[i]; param = &temp_info[i];
ParamType type;
if (i >= m_numparams || m_types[i] == Param_Any) if (i >= m_numparams || m_types[i] == Param_Any)
{
type = param->pushedas; type = param->pushedas;
}
else else
{
type = m_types[i]; type = m_types[i];
}
if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF)) if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF))
{ {
/* If we're byref or we're vararg, we always push everything by ref. /* If we're byref or we're vararg, we always push everything by ref.
* Even if they're byval, we must push them byref. * Even if they're byval, we must push them byref.
*/ */
err = _ExecutePushRef(func, type, param); if (type == Param_String)
{
err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags);
}
else if (type == Param_Float || type == Param_Cell)
{
err = func->PushCellByRef(&param->val);
}
else
{
err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags);
assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef);
}
} }
else else
{ {
@ -331,6 +405,9 @@ int CForward::Execute(cell_t *result, IForwardFilter *filter)
} }
} }
} }
if (!guard.Triggered())
iter++;
} }
done: done:
@ -370,58 +447,6 @@ done:
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
int CForward::_ExecutePushRef(IPluginFunction *func, ParamType type, FwdParamInfo *param)
{
/* If we're byref or we're vararg, we always push everything by ref.
* Even if they're byval, we must push them byref.
*/
int err;
IPluginRuntime *runtime = func->GetParentRuntime();
switch (type)
{
case Param_String:
// Normal string was pushed.
if (!param->isnull)
return func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags);
// If NULL_STRING was pushed, push the reference to the pubvar of the callee instead.
uint32_t null_string_idx;
err = runtime->FindPubvarByName("NULL_STRING", &null_string_idx);
if (err)
return err;
cell_t null_string;
err = runtime->GetPubvarAddrs(null_string_idx, &null_string, nullptr);
if (err)
return err;
return func->PushCell(null_string);
case Param_Float:
case Param_Cell:
return func->PushCellByRef(&param->val);
default:
assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef);
// No NULL_VECTOR was pushed.
if (type != Param_Array || !param->isnull)
return func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags);
// If NULL_VECTOR was pushed, push the reference to the pubvar of the callee instead.
uint32_t null_vector_idx;
err = runtime->FindPubvarByName("NULL_VECTOR", &null_vector_idx);
if (err)
return err;
cell_t null_vector;
err = runtime->GetPubvarAddrs(null_vector_idx, &null_vector, nullptr);
if (err)
return err;
return func->PushCell(null_vector);
}
}
int CForward::PushCell(cell_t cell) int CForward::PushCell(cell_t cell)
{ {
if (m_curparam < m_numparams) if (m_curparam < m_numparams)
@ -440,7 +465,6 @@ int CForward::PushCell(cell_t cell)
m_params[m_curparam].pushedas = Param_Cell; m_params[m_curparam].pushedas = Param_Cell;
} }
m_params[m_curparam].isnull = false;
m_params[m_curparam++].val = cell; m_params[m_curparam++].val = cell;
return SP_ERROR_NONE; return SP_ERROR_NONE;
@ -464,7 +488,6 @@ int CForward::PushFloat(float number)
m_params[m_curparam].pushedas = Param_Float; m_params[m_curparam].pushedas = Param_Float;
} }
m_params[m_curparam].isnull = false;
m_params[m_curparam++].val = *(cell_t *)&number; m_params[m_curparam++].val = *(cell_t *)&number;
return SP_ERROR_NONE; return SP_ERROR_NONE;
@ -523,22 +546,14 @@ void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags)
m_params[m_curparam].byref.cells = cells; m_params[m_curparam].byref.cells = cells;
m_params[m_curparam].byref.flags = flags; m_params[m_curparam].byref.flags = flags;
m_params[m_curparam].byref.orig_addr = inarray; m_params[m_curparam].byref.orig_addr = inarray;
m_params[m_curparam].isnull = false;
} }
int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags) int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags)
{ {
/* Push a reference to the NULL_VECTOR pubvar if NULL was passed. */ /* We don't allow this here */
if (!inarray) if (!inarray)
{ {
/* Make sure this was intentional. */ return SetError(SP_ERROR_PARAM);
if (cells == 3)
{
return PushNullVector();
} else {
/* We don't allow this here */
return SetError(SP_ERROR_PARAM);
}
} }
if (m_curparam < m_numparams) if (m_curparam < m_numparams)
@ -570,17 +585,10 @@ void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags
m_params[m_curparam].byref.flags = cp_flags; m_params[m_curparam].byref.flags = cp_flags;
m_params[m_curparam].byref.orig_addr = inarray; m_params[m_curparam].byref.orig_addr = inarray;
m_params[m_curparam].byref.sz_flags = sz_flags; m_params[m_curparam].byref.sz_flags = sz_flags;
m_params[m_curparam].isnull = false;
} }
int CForward::PushString(const char *string) int CForward::PushString(const char *string)
{ {
/* Push a reference to the NULL_STRING pubvar if NULL was passed. */
if (!string)
{
return PushNullString();
}
if (m_curparam < m_numparams) if (m_curparam < m_numparams)
{ {
if (m_types[m_curparam] == Param_Any) if (m_types[m_curparam] == Param_Any)
@ -627,52 +635,6 @@ int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_fla
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
int CForward::PushNullString()
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_String;
} else if (m_types[m_curparam] != Param_String) {
return SetError(SP_ERROR_PARAM);
}
} else {
if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_String;
}
m_params[m_curparam++].isnull = true;
return SP_ERROR_NONE;
}
int CForward::PushNullVector()
{
if (m_curparam < m_numparams)
{
if (m_types[m_curparam] == Param_Any)
{
m_params[m_curparam].pushedas = Param_Array;
} else if (m_types[m_curparam] != Param_Array) {
return SetError(SP_ERROR_PARAM);
}
} else {
if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS)
{
return SetError(SP_ERROR_PARAMS_MAX);
}
m_params[m_curparam].pushedas = Param_Array;
}
m_params[m_curparam++].isnull = true;
return SP_ERROR_NONE;
}
void CForward::Cancel() void CForward::Cancel()
{ {
if (!m_curparam) if (!m_curparam)
@ -710,69 +672,75 @@ bool CForward::RemoveFunction(IPluginContext *pContext, funcid_t index)
bool CForward::RemoveFunction(IPluginFunction *func) bool CForward::RemoveFunction(IPluginFunction *func)
{ {
ReentrantList<IPluginFunction *> *lst;
if (func->IsRunnable())
lst = &m_functions;
else
lst = &m_paused;
bool found = false; bool found = false;
for (FuncIter iter(lst); !iter.done(); iter.next()) { FuncIter iter;
if (*iter == func) { List<IPluginFunction *> *lst;
iter.remove();
if (func->IsRunnable())
{
lst = &m_functions;
} else {
lst = &m_paused;
}
for (iter=m_functions.begin(); iter!=m_functions.end(); iter++)
{
if ((*iter) == func)
{
m_IterGuard->FixIteratorChain(iter);
found = true; found = true;
lst->erase(iter);
break; break;
} }
} }
/* Cancel a call, if any */ /* Cancel a call, if any */
if (found || m_curparam) if (found || m_curparam)
{
func->Cancel(); func->Cancel();
}
return found; return found;
} }
unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin) unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin)
{ {
FuncIter iter;
IPluginFunction *func;
unsigned int removed = 0; unsigned int removed = 0;
for (FuncIter iter(m_functions); !iter.done(); iter.next()) { IPluginContext *pContext = plugin->GetBaseContext();
IPluginFunction *func = *iter; for (iter=m_functions.begin(); iter!=m_functions.end();)
if (func->GetParentContext() == plugin->GetBaseContext()) { {
iter.remove(); func = (*iter);
if (func->GetParentContext() == pContext)
{
iter = m_functions.erase(iter);
removed++; removed++;
} else {
iter++;
} }
} }
return removed; return removed;
} }
bool CForward::AddFunction(IPluginFunction *func) bool CForward::AddFunction(IPluginFunction *func)
{ {
if (m_curparam) if (m_curparam)
{
return false; return false;
}
if (func->IsRunnable()) if (func->IsRunnable())
m_functions.append(func); {
else m_functions.push_back(func);
m_paused.append(func); } else {
m_paused.push_back(func);
}
return true; return true;
} }
bool CForward::IsFunctionRegistered(IPluginFunction *func)
{
ReentrantList<IPluginFunction *> *lst;
if (func->IsRunnable())
lst = &m_functions;
else
lst = &m_paused;
for (FuncIter iter(lst); !iter.done(); iter.next()) {
if ((*iter) == func)
return true;
}
return false;
}
const char *CForward::GetForwardName() const char *CForward::GetForwardName()
{ {
return m_name; return m_name;
@ -780,7 +748,7 @@ const char *CForward::GetForwardName()
unsigned int CForward::GetFunctionCount() unsigned int CForward::GetFunctionCount()
{ {
return m_functions.length(); return m_functions.size();
} }
ExecType CForward::GetExecType() ExecType CForward::GetExecType()

211
core/ForwardSys.h Normal file
View File

@ -0,0 +1,211 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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_SOURCEMOD_FORWARDSYSTEM_H_
#define _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_
#include <IForwardSys.h>
#include <IPluginSys.h>
#include "sm_globals.h"
#include <sh_list.h>
#include <sh_stack.h>
#include "sourcemod.h"
using namespace SourceHook;
typedef List<IPluginFunction *>::iterator FuncIter;
/* :TODO: a global name max define for sourcepawn, should mirror compiler's sNAMEMAX */
#define FORWARDS_NAME_MAX 64
struct ByrefInfo
{
unsigned int cells;
cell_t *orig_addr;
int flags;
int sz_flags;
};
struct FwdParamInfo
{
cell_t val;
ByrefInfo byref;
ParamType pushedas;
};
class SourceMod::IForwardFilter
{
public:
virtual void Preprocess(IPluginFunction *fun, FwdParamInfo *params)
{
}
};
class FuncIteratorGuard
{
bool triggered;
FuncIteratorGuard **pprev;
FuncIter *iter;
FuncIteratorGuard *next;
public:
FuncIteratorGuard(FuncIteratorGuard **pprev, FuncIter *iter)
: triggered(false), pprev(pprev), iter(iter), next(*pprev)
{
*pprev = this;
}
~FuncIteratorGuard()
{
*pprev = next;
}
inline bool Triggered()
{
bool t = triggered;
triggered = false;
return t;
}
/**
* This should not read from |this| before the NULL check, because FwdSys
* can call (NULL)->FixIteratorChain().
*/
void FixIteratorChain(FuncIter &other)
{
FuncIteratorGuard *guard = this;
while (guard != NULL)
{
if (*guard->iter == other)
{
*(guard->iter) = ++(*(guard->iter));
guard->triggered = true;
}
guard = guard->next;
}
}
};
class CForward : public IChangeableForward
{
public: //ICallable
virtual int PushCell(cell_t cell);
virtual int PushCellByRef(cell_t *cell, int flags);
virtual int PushFloat(float number);
virtual int PushFloatByRef(float *number, int flags);
virtual int PushArray(cell_t *inarray, unsigned int cells, int flags);
virtual int PushString(const char *string);
virtual int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
virtual void Cancel();
public: //IForward
virtual const char *GetForwardName();
virtual unsigned int GetFunctionCount();
virtual ExecType GetExecType();
virtual int Execute(cell_t *result, IForwardFilter *filter);
public: //IChangeableForward
virtual bool RemoveFunction(IPluginFunction *func);
virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin);
virtual bool AddFunction(IPluginFunction *func);
virtual bool AddFunction(IPluginContext *ctx, funcid_t index);
virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index);
public:
static CForward *CreateForward(const char *name,
ExecType et,
unsigned int num_params,
const ParamType *types,
va_list ap);
private:
void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags);
void _Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags);
inline int SetError(int err)
{
m_errstate = err;
return err;
}
protected:
/* :TODO: I want a caching list type here.
* Destroying these things and using new/delete for their members feels bad.
*/
mutable List<IPluginFunction *> m_functions;
mutable List<IPluginFunction *> m_paused;
FuncIteratorGuard *m_IterGuard;
/* Type and name information */
FwdParamInfo m_params[SP_MAX_EXEC_PARAMS];
ParamType m_types[SP_MAX_EXEC_PARAMS];
char m_name[FORWARDS_NAME_MAX+1];
unsigned int m_numparams;
unsigned int m_varargs;
ExecType m_ExecType;
/* State information */
unsigned int m_curparam;
int m_errstate;
};
class CForwardManager :
public IForwardManager,
public IPluginsListener,
public SMGlobalClass
{
friend class CForward;
public:
~CForwardManager();
public: //IForwardManager
IForward *CreateForward(const char *name,
ExecType et,
unsigned int num_params,
const ParamType *types,
...);
IChangeableForward *CreateForwardEx(const char *name,
ExecType et,
int num_params,
const ParamType *types,
...);
IForward *FindForward(const char *name, IChangeableForward **ifchng);
void ReleaseForward(IForward *forward);
public: //IPluginsListener
void OnPluginLoaded(IPlugin *plugin);
void OnPluginUnloaded(IPlugin *plugin);
void OnPluginPauseChange(IPlugin *plugin, bool paused);
public: //SMGlobalClass
void OnSourceModAllInitialized();
protected:
CForward *ForwardMake();
void ForwardFree(CForward *fwd);
private:
CStack<CForward *> m_FreeForwards;
List<CForward *> m_managed;
List<CForward *> m_unmanaged;
};
extern CForwardManager g_Forwards;
#endif //_INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_

View File

@ -1,8 +1,8 @@
/** /**
* vim: set ts=4 : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -25,27 +25,31 @@
* this exception to all derivative works. AlliedModders LLC defines further * this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>. * or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "GameConfigs.h" #include "GameConfigs.h"
//#include "sm_stringutil.h" #include "sm_stringutil.h"
//#include "sourcemod.h" #include "sourcemod.h"
//#include "sourcemm_api.h" #include "sourcemm_api.h"
//#include "HalfLife2.h" #include "HalfLife2.h"
//#include "Logger.h" #include "Logger.h"
//#include "ShareSys.h" #include "ShareSys.h"
//#include "MemoryUtils.h" #include "MemoryUtils.h"
//#include "LibrarySys.h" #include "LibrarySys.h"
//#include "HandleSys.h" #include "HandleSys.h"
//#include "sm_crc32.h" #include "sm_crc32.h"
//#if defined PLATFORM_LINUX #if defined PLATFORM_LINUX
//#include <dlfcn.h> #include <dlfcn.h>
//#endif #endif
GameConfigManager g_GameConfigs;
IGameConfig *g_pGameConf = NULL;
static char g_Game[256];
static char g_GameDesc[256] = {'!', '\0'};
static char g_GameName[256] = {'$', '\0'};
#define PSTATE_NONE 0 #define PSTATE_NONE 0
#define PSTATE_GAMES 1 #define PSTATE_GAMES 1
@ -60,42 +64,76 @@
#define PSTATE_GAMEDEFS_CRC_BINARY 10 #define PSTATE_GAMEDEFS_CRC_BINARY 10
#define PSTATE_GAMEDEFS_CUSTOM 11 #define PSTATE_GAMEDEFS_CUSTOM 11
#define WIN 0 #if defined PLATFORM_WINDOWS
#define LIN 1 #define PLATFORM_NAME "windows"
#define PLATFORM_SERVER_BINARY "server.dll"
#elif defined PLATFORM_LINUX
#define PLATFORM_NAME "linux" #define PLATFORM_NAME "linux"
#define PLATFORM_SERVER_BINARY "server_i486.so" #define PLATFORM_SERVER_BINARY "server_i486.so"
#elif defined PLATFORM_APPLE
#define PLATFORM_NAME "undef"
#define PLATFORM_SERVER_BINARY "undef.dylib"
#endif
Offset *tempOffset; struct TempSigInfo
Sig *tempSig; {
void Reset()
{
library[0] = '\0';
sig[0] = '\0';
}
char sig[512];
char library[64];
} s_TempSig;
unsigned int s_ServerBinCRC; unsigned int s_ServerBinCRC;
bool s_ServerBinCRC_Ok = false; bool s_ServerBinCRC_Ok = false;
bool /*CGameConfig::*/DoesGameMatch(const char *value) static bool DoesGameMatch(const char *value)
{ {
if (strcmp(value, /*m_gdcG*/game) == 0) #if defined PLATFORM_WINDOWS
if (strcasecmp(value, g_Game) == 0 ||
#else
if (strcmp(value, g_Game) == 0 ||
#endif
strcmp(value, g_GameDesc) == 0 ||
strcmp(value, g_GameName) == 0)
{ {
return true; return true;
} }
return false; return false;
} }
bool /*CGameConfig::*/DoesEngineMatch(const char *value) static bool DoesEngineMatch(const char *value)
{ {
if (strcmp(value, /*m_gdcE*/engine) == 0) #if SOURCE_ENGINE == SE_EPISODEONE
if (strcmp(value, "original") == 0)
#elif SOURCE_ENGINE == SE_DARKMESSIAH
if (strcmp(value, "darkmessiah") == 0)
#elif SOURCE_ENGINE == SE_ORANGEBOX
if (strcmp(value, "orangebox") == 0)
#elif SOURCE_ENGINE == SE_ORANGEBOXVALVE
if (strcmp(value, "orangebox_valve") == 0)
#elif SOURCE_ENGINE == SE_LEFT4DEAD
if (strcmp(value, "left4dead") == 0)
#elif SOURCE_ENGINE == SE_LEFT4DEAD2
if (strcmp(value, "left4dead2") == 0)
#else
#error "Unknown engine type"
#endif
{ {
return true; return true;
} }
return false; return false;
} }
CGameConfig::CGameConfig()//const char *file)//, const char *game, const char *engine) CGameConfig::CGameConfig(const char *file)
{ {
// strncopy(m_File, file, sizeof(m_File)); strncopy(m_File, file, sizeof(m_File));
// strncopy(m_gdcGame, game, sizeof(m_gdcGame)); m_pOffsets = sm_trie_create();
// strncopy(m_gdcEngine, engine, sizeof(m_gdcEngine)); m_pProps = sm_trie_create();
//m_pStrings = new BaseStringTable(512); m_pKeys = sm_trie_create();
m_pSigs = sm_trie_create();
m_pStrings = new BaseStringTable(512);
m_RefCount = 0; m_RefCount = 0;
m_CustomLevel = 0; m_CustomLevel = 0;
@ -104,7 +142,11 @@ CGameConfig::CGameConfig()//const char *file)//, const char *game, const char *e
CGameConfig::~CGameConfig() CGameConfig::~CGameConfig()
{ {
// delete m_pStrings; sm_trie_destroy(m_pOffsets);
sm_trie_destroy(m_pProps);
sm_trie_destroy(m_pKeys);
sm_trie_destroy(m_pSigs);
delete m_pStrings;
} }
SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name) SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
@ -167,14 +209,11 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
} }
else if (strcmp(name, "CRC") == 0) else if (strcmp(name, "CRC") == 0)
{ {
#if 0
m_ParseState = PSTATE_GAMEDEFS_CRC; m_ParseState = PSTATE_GAMEDEFS_CRC;
bShouldBeReadingDefault = false; bShouldBeReadingDefault = false;
#endif
} }
else else
{ {
#if 0
ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name); ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name);
if (pListen != NULL) if (pListen != NULL)
@ -185,7 +224,8 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
m_CustomHandler->ReadSMC_ParseStart(); m_CustomHandler->ReadSMC_ParseStart();
break; break;
} }
#endif
m_IgnoreLevel++;
} }
break; break;
} }
@ -193,19 +233,17 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
{ {
m_Prop[0] = '\0'; m_Prop[0] = '\0';
m_Class[0] = '\0'; m_Class[0] = '\0';
tempOffset = new Offset(); strncopy(m_offset, name, sizeof(m_offset));
tempOffset->setName(name);
m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET; m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
break; break;
} }
case PSTATE_GAMEDEFS_SIGNATURES: case PSTATE_GAMEDEFS_SIGNATURES:
{ {
tempSig = new Sig(); strncopy(m_offset, name, sizeof(m_offset));
tempSig->setName(name); s_TempSig.Reset();
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG; m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
break; break;
} }
#if 0
case PSTATE_GAMEDEFS_CRC: case PSTATE_GAMEDEFS_CRC:
{ {
char error[255]; char error[255];
@ -255,7 +293,6 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
return m_CustomHandler->ReadSMC_NewSection(states, name); return m_CustomHandler->ReadSMC_NewSection(states, name);
break; break;
} }
#endif
/* No sub-sections allowed: /* No sub-sections allowed:
case PSTATE_GAMEDEFS_OFFSETS_OFFSET: case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
case PSTATE_GAMEDEFS_KEYS: case PSTATE_GAMEDEFS_KEYS:
@ -288,17 +325,13 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
strncopy(m_Class, value, sizeof(m_Class)); strncopy(m_Class, value, sizeof(m_Class));
} else if (strcmp(key, "prop") == 0) { } else if (strcmp(key, "prop") == 0) {
strncopy(m_Prop, value, sizeof(m_Prop)); strncopy(m_Prop, value, sizeof(m_Prop));
} else /*if (strcmp(key, PLATFORM_NAME) == 0) */ { } else if (strcmp(key, PLATFORM_NAME) == 0) {
int val = atoi(value); int val = atoi(value);
// sm_trie_replace(m_pOffsets, m_offset, (void *)val); sm_trie_replace(m_pOffsets, m_offset, (void *)val);
if (strcmp(key, "windows") == 0) tempOffset->setWin(val);
else if (strcmp(key, "linux") == 0) tempOffset->setLin(val);
} }
} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) { } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
m_Keys[key] = value; int id = m_pStrings->AddString(value);
// printf("Inserting %s - %s\n", key, value); sm_trie_replace(m_pKeys, key, (void *)id);
// int id = m_pStrings->AddString(value);
// sm_trie_replace(m_pKeys, key, (void *)id);
} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) { } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
if (strcmp(key, "game") == 0) if (strcmp(key, "game") == 0)
{ {
@ -325,9 +358,12 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
} }
} }
} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) { } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
if (strcmp(key, "windows") == 0) tempSig->setWin(value); if (strcmp(key, PLATFORM_NAME) == 0)
else if (strcmp(key, "linux") == 0) tempSig->setLin(value); {
else if (strcmp(key, "library") == 0) tempSig->setLib(value); strncopy(s_TempSig.sig, value, sizeof(s_TempSig.sig));
} else if (strcmp(key, "library") == 0) {
strncopy(s_TempSig.library, value, sizeof(s_TempSig.library));
}
} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) { } else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
if (strcmp(key, PLATFORM_NAME) == 0 if (strcmp(key, PLATFORM_NAME) == 0
&& s_ServerBinCRC_Ok && s_ServerBinCRC_Ok
@ -388,8 +424,6 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
} }
case PSTATE_GAMEDEFS_OFFSETS_OFFSET: case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
{ {
m_Offsets.push_back(*tempOffset);
#if 0
/* Parse the offset... */ /* Parse the offset... */
if (m_Class[0] != '\0' if (m_Class[0] != '\0'
&& m_Prop[0] != '\0') && m_Prop[0] != '\0')
@ -413,7 +447,6 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
} }
} }
} }
#endif
m_ParseState = PSTATE_GAMEDEFS_OFFSETS; m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
break; break;
} }
@ -442,8 +475,6 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
} }
case PSTATE_GAMEDEFS_SIGNATURES_SIG: case PSTATE_GAMEDEFS_SIGNATURES_SIG:
{ {
m_Sigs.push_back(*tempSig);
#if 0
if (s_TempSig.library[0] == '\0') if (s_TempSig.library[0] == '\0')
{ {
/* assume server */ /* assume server */
@ -473,7 +504,11 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
void *handle = dlopen(info.dli_fname, RTLD_NOW); void *handle = dlopen(info.dli_fname, RTLD_NOW);
if (handle) if (handle)
{ {
#if (SOURCE_ENGINE == SE_ORANGEBOXVALVE) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
final_addr = g_MemUtils.ResolveSymbol(handle, &s_TempSig.sig[1]);
#else
final_addr = dlsym(handle, &s_TempSig.sig[1]); final_addr = dlsym(handle, &s_TempSig.sig[1]);
#endif
dlclose(handle); dlclose(handle);
} else { } else {
g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")", g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
@ -492,45 +527,18 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
} }
#endif #endif
/* First, preprocess the signature */ /* First, preprocess the signature */
char real_sig[511]; unsigned char real_sig[511];
size_t real_bytes; size_t real_bytes;
size_t length; size_t length;
real_bytes = 0; real_bytes = 0;
length = strlen(s_TempSig.sig); length = strlen(s_TempSig.sig);
for (size_t i=0; i<length; i++) real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), s_TempSig.sig);
{
if (real_bytes >= sizeof(real_sig))
{
break;
}
real_sig[real_bytes++] = s_TempSig.sig[i];
if (s_TempSig.sig[i] == '\\'
&& s_TempSig.sig[i+1] == 'x')
{
if (i + 3 >= length)
{
continue;
}
/* Get the hex part */
char s_byte[3];
int r_byte;
s_byte[0] = s_TempSig.sig[i+2];
s_byte[1] = s_TempSig.sig[i+3];
s_byte[2] = '\0';
/* Read it as an integer */
sscanf(s_byte, "%x", &r_byte);
/* Save the value */
real_sig[real_bytes-1] = r_byte;
/* Adjust index */
i += 3;
}
}
if (real_bytes >= 1) if (real_bytes >= 1)
{ {
final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes); final_addr = g_MemUtils.FindPattern(addrInBase, (char*)real_sig, real_bytes);
} }
} }
@ -538,7 +546,6 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
skip_find: skip_find:
#endif #endif
sm_trie_replace(m_pSigs, m_offset, final_addr); sm_trie_replace(m_pSigs, m_offset, final_addr);
#endif
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES; m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
break; break;
@ -663,7 +670,7 @@ public:
return SMCResult_Continue; return SMCResult_Continue;
} }
public: public:
list<char*> *fileList; List<String> *fileList;
unsigned int state; unsigned int state;
unsigned int ignoreLevel; unsigned int ignoreLevel;
char cur_file[PLATFORM_MAX_PATH]; char cur_file[PLATFORM_MAX_PATH];
@ -678,21 +685,26 @@ static MasterReader master_reader;
bool CGameConfig::Reparse(char *error, size_t maxlength) bool CGameConfig::Reparse(char *error, size_t maxlength)
{ {
/* Reset cached data */ /* Reset cached data */
// m_pStrings->Reset(); m_pStrings->Reset();
m_Offsets.clear(); sm_trie_clear(m_pOffsets);
m_Sigs.clear(); sm_trie_clear(m_pProps);
sm_trie_clear(m_pKeys);
char path[PLATFORM_MAX_PATH]; char path[PLATFORM_MAX_PATH];
/* See if we can use the extended gamedata format. */ /* See if we can use the extended gamedata format. */
//TODO pass in path to gamedata somehow g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
// g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File); if (!g_LibSys.PathExists(path))
{
/* Nope, use the old mechanism. */
UTIL_Format(path, sizeof(path), "%s.txt", m_File);
return EnterFile(path, error, maxlength);
}
/* Otherwise, it's time to parse the master. */ /* Otherwise, it's time to parse the master. */
SMCError err; SMCError err;
SMCStates state = {0, 0}; SMCStates state = {0, 0};
list<char*> fileList; List<String> fileList;
master_reader.fileList = &fileList; master_reader.fileList = &fileList;
err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength); err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
@ -700,26 +712,64 @@ bool CGameConfig::Reparse(char *error, size_t maxlength)
{ {
const char *msg = textparsers->GetSMCErrorString(err); const char *msg = textparsers->GetSMCErrorString(err);
printf("[SM] Error parsing master gameconf file \"%s\":", path); g_Logger.LogError("[SM] Error parsing master gameconf file \"%s\":", path);
printf("[SM] Error %d on line %d, col %d: %s", g_Logger.LogError("[SM] Error %d on line %d, col %d: %s",
err, err,
state.line, state.line,
state.col, state.col,
msg ? msg : "Unknown error"); msg ? msg : "Unknown error");
exit(PARSE_ERROR); return false;
} }
/* Go through each file we found and parse it. */ /* Go through each file we found and parse it. */
list<char*>::iterator iter; List<String>::iterator iter;
for (iter = fileList.begin(); iter != fileList.end(); iter++) for (iter = fileList.begin(); iter != fileList.end(); iter++)
{ {
UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter); UTIL_Format(path, sizeof(path), "%s/%s", m_File, (*iter).c_str());
if (!EnterFile(path, error, maxlength)) if (!EnterFile(path, error, maxlength))
{ {
return false; return false;
} }
} }
/* Parse the contents of the 'custom' directory */
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/custom", m_File);
IDirectory *customDir = g_LibSys.OpenDirectory(path);
if (!customDir)
{
return true;
}
while (customDir->MoreFiles())
{
if (!customDir->IsEntryFile())
{
customDir->NextEntry();
continue;
}
const char *curFile = customDir->GetEntryName();
/* Only allow .txt files */
int len = strlen(curFile);
if (len > 4 && strcmp(&curFile[len-4], ".txt") != 0)
{
customDir->NextEntry();
continue;
}
UTIL_Format(path, sizeof(path), "%s/custom/%s", m_File, curFile);
if (!EnterFile(path, error, maxlength))
{
g_LibSys.CloseDirectory(customDir);
return false;
}
customDir->NextEntry();
}
g_LibSys.CloseDirectory(customDir);
return true; return true;
} }
@ -728,20 +778,22 @@ bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
SMCError err; SMCError err;
SMCStates state = {0, 0}; SMCStates state = {0, 0};
g_SourceMod.BuildPath(Path_SM, m_CurFile, sizeof(m_CurFile), "gamedata/%s", file);
/* Initialize parse states */ /* Initialize parse states */
m_IgnoreLevel = 0; m_IgnoreLevel = 0;
bShouldBeReadingDefault = true; bShouldBeReadingDefault = true;
m_ParseState = PSTATE_NONE; m_ParseState = PSTATE_NONE;
if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength)) if ((err=textparsers->ParseSMCFile(m_CurFile, this, &state, error, maxlength))
!= SMCError_Okay) != SMCError_Okay)
{ {
const char *msg; const char *msg;
msg = textparsers->GetSMCErrorString(err); msg = textparsers->GetSMCErrorString(err);
printf("[SM] Error parsing gameconfig file \"%s\":\n", file); g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", m_CurFile);
printf("[SM] Error %d on line %d, col %d: %s\n", g_Logger.LogError("[SM] Error %d on line %d, col %d: %s",
err, err,
state.line, state.line,
state.col, state.col,
@ -755,13 +807,12 @@ bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
m_CustomLevel = 0; m_CustomLevel = 0;
} }
exit(PARSE_ERROR); return false;
} }
return true; return true;
} }
#if 0
bool CGameConfig::GetOffset(const char *key, int *value) bool CGameConfig::GetOffset(const char *key, int *value)
{ {
void *obj; void *obj;
@ -813,16 +864,149 @@ unsigned int CGameConfig::DecRefCount()
m_RefCount--; m_RefCount--;
return m_RefCount; return m_RefCount;
} }
#endif
const char *CGameConfig::GetKeyValue(const char *key) GameConfigManager::GameConfigManager()
{ {
map<const char*,const char*,cmp_str>::iterator it = m_Keys.find(key); m_pLookup = sm_trie_create();
if (it == m_Keys.end()) return NULL;
return it->second;
} }
list<Offset> CGameConfig::GetOffsets() { return m_Offsets; } GameConfigManager::~GameConfigManager()
list<Sig> CGameConfig::GetSigs() { return m_Sigs; } {
sm_trie_destroy(m_pLookup);
}
void GameConfigManager::OnSourceModStartup(bool late)
{
LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
strncopy(g_Game, g_SourceMod.GetGameFolderName(), sizeof(g_Game));
strncopy(g_GameDesc + 1, SERVER_CALL(GetGameDescription)(), sizeof(g_GameDesc) - 1);
KeyValues *pGameInfo = new KeyValues("GameInfo");
if (g_HL2.KVLoadFromFile(pGameInfo, basefilesystem, "gameinfo.txt"))
{
const char *str;
if ((str = pGameInfo->GetString("game", NULL)) != NULL)
{
strncopy(g_GameName + 1, str, sizeof(g_GameName) - 1);
}
}
pGameInfo->deleteThis();
}
void GameConfigManager::OnSourceModAllInitialized()
{
/* NOW initialize the game file */
CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
char error[255];
if (!pGameConf->Reparse(error, sizeof(error)))
{
/* :TODO: log */
}
g_ShareSys.AddInterface(NULL, this);
}
void GameConfigManager::OnSourceModAllShutdown()
{
CloseGameConfigFile(g_pGameConf);
}
bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
{
#if 0
/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
if (g_blockGameDataLoad)
{
UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
return false;
}
#endif
CGameConfig *pConfig;
if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
{
pConfig->IncRefCount();
*_pConfig = pConfig;
return true;
}
pConfig = new CGameConfig(file);
/* :HACKHACK: Don't parse the main config file */
bool retval = true;
if (_pConfig != &g_pGameConf)
{
retval = pConfig->Reparse(error, maxlength);
}
m_cfgs.push_back(pConfig);
sm_trie_insert(m_pLookup, file, pConfig);
pConfig->IncRefCount();
*_pConfig = pConfig;
return retval;
}
void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
{
CGameConfig *pConfig = (CGameConfig *)cfg;
if (pConfig->DecRefCount() == 0)
{
sm_trie_delete(m_pLookup, pConfig->m_File);
delete pConfig;
}
}
extern HandleType_t g_GameConfigsType;
IGameConfig *GameConfigManager::ReadHandle(Handle_t hndl, IdentityToken_t *ident, HandleError *err)
{
HandleSecurity sec(ident, g_pCoreIdent);
IGameConfig *conf = NULL;
HandleError _err = g_HandleSys.ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&conf);
if (err)
{
*err = _err;
}
return conf;
}
void GameConfigManager::AddUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
{
m_customHandlers.insert(sectionname, listener);
}
void GameConfigManager::RemoveUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
{
ITextListener_SMC **pListener = m_customHandlers.retrieve(sectionname);
if (pListener == NULL)
{
return;
}
if (*pListener != listener)
{
return;
}
m_customHandlers.remove(sectionname);
return;
}
void GameConfigManager::AcquireLock()
{
}
void GameConfigManager::ReleaseLock()
{
}

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet: * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -32,31 +32,30 @@
#ifndef _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_ #ifndef _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_
#define _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_ #define _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_
#include "common_logic.h"
#include <IGameConfigs.h> #include <IGameConfigs.h>
#include <ITextParsers.h> #include <ITextParsers.h>
#include <am-refcounting.h> #include <sh_list.h>
#include <sm_stringhashmap.h> #include "sm_trie.h"
#include <sm_namehashset.h> #include "sm_globals.h"
#include "sm_memtable.h"
#include "sm_trie_tpl.h"
using namespace SourceMod; using namespace SourceMod;
using namespace SourceHook;
class SendProp; class SendProp;
class CGameConfig : class CGameConfig :
public ITextListener_SMC, public ITextListener_SMC,
public IGameConfig, public IGameConfig
public ke::Refcounted<CGameConfig>
{ {
friend class GameConfigManager; friend class GameConfigManager;
public: public:
CGameConfig(const char *file, const char *engine = NULL); CGameConfig(const char *file);
~CGameConfig(); ~CGameConfig();
public: public:
bool Reparse(char *error, size_t maxlength); bool Reparse(char *error, size_t maxlength);
bool EnterFile(const char *file, char *error, size_t maxlength); bool EnterFile(const char *file, char *error, size_t maxlength);
void SetBaseEngine(const char *engine);
void SetParseEngine(const char *engine);
public: //ITextListener_SMC public: //ITextListener_SMC
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name); SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
@ -66,23 +65,18 @@ public: //IGameConfig
bool GetOffset(const char *key, int *value); bool GetOffset(const char *key, int *value);
SendProp *GetSendProp(const char *key); SendProp *GetSendProp(const char *key);
bool GetMemSig(const char *key, void **addr); bool GetMemSig(const char *key, void **addr);
bool GetAddress(const char *key, void **addr); public:
public: //NameHashSet void IncRefCount();
static inline bool matches(const char *key, const CGameConfig *value) unsigned int DecRefCount();
{
return strcmp(key, value->m_File) == 0;
}
static inline uint32_t hash(const detail::CharsAndLength &key)
{
return key.hash();
}
private: private:
BaseStringTable *m_pStrings;
char m_File[PLATFORM_MAX_PATH]; char m_File[PLATFORM_MAX_PATH];
char m_CurFile[PLATFORM_MAX_PATH]; char m_CurFile[PLATFORM_MAX_PATH];
StringHashMap<int> m_Offsets; Trie *m_pOffsets;
StringHashMap<SendProp *> m_Props; Trie *m_pProps;
StringHashMap<ke::AString> m_Keys; Trie *m_pKeys;
StringHashMap<void *> m_Sigs; Trie *m_pSigs;
unsigned int m_RefCount;
/* Parse states */ /* Parse states */
int m_ParseState; int m_ParseState;
unsigned int m_IgnoreLevel; unsigned int m_IgnoreLevel;
@ -90,39 +84,15 @@ private:
char m_Prop[64]; char m_Prop[64];
char m_offset[64]; char m_offset[64];
char m_Game[256]; char m_Game[256];
char m_Key[64];
bool bShouldBeReadingDefault; bool bShouldBeReadingDefault;
bool had_game; bool had_game;
bool matched_game; bool matched_game;
bool had_engine; bool had_engine;
bool matched_engine; bool matched_engine;
bool matched_platform;
/* Custom Sections */ /* Custom Sections */
unsigned int m_CustomLevel; unsigned int m_CustomLevel;
ITextListener_SMC *m_CustomHandler; ITextListener_SMC *m_CustomHandler;
/* Support for reading Addresses */
struct AddressConf
{
char signatureName[64];
int readCount;
int read[8];
bool lastIsOffset;
AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read, bool lastIsOffset);
AddressConf() {}
};
char m_Address[64];
char m_AddressSignature[64];
int m_AddressReadCount;
int m_AddressRead[8];
bool m_AddressLastIsOffset;
StringHashMap<AddressConf> m_Addresses;
const char *m_pEngine;
const char *m_pBaseEngine;
}; };
class GameConfigManager : class GameConfigManager :
@ -146,12 +116,11 @@ public: //SMGlobalClass
void OnSourceModStartup(bool late); void OnSourceModStartup(bool late);
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
void OnSourceModAllShutdown(); void OnSourceModAllShutdown();
public:
void RemoveCachedConfig(CGameConfig *config);
private: private:
NameHashSet<CGameConfig *> m_Lookup; List<CGameConfig *> m_cfgs;
Trie *m_pLookup;
public: public:
StringHashMap<ITextListener_SMC *> m_customHandlers; KTrie<ITextListener_SMC *> m_customHandlers;
}; };
extern GameConfigManager g_GameConfigs; extern GameConfigManager g_GameConfigs;

View File

@ -1,171 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet :
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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 "GameHooks.h"
#include "sourcemod.h"
#include "ConVarManager.h"
#include "command_args.h"
#include "provider.h"
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
#else
SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallback, SH_NOATTRIB, false, ConVar *, const char *);
#endif
#if SOURCE_ENGINE != SE_DARKMESSIAH
SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
#endif
#if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
#else
SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
#endif
SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
GameHooks::GameHooks()
: client_cvar_query_mode_(ClientCvarQueryMode::Unavailable),
last_command_client_(-1)
{
}
void GameHooks::Start()
{
// Hook ICvar::CallGlobalChangeCallbacks.
#if SOURCE_ENGINE >= SE_ORANGEBOX
hooks_ += SH_ADD_HOOK(ICvar, CallGlobalChangeCallbacks, icvar, SH_STATIC(OnConVarChanged), false);
#else
hooks_ += SH_ADD_HOOK(ICvar, CallGlobalChangeCallback, icvar, SH_STATIC(OnConVarChanged), false);
#endif
// Episode 2 has this function by default, but the older versions do not.
#if SOURCE_ENGINE == SE_EPISODEONE
if (g_SMAPI->GetGameDLLVersion() >= 6) {
hooks_ += SH_ADD_HOOK(IServerGameDLL, OnQueryCvarValueFinished, gamedll, SH_MEMBER(this, &GameHooks::OnQueryCvarValueFinished), false);
client_cvar_query_mode_ = ClientCvarQueryMode::DLL;
}
#endif
hooks_ += SH_ADD_HOOK(IServerGameClients, SetCommandClient, serverClients, SH_MEMBER(this, &GameHooks::SetCommandClient), false);
}
void GameHooks::OnVSPReceived()
{
if (client_cvar_query_mode_ != ClientCvarQueryMode::Unavailable)
return;
if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
return;
#if SOURCE_ENGINE != SE_DARKMESSIAH
hooks_ += SH_ADD_HOOK(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, SH_MEMBER(this, &GameHooks::OnQueryCvarValueFinished), false);
client_cvar_query_mode_ = ClientCvarQueryMode::VSP;
#endif
}
void GameHooks::Shutdown()
{
for (size_t i = 0; i < hooks_.length(); i++)
SH_REMOVE_HOOK_ID(hooks_[i]);
hooks_.clear();
client_cvar_query_mode_ = ClientCvarQueryMode::Unavailable;
}
#if SOURCE_ENGINE >= SE_ORANGEBOX
void GameHooks::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
#else
void GameHooks::OnConVarChanged(ConVar *pConVar, const char *oldValue)
#endif
{
#if SOURCE_ENGINE < SE_ORANGEBOX
float flOldValue = atof(oldValue);
#endif
g_ConVarManager.OnConVarChanged(pConVar, oldValue, flOldValue);
}
#if SOURCE_ENGINE != SE_DARKMESSIAH
void GameHooks::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result,
const char *cvarName, const char *cvarValue){
int client = IndexOfEdict(pPlayer);
# if SOURCE_ENGINE == SE_CSGO
if (g_Players.HandleConVarQuery(cookie, client, result, cvarName, cvarValue))
return;
# endif
g_ConVarManager.OnClientQueryFinished(cookie, client, result, cvarName, cvarValue);
}
#endif
ke::RefPtr<CommandHook>
GameHooks::AddCommandHook(ConCommand *cmd, const CommandHook::Callback &callback)
{
return new CommandHook(cmd, callback, false);
}
ke::RefPtr<CommandHook>
GameHooks::AddPostCommandHook(ConCommand *cmd, const CommandHook::Callback &callback)
{
return new CommandHook(cmd, callback, true);
}
void GameHooks::SetCommandClient(int client)
{
last_command_client_ = client + 1;
}
CommandHook::CommandHook(ConCommand *cmd, const Callback &callback, bool post)
: hook_id_(0),
callback_(callback)
{
hook_id_ = SH_ADD_HOOK(ConCommand, Dispatch, cmd, SH_MEMBER(this, &CommandHook::Dispatch), post);
}
CommandHook::~CommandHook()
{
if (hook_id_)
SH_REMOVE_HOOK_ID(hook_id_);
}
void CommandHook::Dispatch(DISPATCH_ARGS)
{
DISPATCH_PROLOGUE;
EngineArgs args(command);
AddRef();
bool rval = callback_(sCoreProviderImpl.CommandClient(), &args);
Release();
if (rval)
RETURN_META(MRES_SUPERCEDE);
}
void CommandHook::Zap()
{
hook_id_ = 0;
}

View File

@ -1,133 +0,0 @@
// vim: set ts=4 sw=4 tw=99 noet :
// =============================================================================
// SourceMod
// Copyright (C) 2004-2015 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_PROVIDER_GAME_HOOKS_H_
#define _INCLUDE_SOURCEMOD_PROVIDER_GAME_HOOKS_H_
// Needed for CEntityIndex, edict_t, etc.
#include <stdint.h>
#include <stddef.h>
#include <eiface.h>
#include <iserverplugin.h>
#include <amtl/am-refcounting.h>
#include <amtl/am-vector.h>
#include <amtl/am-function.h>
class ConVar;
class CCommand;
struct CCommandContext;
#if SOURCE_ENGINE >= SE_ORANGEBOX
# define DISPATCH_ARGS const CCommand &command
# define DISPATCH_PROLOGUE
#else
# define DISPATCH_ARGS
# define DISPATCH_PROLOGUE CCommand command
#endif
namespace SourceMod {
// Describes the mechanism in which client cvar queries are implemented.
enum class ClientCvarQueryMode {
Unavailable,
DLL,
VSP
};
class ICommandArgs;
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;
public:
CommandHook(ConCommand *cmd, const Callback &callback, bool post);
~CommandHook();
void Dispatch(DISPATCH_ARGS);
void Zap();
private:
int hook_id_;
Callback callback_;
};
class GameHooks
{
public:
GameHooks();
void Start();
void Shutdown();
void OnVSPReceived();
ClientCvarQueryMode GetClientCvarQueryMode() const {
return client_cvar_query_mode_;
}
public:
ke::RefPtr<CommandHook> AddCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
ke::RefPtr<CommandHook> AddPostCommandHook(ConCommand *cmd, const CommandHook::Callback &callback);
int CommandClient() const {
return last_command_client_;
}
private:
// Static callback that Valve's ConVar object executes when the convar's value changes.
#if SOURCE_ENGINE >= SE_ORANGEBOX
static void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
#else
static void OnConVarChanged(ConVar *pConVar, const char *oldValue);
#endif
// Callback for when StartQueryCvarValue() has finished.
#if SOURCE_ENGINE != SE_DARKMESSIAH
void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result,
const char *cvarName, const char *cvarValue);
#endif
void SetCommandClient(int client);
private:
class HookList : public ke::Vector<int>
{
public:
HookList &operator += (int hook_id) {
this->append(hook_id);
return *this;
}
};
HookList hooks_;
ClientCvarQueryMode client_cvar_query_mode_;
int last_command_client_;
};
} // namespace SourceMod
#endif // _INCLUDE_SOURCEMOD_PROVIDER_GAME_HOOKS_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -35,11 +35,8 @@
#include <sh_list.h> #include <sh_list.h>
#include <sh_string.h> #include <sh_string.h>
#include <sh_tinyhash.h> #include <sh_tinyhash.h>
#include <am-utility.h> #include <sm_trie_tpl.h>
#include <am-hashset.h> #include "sm_trie.h"
#include <am-hashmap.h>
#include <sm_stringhashmap.h>
#include <sm_namehashset.h>
#include "sm_globals.h" #include "sm_globals.h"
#include "sm_queue.h" #include "sm_queue.h"
#include <IGameHelpers.h> #include <IGameHelpers.h>
@ -47,12 +44,8 @@
#include <server_class.h> #include <server_class.h>
#include <datamap.h> #include <datamap.h>
#include <ihandleentity.h> #include <ihandleentity.h>
#include <tier0/icommandline.h>
#include <string_t.h>
namespace SourceMod { class CCommand;
class ICommandArgs;
} // namespace SourceMod
using namespace SourceHook; using namespace SourceHook;
using namespace SourceMod; using namespace SourceMod;
@ -60,79 +53,18 @@ using namespace SourceMod;
#define HUD_PRINTTALK 3 #define HUD_PRINTTALK 3
#define HUD_PRINTCENTER 4 #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 DataTableInfo
{ {
struct SendPropPolicy
{
static inline bool matches(const char *name, const sm_sendprop_info_t &info)
{
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)
{
}
ServerClass *sc; ServerClass *sc;
NameHashSet<sm_sendprop_info_t, SendPropPolicy> lookup; KTrie<sm_sendprop_info_t> lookup;
}; };
struct DataMapCachePolicy struct DataMapTrie
{ {
static inline bool matches(const char *name, const sm_datatable_info_t &info) DataMapTrie() : trie(NULL) {}
{ Trie *trie;
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;
struct DelayedFakeCliCmd struct DelayedFakeCliCmd
{ {
String cmd; String cmd;
@ -142,7 +74,7 @@ struct DelayedFakeCliCmd
struct CachedCommandInfo struct CachedCommandInfo
{ {
const ICommandArgs *args; const CCommand *args;
#if SOURCE_ENGINE <= SE_DARKMESSIAH #if SOURCE_ENGINE <= SE_DARKMESSIAH
char cmd[300]; char cmd[300];
#endif #endif
@ -155,7 +87,6 @@ struct DelayedKickInfo
char buffer[384]; char buffer[384];
}; };
// copy from game/shared/entitylist_base.h
class CEntInfo class CEntInfo
{ {
public: public:
@ -163,27 +94,12 @@ public:
int m_SerialNumber; int m_SerialNumber;
CEntInfo *m_pPrev; CEntInfo *m_pPrev;
CEntInfo *m_pNext; CEntInfo *m_pNext;
#if SOURCE_ENGINE >= SE_PORTAL2
string_t m_iName;
string_t m_iClassName;
#endif
};
// Corresponds to TF2's eFindMapResult in eiface.h
// Not yet in other games, but eventually in others on same branch.
enum class SMFindMapResult : cell_t {
Found,
NotFound,
FuzzyMatch,
NonCanonical,
PossiblyAvailable
}; };
class CHalfLife2 : class CHalfLife2 :
public SMGlobalClass, public SMGlobalClass,
public IGameHelpers public IGameHelpers
{ {
friend class AutoEnterCommand;
public: public:
CHalfLife2(); CHalfLife2();
~CHalfLife2(); ~CHalfLife2();
@ -192,15 +108,12 @@ public:
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
void OnSourceModAllInitialized_Post(); void OnSourceModAllInitialized_Post();
/*void OnSourceModAllShutdown();*/ /*void OnSourceModAllShutdown();*/
ConfigResult OnSourceModConfigChanged(const char *key, const char *value,
ConfigSource source, char *error, size_t maxlength) override;
public: //IGameHelpers public: //IGameHelpers
SendProp *FindInSendTable(const char *classname, const char *offset); SendProp *FindInSendTable(const char *classname, const char *offset);
bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info); bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info);
datamap_t *GetDataMap(CBaseEntity *pEntity); datamap_t *GetDataMap(CBaseEntity *pEntity);
ServerClass *FindServerClass(const char *classname); ServerClass *FindServerClass(const char *classname);
typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset); typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
bool FindDataMapInfo(datamap_t *pMap, const char *offset, sm_datatable_info_t *pDataTable);
void SetEdictStateChanged(edict_t *pEdict, unsigned short offset); void SetEdictStateChanged(edict_t *pEdict, unsigned short offset);
bool TextMsg(int client, int dest, const char *msg); bool TextMsg(int client, int dest, const char *msg);
bool HintTextMsg(int client, const char *msg); bool HintTextMsg(int client, const char *msg);
@ -222,39 +135,25 @@ public: //IGameHelpers
CEntInfo *LookupEntity(int entIndex); CEntInfo *LookupEntity(int entIndex);
cell_t EntityToBCompatRef(CBaseEntity *pEntity); cell_t EntityToBCompatRef(CBaseEntity *pEntity);
void *GetGlobalEntityList(); void *GetGlobalEntityList();
int GetSendPropOffset(SendProp *prop);
ICommandLine *GetValveCommandLine();
const char *GetEntityClassname(edict_t *pEdict);
const char *GetEntityClassname(CBaseEntity *pEntity);
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);
#endif
bool GetServerSteam3Id(char *pszOut, size_t len) const override;
uint64_t GetServerSteamId64() const override;
public: public:
void AddToFakeCliCmdQueue(int client, int userid, const char *cmd); void AddToFakeCliCmdQueue(int client, int userid, const char *cmd);
void ProcessFakeCliCmdQueue(); void ProcessFakeCliCmdQueue();
public: public:
const ICommandArgs *PeekCommandStack(); void PushCommandStack(const CCommand *cmd);
void PopCommandStack();
const CCommand *PeekCommandStack();
const char *CurrentCommandName(); const char *CurrentCommandName();
void AddDelayedKick(int client, int userid, const char *msg); void AddDelayedKick(int client, int userid, const char *msg);
void ProcessDelayedKicks(); void ProcessDelayedKicks();
#if !defined METAMOD_PLAPI_VERSION || PLAPI_VERSION < 11
bool IsOriginalEngine();
#endif
private: private:
void PushCommandStack(const ICommandArgs *cmd);
void PopCommandStack();
DataTableInfo *_FindServerClass(const char *classname); DataTableInfo *_FindServerClass(const char *classname);
private: private:
void InitLogicalEntData(); Trie *m_pClasses;
void InitCommandLine(); List<DataTableInfo *> m_Tables;
private: THash<datamap_t *, DataMapTrie> m_Maps;
typedef ke::HashMap<datamap_t *, DataMapCache *, ke::PointerPolicy<datamap_t> > DataTableMap;
NameHashSet<DataTableInfo *> m_Classes;
DataTableMap m_Maps;
int m_MsgTextMsg; int m_MsgTextMsg;
int m_HinTextMsg; int m_HinTextMsg;
int m_SayTextMsg; int m_SayTextMsg;
@ -263,32 +162,10 @@ private:
CStack<DelayedFakeCliCmd *> m_FreeCmds; CStack<DelayedFakeCliCmd *> m_FreeCmds;
CStack<CachedCommandInfo> m_CommandStack; CStack<CachedCommandInfo> m_CommandStack;
Queue<DelayedKickInfo> m_DelayedKicks; Queue<DelayedKickInfo> m_DelayedKicks;
void *m_pGetCommandLine;
#if SOURCE_ENGINE == SE_CSGO
public:
bool CanSetCSGOEntProp(const char *pszPropName)
{
return !m_bFollowCSGOServerGuidelines || !m_CSGOBadList.has(pszPropName);
}
private:
ke::HashSet<ke::AString, detail::StringHashMapPolicy> m_CSGOBadList;
bool m_bFollowCSGOServerGuidelines = true;
#endif
}; };
extern CHalfLife2 g_HL2; extern CHalfLife2 g_HL2;
bool IndexToAThings(cell_t, CBaseEntity **pEntData, edict_t **pEdictData); bool IndexToAThings(cell_t, CBaseEntity **pEntData, edict_t **pEdictData);
class AutoEnterCommand
{
public:
AutoEnterCommand(const ICommandArgs *args) {
g_HL2.PushCommandStack(args);
}
~AutoEnterCommand() {
g_HL2.PopCommandStack();
}
};
#endif //_INCLUDE_SOURCEMOD_CHALFLIFE2_H_ #endif //_INCLUDE_SOURCEMOD_CHALFLIFE2_H_

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -30,14 +30,13 @@
*/ */
#include "HandleSys.h" #include "HandleSys.h"
#include "ShareSys.h"
#include "PluginSys.h"
#include "ExtensionSys.h"
#include "Logger.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include "common_logic.h" #include "sm_stringutil.h"
#include "ShareSys.h"
#include "ExtensionSys.h"
#include "PluginSys.h"
#include <am-string.h>
#include <bridge/include/ILogger.h>
HandleSystem g_HandleSys; HandleSystem g_HandleSys;
@ -61,6 +60,9 @@ HandleSystem::HandleSystem()
m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE]; m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE); memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
m_TypeLookup = sm_trie_create();
m_strtab = new BaseStringTable(512);
m_TypeTail = 0; m_TypeTail = 0;
} }
@ -68,6 +70,8 @@ HandleSystem::~HandleSystem()
{ {
delete [] m_Handles; delete [] m_Handles;
delete [] m_Types; delete [] m_Types;
sm_trie_destroy(m_TypeLookup);
delete m_strtab;
} }
@ -141,10 +145,12 @@ HandleType_t HandleSystem::CreateType(const char *name,
if (name && name[0] != '\0') if (name && name[0] != '\0')
{ {
if (m_TypeLookup.contains(name)) if (sm_trie_retrieve(m_TypeLookup, name, NULL))
{ {
if (err) if (err)
{
*err = HandleError_Parameter; *err = HandleError_Parameter;
}
return 0; return 0;
} }
} }
@ -206,8 +212,10 @@ HandleType_t HandleSystem::CreateType(const char *name,
pType->dispatch = dispatch; pType->dispatch = dispatch;
if (name && name[0] != '\0') if (name && name[0] != '\0')
{ {
pType->name = new ke::AString(name); pType->nameIdx = m_strtab->AddString(name);
m_TypeLookup.insert(name, pType); sm_trie_insert(m_TypeLookup, name, (void *)pType);
} else {
pType->nameIdx = -1;
} }
pType->opened = 0; pType->opened = 0;
@ -235,14 +243,21 @@ HandleType_t HandleSystem::CreateType(const char *name,
return index; return index;
} }
bool HandleSystem::FindHandleType(const char *name, HandleType_t *aResult) bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
{ {
QHandleType *type; QHandleType *_type;
if (!m_TypeLookup.retrieve(name, &type))
return false;
if (aResult) if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
*aResult = type - m_Types; {
return false;
}
unsigned int offset = _type - m_Types;
if (type)
{
*type = offset;
}
return true; return true;
} }
@ -564,7 +579,6 @@ HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Hand
} }
pNewHandle->clone = index; pNewHandle->clone = index;
pNewHandle->object = NULL;
pHandle->refcount++; pHandle->refcount++;
*newhandle = new_handle; *newhandle = new_handle;
@ -606,53 +620,8 @@ HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, Iden
return CloneHandle(pHandle, index, newhandle, newOwner); return CloneHandle(pHandle, index, newhandle, newOwner);
} }
Handle_t HandleSystem::FastCloneHandle(QHandle *pHandle, unsigned int index)
{
if (pHandle->clone)
return FastCloneHandle(&m_Handles[pHandle->clone], pHandle->clone);
Handle_t hndl;
if (CloneHandle(pHandle, index, &hndl, g_pCoreIdent) != HandleError_None)
return BAD_HANDLE;
return hndl;
}
Handle_t HandleSystem::FastCloneHandle(Handle_t hndl)
{
QHandle *pHandle;
unsigned int index;
GetHandleUnchecked(hndl, pHandle, index);
return FastCloneHandle(pHandle, index);
}
void HandleSystem::GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index)
{
#ifndef NDEBUG
unsigned int serial = (hndl >> 16);
#endif
index = (hndl & HANDLESYS_HANDLE_MASK);
assert(index != 0 && index <= m_HandleTail && index < HANDLESYS_MAX_HANDLES);
pHandle = &m_Handles[index];
assert(pHandle->set && pHandle->set != HandleSet_Freed);
assert(pHandle->serial == serial);
}
HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index) HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
{ {
if (pHandle->is_destroying)
{
/* Someone tried to free this recursively.
* We'll just ignore this safely.
*/
return HandleError_None;
}
QHandleType *pType = &m_Types[pHandle->type]; QHandleType *pType = &m_Types[pHandle->type];
if (pHandle->clone) if (pHandle->clone)
@ -667,7 +636,6 @@ HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
pMaster = &m_Handles[master]; pMaster = &m_Handles[master];
/* Release the clone now */ /* Release the clone now */
pHandle->is_destroying = true;
ReleasePrimHandle(index); ReleasePrimHandle(index);
/* Decrement the master's reference count */ /* Decrement the master's reference count */
@ -676,27 +644,20 @@ HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index)
/* Type should be the same but do this anyway... */ /* Type should be the same but do this anyway... */
pType = &m_Types[pMaster->type]; pType = &m_Types[pMaster->type];
pMaster->is_destroying = true; pMaster->is_destroying = true;
if (pMaster->object) pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
{
pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object);
}
ReleasePrimHandle(master); ReleasePrimHandle(master);
} }
} else if (pHandle->set == HandleSet_Identity) { } else if (pHandle->set == HandleSet_Identity) {
/* If we're an identity, skip all this stuff! /* If we're an identity, skip all this stuff!
* NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR
*/ */
pHandle->is_destroying = true;
ReleasePrimHandle(index); ReleasePrimHandle(index);
} else { } else {
/* Decrement, free if necessary */ /* Decrement, free if necessary */
if (--pHandle->refcount == 0) if (--pHandle->refcount == 0)
{ {
pHandle->is_destroying = true; pHandle->is_destroying = true;
if (pHandle->object) pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
{
pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
}
ReleasePrimHandle(index); ReleasePrimHandle(index);
} else { } else {
/* We must be cloned, so mark ourselves as freed */ /* We must be cloned, so mark ourselves as freed */
@ -729,6 +690,14 @@ HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSec
return HandleError_Access; return HandleError_Access;
} }
if (pHandle->is_destroying)
{
/* Someone tried to free this recursively.
* We'll just ignore this safely.
*/
return HandleError_None;
}
return FreeHandle(pHandle, index); return FreeHandle(pHandle, index);
} }
@ -787,9 +756,6 @@ void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index)
assert(pHandle->owner == 0); assert(pHandle->owner == 0);
return; return;
} }
pHandle->owner = NULL;
/* Note that since 0 is an invalid handle, if any of these links are 0, /* Note that since 0 is an invalid handle, if any of these links are 0,
* the data can still be set. * the data can still be set.
*/ */
@ -899,6 +865,10 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
m_Types[++m_FreeTypes].freeID = type; m_Types[++m_FreeTypes].freeID = type;
} }
/* Invalidate the type now */
IHandleTypeDispatch *dispatch = pType->dispatch;
pType->dispatch = NULL;
/* Make sure nothing is using this type. */ /* Make sure nothing is using this type. */
if (pType->opened) if (pType->opened)
{ {
@ -910,9 +880,29 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
{ {
continue; continue;
} }
if (pHandle->clone)
FreeHandle(pHandle, i); {
/* Get parent */
QHandle *pOther = &m_Handles[pHandle->clone];
if (--pOther->refcount == 0)
{
/* Free! */
dispatch->OnHandleDestroy(type, pOther->object);
ReleasePrimHandle(pHandle->clone);
}
/* Unlink ourselves since we don't have a reference count */
ReleasePrimHandle(i);
} else {
/* If it's not a clone, we still have to check the reference count.
* Either way, we'll be destroyed eventually because the handle types do not change.
*/
if (--pHandle->refcount == 0)
{
/* Free! */
dispatch->OnHandleDestroy(type, pHandle->object);
ReleasePrimHandle(i);
}
}
if (pType->opened == 0) if (pType->opened == 0)
{ {
break; break;
@ -920,12 +910,14 @@ bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
} }
} }
/* Invalidate the type now */
pType->dispatch = NULL;
/* Remove it from the type cache. */ /* Remove it from the type cache. */
if (pType->name) if (pType->nameIdx != -1)
m_TypeLookup.remove(pType->name->chars()); {
const char *typeName;
typeName = m_strtab->GetString(pType->nameIdx);
sm_trie_delete(m_TypeLookup, typeName);
}
return true; return true;
} }
@ -958,17 +950,14 @@ bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHa
return true; return true;
} }
#define HANDLE_LOG_VERY_BAD(message, ...) \
logger->LogFatal(message, ##__VA_ARGS__); \
logger->LogError(message, ##__VA_ARGS__);
bool HandleSystem::TryAndFreeSomeHandles() bool HandleSystem::TryAndFreeSomeHandles()
{ {
IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
IPlugin *highest_owner = NULL; IPlugin *highest_owner = NULL;
unsigned int highest_handle_count = 0; unsigned int highest_handle_count = 0;
/* Search all plugins */ /* Search all plugins */
for (IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator(); pl_iter->MorePlugins(); pl_iter->NextPlugin()) while (pl_iter->MorePlugins())
{ {
IPlugin *plugin = pl_iter->GetPlugin(); IPlugin *plugin = pl_iter->GetPlugin();
IdentityToken_t *identity = plugin->GetIdentity(); IdentityToken_t *identity = plugin->GetIdentity();
@ -997,6 +986,8 @@ bool HandleSystem::TryAndFreeSomeHandles()
highest_owner = plugin; highest_owner = plugin;
highest_handle_count = handle_count; highest_handle_count = handle_count;
} }
pl_iter->NextPlugin();
} }
if (highest_owner == NULL || highest_handle_count == 0) if (highest_owner == NULL || highest_handle_count == 0)
@ -1004,85 +995,20 @@ bool HandleSystem::TryAndFreeSomeHandles()
return false; return false;
} }
HANDLE_LOG_VERY_BAD("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename()); g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename());
HANDLE_LOG_VERY_BAD("[SM] Unloading plugin to free %d handles.", highest_handle_count); g_Logger.LogFatal("[SM] Unloading plugin to free %d handles.", highest_handle_count);
HANDLE_LOG_VERY_BAD("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count); g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count);
HANDLE_LOG_VERY_BAD("--------------------------------------------------------------------------");
const IdentityToken_t *pIdentity = highest_owner->GetIdentity();
unsigned int total = 0, highest_index = 0, total_size = 0, size;
unsigned int * pCount = new unsigned int[HANDLESYS_TYPEARRAY_SIZE+1];
memset(pCount, 0, ((HANDLESYS_TYPEARRAY_SIZE + 1) * sizeof(unsigned int)));
for (unsigned int i = 1; i <= m_HandleTail; ++i)
{
const QHandle &Handle = m_Handles[i];
if (Handle.set != HandleSet_Used || Handle.owner != pIdentity)
{
continue;
}
++pCount[Handle.type];
++total;
if (Handle.type >= highest_index)
{
highest_index = ((Handle.type) + 1);
}
if (Handle.clone != 0)
{
continue;
}
if (m_Types[Handle.type].dispatch->GetHandleApproxSize(Handle.type, Handle.object, &size))
{
total_size += size;
}
}
const char * pTypeName = NULL;
for (unsigned int i = 0; i < highest_index; ++i)
{
if (pCount[i] == 0)
{
continue; /* We may have gaps, it's fine. */
}
if (m_Types[i].name)
pTypeName = m_Types[i].name->chars();
else
pTypeName = "ANON";
HANDLE_LOG_VERY_BAD("Type\t%-20.20s|\tCount\t%u", pTypeName, pCount[i]);
}
HANDLE_LOG_VERY_BAD("-- Approximately %d bytes of memory are in use by (%u) Handles.\n", total_size, total);
delete [] pCount;
highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak"); highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak");
return scripts->UnloadPlugin(highest_owner); return g_PluginSys.UnloadPlugin(highest_owner);
} }
static void rep(const HandleReporter &fn, const char *fmt, ...) void HandleSystem::Dump(HANDLE_REPORTER rep)
{
va_list ap;
char buffer[1024];
va_start(ap, fmt);
ke::SafeVsprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
fn(buffer);
}
void HandleSystem::Dump(const HandleReporter &fn)
{ {
unsigned int total_size = 0; unsigned int total_size = 0;
rep(fn, "%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory"); rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory");
rep(fn, "--------------------------------------------------------------------------"); rep("--------------------------------------------------------------------------");
for (unsigned int i = 1; i <= m_HandleTail; i++) for (unsigned int i = 1; i <= m_HandleTail; i++)
{ {
if (m_Handles[i].set != HandleSet_Used) if (m_Handles[i].set != HandleSet_Used)
@ -1100,20 +1026,20 @@ void HandleSystem::Dump(const HandleReporter &fn)
{ {
owner = "CORE"; owner = "CORE";
} }
else if (pOwner == scripts->GetIdentity()) else if (pOwner == g_PluginSys.GetIdentity())
{ {
owner = "PLUGINSYS"; owner = "PLUGINSYS";
} }
else else
{ {
IExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner); CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner);
if (ext) if (ext)
{ {
owner = ext->GetFilename(); owner = ext->GetFilename();
} }
else else
{ {
SMPlugin *pPlugin = scripts->FindPluginByIdentity(pOwner); CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner);
if (pPlugin) if (pPlugin)
{ {
owner = pPlugin->GetFilename(); owner = pPlugin->GetFilename();
@ -1128,41 +1054,23 @@ void HandleSystem::Dump(const HandleReporter &fn)
const char *type = "ANON"; const char *type = "ANON";
QHandleType *pType = &m_Types[m_Handles[i].type]; QHandleType *pType = &m_Types[m_Handles[i].type];
unsigned int size = 0; unsigned int size = 0;
unsigned int parentIdx; if (pType->nameIdx != -1)
bool bresult;
if (pType->name)
type = pType->name->chars();
if ((parentIdx = m_Handles[i].clone) != 0)
{ {
if (m_Handles[parentIdx].refcount > 0) type = m_strtab->GetString(pType->nameIdx);
{
size = 0;
bresult = true;
}
else
{
bresult = pType->dispatch->GetHandleApproxSize(m_Handles[parentIdx].type, m_Handles[parentIdx].object, &size);
}
} }
else
{
bresult = pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size);
}
if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION
|| !bresult) || !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size))
{ {
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1"); rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1");
} }
else else
{ {
char buffer[32]; char buffer[32];
ke::SafeSprintf(buffer, sizeof(buffer), "%d", size); UTIL_Format(buffer, sizeof(buffer), "%d", size);
rep(fn, "0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer); rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer);
total_size += size; total_size += size;
} }
} }
rep(fn, "-- Approximately %d bytes of memory are in use by Handles.\n", total_size); rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size);
} }

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,13 +34,12 @@
#include <IHandleSys.h> #include <IHandleSys.h>
#include <stdio.h> #include <stdio.h>
#include <sm_namehashset.h> #include "sm_globals.h"
#include <amtl/am-autoptr.h> #include "sm_trie.h"
#include <amtl/am-string.h> #include "sourcemod.h"
#include <amtl/am-function.h> #include "sm_memtable.h"
#include "common_logic.h"
#define HANDLESYS_MAX_HANDLES (1<<15) #define HANDLESYS_MAX_HANDLES (1<<14)
#define HANDLESYS_MAX_TYPES (1<<9) #define HANDLESYS_MAX_TYPES (1<<9)
#define HANDLESYS_MAX_SUBTYPES 0xF #define HANDLESYS_MAX_SUBTYPES 0xF
#define HANDLESYS_SUBTYPE_MASK 0xF #define HANDLESYS_SUBTYPE_MASK 0xF
@ -105,19 +104,10 @@ struct QHandleType
TypeAccess typeSec; TypeAccess typeSec;
HandleAccess hndlSec; HandleAccess hndlSec;
unsigned int opened; unsigned int opened;
ke::AutoPtr<ke::AString> name; int nameIdx;
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 void (HANDLE_REPORTER)(const char *str, ...);
class HandleSystem : class HandleSystem :
public IHandleSys public IHandleSys
@ -169,10 +159,7 @@ public: //IHandleSystem
const HandleAccess *pAccess, const HandleAccess *pAccess,
HandleError *err); HandleError *err);
void Dump(const HandleReporter &reporter); void Dump(HANDLE_REPORTER rep);
/* Bypasses security checks. */
Handle_t FastCloneHandle(Handle_t hndl);
protected: protected:
/** /**
* Decodes a handle with sanity and security checking. * Decodes a handle with sanity and security checking.
@ -183,9 +170,6 @@ protected:
unsigned int *index, unsigned int *index,
bool ignoreFree=false); bool ignoreFree=false);
Handle_t FastCloneHandle(QHandle *pHandle, unsigned int index);
void GetHandleUnchecked(Handle_t hndl, QHandle *& pHandle, unsigned int &index);
/** /**
* Creates a basic handle and sets its reference count to 1. * Creates a basic handle and sets its reference count to 1.
* Does not do any type or security checking. * Does not do any type or security checking.
@ -227,37 +211,15 @@ protected:
private: private:
QHandle *m_Handles; QHandle *m_Handles;
QHandleType *m_Types; QHandleType *m_Types;
NameHashSet<QHandleType *> m_TypeLookup; Trie *m_TypeLookup;
unsigned int m_TypeTail; unsigned int m_TypeTail;
unsigned int m_FreeTypes; unsigned int m_FreeTypes;
unsigned int m_HandleTail; unsigned int m_HandleTail;
unsigned int m_FreeHandles; unsigned int m_FreeHandles;
unsigned int m_HSerial; unsigned int m_HSerial;
BaseStringTable *m_strtab;
}; };
extern HandleSystem g_HandleSys; extern HandleSystem g_HandleSys;
struct AutoHandleRooter
{
public:
AutoHandleRooter(Handle_t hndl)
{
if (hndl != BAD_HANDLE)
this->hndl = g_HandleSys.FastCloneHandle(hndl);
else
this->hndl = BAD_HANDLE;
}
~AutoHandleRooter()
{
if (hndl != BAD_HANDLE)
{
HandleSecurity sec(g_pCoreIdent, g_pCoreIdent);
g_HandleSys.FreeHandle(hndl, &sec);
}
}
private:
Handle_t hndl;
};
#endif //_INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ #endif //_INCLUDE_SOURCEMOD_HANDLESYSTEM_H_

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,16 +34,27 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <sm_platform.h> #include <sm_platform.h>
#include "sm_stringutil.h"
#include "LibrarySys.h" #include "LibrarySys.h"
#include <amtl/am-string.h>
#include <amtl/os/am-path.h>
#include <amtl/os/am-fsutil.h>
LibrarySystem g_LibSys; LibrarySystem g_LibSys;
CLibrary::CLibrary(ke::RefPtr<ke::SharedLib> lib) CLibrary::~CLibrary()
: lib_(lib)
{ {
if (m_lib)
{
#if defined PLATFORM_WINDOWS
FreeLibrary(m_lib);
#elif defined PLATFORM_POSIX
dlclose(m_lib);
#endif
m_lib = NULL;
}
}
CLibrary::CLibrary(LibraryHandle me)
{
m_lib = me;
} }
void CLibrary::CloseLibrary() void CLibrary::CloseLibrary()
@ -53,7 +64,11 @@ void CLibrary::CloseLibrary()
void *CLibrary::GetSymbolAddress(const char *symname) void *CLibrary::GetSymbolAddress(const char *symname)
{ {
return lib_->lookup(symname); #if defined PLATFORM_WINDOWS
return GetProcAddress(m_lib, symname);
#elif defined PLATFORM_POSIX
return dlsym(m_lib, symname);
#endif
} }
@ -66,7 +81,7 @@ CDirectory::CDirectory(const char *path)
{ {
#if defined PLATFORM_WINDOWS #if defined PLATFORM_WINDOWS
char newpath[PLATFORM_MAX_PATH]; char newpath[PLATFORM_MAX_PATH];
ke::SafeSprintf(newpath, sizeof(newpath), "%s\\*.*", path); snprintf(newpath, sizeof(newpath), "%s\\*.*", path);
m_dir = FindFirstFileA(newpath, &m_fd); m_dir = FindFirstFileA(newpath, &m_fd);
if (!IsValid()) if (!IsValid())
{ {
@ -78,7 +93,7 @@ CDirectory::CDirectory(const char *path)
{ {
/* :TODO: we need to read past "." and ".."! */ /* :TODO: we need to read past "." and ".."! */
ep = readdir(m_dir); ep = readdir(m_dir);
ke::SafeStrcpy(m_origpath, PLATFORM_MAX_PATH, path); snprintf(m_origpath, PLATFORM_MAX_PATH, "%s", path);
} else { } else {
ep = NULL; ep = NULL;
} }
@ -125,10 +140,8 @@ bool CDirectory::IsEntryDirectory()
return ((m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); return ((m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY);
#elif defined PLATFORM_POSIX #elif defined PLATFORM_POSIX
char temppath[PLATFORM_MAX_PATH]; char temppath[PLATFORM_MAX_PATH];
int ret = ke::SafeSprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
if (static_cast<size_t>(ret) >= sizeof(temppath)) return g_LibSys.IsPathDirectory(temppath);
return false;
return ke::file::IsDirectory(temppath);
#endif #endif
} }
@ -138,10 +151,8 @@ bool CDirectory::IsEntryFile()
return !(m_fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)); return !(m_fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE));
#elif defined PLATFORM_POSIX #elif defined PLATFORM_POSIX
char temppath[PLATFORM_MAX_PATH]; char temppath[PLATFORM_MAX_PATH];
int ret = ke::SafeSprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName());
if (static_cast<size_t>(ret) >= sizeof(temppath)) return g_LibSys.IsPathFile(temppath);
return false;
return ke::file::IsFile(temppath);
#endif #endif
} }
@ -176,17 +187,75 @@ bool CDirectory::IsValid()
bool LibrarySystem::PathExists(const char *path) bool LibrarySystem::PathExists(const char *path)
{ {
return ke::file::PathExists(path); #if defined PLATFORM_WINDOWS
DWORD attr = GetFileAttributesA(path);
return (attr != INVALID_FILE_ATTRIBUTES);
#elif defined PLATFORM_POSIX
struct stat s;
return (stat(path, &s) == 0);
#endif
} }
bool LibrarySystem::IsPathFile(const char *path) bool LibrarySystem::IsPathFile(const char *path)
{ {
return ke::file::IsFile(path); #if defined PLATFORM_WINDOWS
DWORD attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
return false;
}
if (attr & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE))
{
return false;
}
return true;
#elif defined PLATFORM_POSIX
struct stat s;
if (stat(path, &s) != 0)
{
return false;
}
return S_ISREG(s.st_mode) ? true : false;
#endif
} }
bool LibrarySystem::IsPathDirectory(const char *path) bool LibrarySystem::IsPathDirectory(const char *path)
{ {
return ke::file::IsDirectory(path); #if defined PLATFORM_WINDOWS
DWORD attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
return false;
}
if (attr & FILE_ATTRIBUTE_DIRECTORY)
{
return true;
}
#elif defined PLATFORM_LINUX
struct stat s;
if (stat(path, &s) != 0)
{
return false;
}
if (S_ISDIR(s.st_mode))
{
return true;
}
#endif
return false;
} }
IDirectory *LibrarySystem::OpenDirectory(const char *path) IDirectory *LibrarySystem::OpenDirectory(const char *path)
@ -224,13 +293,13 @@ void LibrarySystem::GetPlatformErrorEx(int code, char *error, size_t maxlength)
maxlength, maxlength,
NULL) == 0) NULL) == 0)
{ {
ke::SafeSprintf(error, maxlength, "error code %08x", code); UTIL_Format(error, maxlength, "error code %08x", code);
} }
#elif defined PLATFORM_LINUX #elif defined PLATFORM_LINUX
const char *ae = strerror_r(code, error, maxlength); const char *ae = strerror_r(code, error, maxlength);
if (ae != error) if (ae != error)
{ {
ke::SafeStrcpy(error, maxlength, ae); UTIL_Format(error, maxlength, "%s", ae);
} }
#elif defined PLATFORM_POSIX #elif defined PLATFORM_POSIX
strerror_r(code, error, maxlength); strerror_r(code, error, maxlength);
@ -238,6 +307,18 @@ void LibrarySystem::GetPlatformErrorEx(int code, char *error, size_t maxlength)
} }
} }
void LibrarySystem::GetLoaderError(char *buffer, size_t maxlength)
{
#if defined PLATFORM_WINDOWS
GetPlatformError(buffer, maxlength);
#elif defined PLATFORM_POSIX
if (buffer != NULL && maxlength)
{
strncopy(buffer, dlerror(), maxlength);
}
#endif
}
void LibrarySystem::CloseDirectory(IDirectory *dir) void LibrarySystem::CloseDirectory(IDirectory *dir)
{ {
delete dir; delete dir;
@ -245,9 +326,19 @@ void LibrarySystem::CloseDirectory(IDirectory *dir)
ILibrary *LibrarySystem::OpenLibrary(const char *path, char *error, size_t maxlength) ILibrary *LibrarySystem::OpenLibrary(const char *path, char *error, size_t maxlength)
{ {
ke::RefPtr<ke::SharedLib> lib = ke::SharedLib::Open(path, error, maxlength); LibraryHandle lib;
if (!lib) #if defined PLATFORM_WINDOWS
return nullptr; lib = LoadLibraryA(path);
#elif defined PLATFORM_POSIX
lib = dlopen(path, RTLD_NOW);
#endif
if (lib == NULL)
{
GetLoaderError(error, maxlength);
return NULL;
}
return new CLibrary(lib); return new CLibrary(lib);
} }
@ -255,8 +346,23 @@ size_t LibrarySystem::PathFormat(char *buffer, size_t len, const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
size_t mylen = ke::path::FormatVa(buffer, len, fmt, ap); size_t mylen = vsnprintf(buffer, len, fmt, ap);
va_end(ap); va_end(ap);
if (mylen >= len)
{
mylen = len - 1;
buffer[mylen] = '\0';
}
for (size_t i=0; i<mylen; i++)
{
if (buffer[i] == PLATFORM_SEP_ALTCHAR)
{
buffer[i] = PLATFORM_SEP_CHAR;
}
}
return mylen; return mylen;
} }
@ -292,7 +398,11 @@ const char *LibrarySystem::GetFileExtension(const char *filename)
bool LibrarySystem::CreateFolder(const char *path) bool LibrarySystem::CreateFolder(const char *path)
{ {
return ke::file::CreateDirectory(path, 0775); #if defined PLATFORM_WINDOWS
return (mkdir(path) != -1);
#elif defined PLATFORM_POSIX
return (mkdir(path, 0775) != -1);
#endif
} }
size_t LibrarySystem::GetFileFromPath(char *buffer, size_t maxlength, const char *path) size_t LibrarySystem::GetFileFromPath(char *buffer, size_t maxlength, const char *path)
@ -300,7 +410,7 @@ size_t LibrarySystem::GetFileFromPath(char *buffer, size_t maxlength, const char
size_t length = strlen(path); size_t length = strlen(path);
for (size_t i = length - 1; for (size_t i = length - 1;
i <= length - 1; i >= 0 && i <= length - 1;
i--) i--)
{ {
if (path[i] == '/' if (path[i] == '/'
@ -309,12 +419,12 @@ size_t LibrarySystem::GetFileFromPath(char *buffer, size_t maxlength, const char
#endif #endif
) )
{ {
return ke::SafeStrcpy(buffer, maxlength, &path[i+1]); return UTIL_Format(buffer, maxlength, "%s", &path[i+1]);
} }
} }
/* We scanned and found no path separator */ /* We scanned and found no path separator */
return ke::SafeStrcpy(buffer, maxlength, path); return UTIL_Format(buffer, maxlength, "%s", path);
} }
bool LibrarySystem::FileTime(const char *path, FileTimeType type, time_t *pTime) bool LibrarySystem::FileTime(const char *path, FileTimeType type, time_t *pTime)

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -33,11 +33,16 @@
#define _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ #define _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_
#include <ILibrarySys.h> #include <ILibrarySys.h>
#include <amtl/os/am-shared-library.h>
#include "sm_platform.h" #include "sm_platform.h"
using namespace SourceMod; using namespace SourceMod;
#if defined PLATFORM_WINDOWS
typedef HMODULE LibraryHandle;
#elif defined PLATFORM_POSIX
typedef void * LibraryHandle;
#endif
class CDirectory : public IDirectory class CDirectory : public IDirectory
{ {
public: public:
@ -66,12 +71,13 @@ private:
class CLibrary : public ILibrary class CLibrary : public ILibrary
{ {
public: public:
CLibrary(ke::RefPtr<ke::SharedLib> lib); CLibrary(LibraryHandle me);
~CLibrary();
public: public:
void CloseLibrary() override; void CloseLibrary();
void *GetSymbolAddress(const char *symname) override; void *GetSymbolAddress(const char *symname);
private: private:
ke::RefPtr<ke::SharedLib> lib_; LibraryHandle m_lib;
}; };
class LibrarySystem : public ILibrarySys class LibrarySystem : public ILibrarySys
@ -90,6 +96,7 @@ public:
bool CreateFolder(const char *path); bool CreateFolder(const char *path);
size_t GetFileFromPath(char *buffer, size_t maxlength, const char *path); size_t GetFileFromPath(char *buffer, size_t maxlength, const char *path);
bool FileTime(const char *path, FileTimeType type, time_t *pTime); bool FileTime(const char *path, FileTimeType type, time_t *pTime);
void GetLoaderError(char *buffer, size_t maxlength);
}; };
extern LibrarySystem g_LibSys; extern LibrarySystem g_LibSys;

View File

@ -34,37 +34,517 @@
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "Logger.h" #include "Logger.h"
#include "LibrarySys.h"
#include "TimerSys.h" #include "TimerSys.h"
#include "logic_bridge.h"
#include <sourcemod_version.h> #include <sourcemod_version.h>
#include <bridge/include/IProviderCallbacks.h>
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,
char *error,
size_t maxlength)
{
if (strcasecmp(key, "Logging") == 0)
{
bool state;
if (strcasecmp(value, "on") == 0)
{
state = true;
} else if (strcasecmp(value, "off") == 0) {
state = false;
} else {
UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
return ConfigResult_Reject;
}
if (source == ConfigSource_Console)
{
state ? EnableLogging() : DisableLogging();
} else {
m_InitialState = state;
}
return ConfigResult_Accept;
} else if (strcasecmp(key, "LogMode") == 0) {
if (strcasecmp(value, "daily") == 0)
{
m_Mode = LoggingMode_Daily;
} else if (strcasecmp(value, "map") == 0) {
m_Mode = LoggingMode_PerMap;
} else if (strcasecmp(value, "game") == 0) {
m_Mode = LoggingMode_Game;
} else {
UTIL_Format(error, maxlength, "Invalid value: must be [daily|map|game]");
return ConfigResult_Reject;
}
return ConfigResult_Accept;
}
return ConfigResult_Ignore;
}
void Logger::OnSourceModStartup(bool late)
{
InitLogger(m_Mode);
}
void Logger::OnSourceModAllShutdown()
{
CloseLogger();
}
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;
GetAdjustedTime(&t);
tm *curtime = localtime(&t);
while (true)
{
g_SourceMod.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];
g_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, SM_FULL_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;
GetAdjustedTime(&t);
tm *curtime = localtime(&t);
m_CurDay = curtime->tm_mday;
char _filename[256];
g_SourceMod.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_SourceMod.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;
}
}
}
void Logger::CloseLogger()
{
_CloseFile();
}
void Logger::LogToOpenFile(FILE *fp, const char *msg, ...)
{
if (!m_Active)
{
return;
}
va_list ap;
va_start(ap, msg);
LogToOpenFileEx(fp, msg, ap);
va_end(ap);
}
void Logger::LogToFileOnly(FILE *fp, const char *msg, ...)
{
if (!m_Active)
{
return;
}
va_list ap;
va_start(ap, msg);
LogToFileOnlyEx(fp, msg, ap);
va_end(ap);
}
void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
char buffer[3072];
UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t;
GetAdjustedTime(&t);
tm *curtime = localtime(&t);
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
fprintf(fp, "L %s: %s\n", date, buffer);
g_SMAPI->ConPrintf("L %s: %s\n", date, buffer);
}
void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap)
{
if (!m_Active)
{
return;
}
char buffer[3072];
UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
char date[32];
time_t t;
GetAdjustedTime(&t);
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);
}
void Logger::LogMessage(const char *vafmt, ...)
{
if (!m_Active)
{
return;
}
if (m_Mode == LoggingMode_Game)
{
va_list ap;
va_start(ap, vafmt);
_PrintToGameLog(vafmt, ap);
va_end(ap);
return;
}
if (m_DelayedStart)
{
m_DelayedStart = false;
_NewMapFile();
}
time_t t;
GetAdjustedTime(&t);
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_CurDay != curtime->tm_mday)
{
char _filename[256];
g_SourceMod.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_CurDay = 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, SM_FULL_VERSION);
}
va_list ap;
va_start(ap, vafmt);
LogToOpenFileEx(fp, vafmt, ap);
va_end(ap);
fclose(fp);
} else {
goto print_error;
}
return;
print_error:
char error[255];
g_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;
}
void Logger::LogError(const char *vafmt, ...)
{
va_list ap;
va_start(ap, vafmt);
LogErrorEx(vafmt, ap);
va_end(ap);
}
void Logger::LogErrorEx(const char *vafmt, va_list ap)
{
if (!m_Active)
{
return;
}
time_t t;
GetAdjustedTime(&t);
tm *curtime = localtime(&t);
if (curtime->tm_mday != m_CurDay)
{
char _filename[256];
g_SourceMod.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_CurDay = 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];
g_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;
}
}
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;
}
void Logger::_PrintToGameLog(const char *fmt, va_list ap)
{
char msg[3072];
size_t len;
len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
msg[len++] = '\n';
msg[len] = '\0';
Engine_LogPrintWrapper(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)
{
return;
}
m_Active = true;
LogMessage("[SM] Logging enabled manually by user.");
}
void Logger::DisableLogging()
{
if (!m_Active)
{
return;
}
LogMessage("[SM] Logging disabled manually by user.");
m_Active = false;
}
void Logger::LogFatal(const char *msg, ...)
{
/* :TODO: make this print all pretty-like
* In fact, the pretty log printing function should be abstracted.
* It's already implemented twice which is bad.
*/
va_list ap;
char path[PLATFORM_MAX_PATH];
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
FILE *fp = fopen(path, "at");
if (fp)
{
m_Active = true;
va_start(ap, msg);
LogToOpenFileEx(fp, msg, ap);
va_end(ap);
m_Active = false;
fclose(fp);
}
}
bool g_in_game_log_hook = false; bool g_in_game_log_hook = false;
static LoggerCore g_LoggerCore;
SH_DECL_HOOK1_void(IVEngineServer, LogPrint, SH_NOATTRIB, false, const char *);
static void HookLogPrint(const char *message)
{
g_in_game_log_hook = true;
bool stopped = logicore.callbacks->OnLogPrint(message);
g_in_game_log_hook = false;
if (stopped)
RETURN_META(MRES_SUPERCEDE);
}
void LoggerCore::OnSourceModStartup(bool late)
{
SH_ADD_HOOK(IVEngineServer, LogPrint, engine, SH_STATIC(HookLogPrint), false);
}
void LoggerCore::OnSourceModAllShutdown()
{
SH_REMOVE_HOOK(IVEngineServer, LogPrint, engine, SH_STATIC(HookLogPrint), false);
}
void Engine_LogPrintWrapper(const char *msg) void Engine_LogPrintWrapper(const char *msg)
{ {
if (g_in_game_log_hook) if (g_in_game_log_hook)

View File

@ -38,13 +38,73 @@
using namespace SourceHook; using namespace SourceHook;
class LoggerCore : public SMGlobalClass enum LogType
{ {
LogType_Normal,
LogType_Error
};
enum LoggingMode
{
LoggingMode_Daily,
LoggingMode_PerMap,
LoggingMode_Game
};
class Logger : public SMGlobalClass
{
public:
Logger() : m_Mode(LoggingMode_Daily), m_ErrMapStart(false),
m_Active(false), m_DelayedStart(false), m_DailyPrintHdr(false),
m_InitialState(true)
{
}
public: //SMGlobalClass public: //SMGlobalClass
ConfigResult OnSourceModConfigChanged(const char *key,
const char *value,
ConfigSource source,
char *error,
size_t maxlength);
void OnSourceModStartup(bool late); void OnSourceModStartup(bool late);
void OnSourceModAllShutdown(); void OnSourceModAllShutdown();
void OnSourceModLevelChange(const char *mapName);
public:
void InitLogger(LoggingMode mode);
void CloseLogger();
void EnableLogging();
void DisableLogging();
void LogMessage(const char *msg, ...);
void LogError(const char *msg, ...);
void LogErrorEx(const char *msg, va_list ap);
void LogFatal(const char *msg, ...);
void LogToOpenFile(FILE *fp, const char *msg, ...);
void LogToOpenFileEx(FILE *fp, const char *msg, va_list ap);
/* 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 _CloseFile();
void _NewMapFile();
void _PrintToGameLog(const char *fmt, va_list ap);
private:
String m_NrmFileName;
String m_ErrFileName;
String m_CurMapName;
LoggingMode m_Mode;
int m_CurDay;
bool m_ErrMapStart;
bool m_Active;
bool m_DelayedStart;
bool m_DailyPrintHdr;
bool m_InitialState;
}; };
void Engine_LogPrintWrapper(const char *msg); void Engine_LogPrintWrapper(const char *msg);
extern bool g_in_game_log_hook;
extern Logger g_Logger;
#endif // _INCLUDE_SOURCEMOD_CLOGGER_H_ #endif // _INCLUDE_SOURCEMOD_CLOGGER_H_

181
core/Makefile Normal file
View File

@ -0,0 +1,181 @@
# (C)2004-2008 SourceMod Development Team
# Makefile written by David "BAILOPAN" Anderson
SMSDK = ..
SRCDS_BASE = ~/srcds
HL2SDK_ORIG = ../../hl2sdk
HL2SDK_OB = ../../hl2sdk-ob
HL2SDK_OB_VALVE = ../../hl2sdk-ob-valve
HL2SDK_L4D = ../../hl2sdk-l4d
HL2SDK_L4D2 = ../../hl2sdk-l4d2
MMSOURCE17 = ../../mmsource-central
#####################################
### EDIT BELOW FOR OTHER PROJECTS ###
#####################################
OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreConfig.cpp \
Database.cpp DebugReporter.cpp EventManager.cpp GameConfigs.cpp HalfLife2.cpp Logger.cpp \
MemoryUtils.cpp PlayerManager.cpp TimerSys.cpp Translator.cpp UserMessages.cpp \
sm_autonatives.cpp sm_memtable.cpp sm_srvcmds.cpp sm_stringutil.cpp sm_trie.cpp \
sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \
frame_hooks.cpp concmd_cleaner.cpp PhraseCollection.cpp NextMap.cpp \
NativeOwner.cpp logic_bridge.cpp ConsoleDetours.cpp
OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
smn_filesystem.cpp smn_gameconfigs.cpp smn_halflife.cpp \
smn_handles.cpp smn_keyvalues.cpp smn_banning.cpp \
smn_lang.cpp smn_player.cpp smn_string.cpp smn_timers.cpp \
smn_usermsgs.cpp smn_menus.cpp smn_database.cpp smn_vector.cpp \
smn_hudtext.cpp smn_nextmap.cpp
OBJECTS += ExtensionSys.cpp \
ForwardSys.cpp \
HandleSys.cpp \
LibrarySys.cpp \
PluginInfoDatabase.cpp \
PluginSys.cpp \
ShareSys.cpp \
NativeInvoker.cpp
##############################################
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
##############################################
C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing
C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3
C_GCC4_FLAGS = -fvisibility=hidden
CPP_GCC4_FLAGS = -fvisibility-inlines-hidden
CPP = gcc
override ENGSET = false
ifeq "$(ENGINE)" "original"
HL2SDK = $(HL2SDK_ORIG)
HL2PUB = $(HL2SDK)/public
HL2LIB = $(HL2SDK)/linux_sdk
CFLAGS += -DSOURCE_ENGINE=1
METAMOD = $(MMSOURCE17)/core-legacy
INCLUDE += -I$(HL2SDK)/public/dlls
SRCDS = $(SRCDS_BASE)
BINARY = sourcemod.1.ep1.so
override ENGSET = true
endif
ifeq "$(ENGINE)" "orangebox"
HL2SDK = $(HL2SDK_OB)
HL2PUB = $(HL2SDK)/public
HL2LIB = $(HL2SDK)/lib/linux
CFLAGS += -DSOURCE_ENGINE=3
METAMOD = $(MMSOURCE17)/core
INCLUDE += -I$(HL2SDK)/public/game/server
SRCDS = $(SRCDS_BASE)/orangebox
BINARY = sourcemod.2.ep2.so
override ENGSET = true
endif
ifeq "$(ENGINE)" "orangeboxvalve"
HL2SDK = $(HL2SDK_OB_VALVE)
HL2PUB = $(HL2SDK)/public
HL2LIB = $(HL2SDK)/lib/linux
CFLAGS += -DSOURCE_ENGINE=4
METAMOD = $(MMSOURCE17)/core
INCLUDE += -I$(HL2SDK)/public/game/server -I$(HL2SDK)/common
SRCDS = $(SRCDS_BASE)/orangebox
BINARY = sourcemod.2.ep2v.so
override ENGSET = true
endif
ifeq "$(ENGINE)" "left4dead"
HL2SDK = $(HL2SDK_L4D)
HL2PUB = $(HL2SDK)/public
HL2LIB = $(HL2SDK)/lib/linux
CFLAGS += -DSOURCE_ENGINE=5
METAMOD = $(MMSOURCE17)/core
INCLUDE += -I$(HL2SDK)/public/game/server
SRCDS = $(SRCDS_BASE)/l4d
BINARY = sourcemod.2.l4d.so
override ENGSET = true
endif
ifeq "$(ENGINE)" "left4dead2"
HL2SDK = $(HL2SDK_L4D2)
HL2PUB = $(HL2SDK)/public
HL2LIB = $(HL2SDK)/lib/linux
CFLAGS += -DSOURCE_ENGINE=6
METAMOD = $(MMSOURCE17)/core
INCLUDE += -I$(HL2SDK)/public/game/server
SRCDS = $(SRCDS_BASE)/left4dead2
BINARY = sourcemod.2.l4d2.so
override ENGSET = true
endif
CFLAGS += -DSE_EPISODEONE=1 -DSE_DARKMESSIAH=2 -DSE_ORANGEBOX=3 -DSE_ORANGEBOXVALVE=4 -DSE_LEFT4DEAD=5 -DSE_LEFT4DEAD2=6
ifeq "$(ENGINE)" "left4dead2"
LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a libvstdlib.so \
libtier0.so -static-libgcc
else
LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a vstdlib_i486.so \
tier0_i486.so -static-libgcc
endif
INCLUDE += -I. -I.. -I$(HL2PUB) -I$(HL2PUB)/engine -I$(HL2PUB)/mathlib -I$(HL2PUB)/vstdlib \
-I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 -I$(METAMOD) -I$(METAMOD)/sourcehook \
-I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn
CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror \
-Wno-uninitialized -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -DSM_DEFAULT_THREADER -m32
CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti
################################################
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
################################################
ifeq "$(DEBUG)" "true"
BIN_DIR = Debug.$(ENGINE)
CFLAGS += $(C_DEBUG_FLAGS)
else
BIN_DIR = Release.$(ENGINE)
CFLAGS += $(C_OPT_FLAGS)
endif
GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1)
ifeq "$(GCC_VERSION)" "4"
CFLAGS += $(C_GCC4_FLAGS)
CPPFLAGS += $(CPP_GCC4_FLAGS)
endif
OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o)
OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o)
$(BIN_DIR)/%.o: %.cpp
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
$(BIN_DIR)/%.o: %.c
$(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $<
all: check
mkdir -p $(BIN_DIR)
ifeq "$(ENGINE)" "left4dead2"
ln -sf $(SRCDS)/bin/libvstdlib.so libvstdlib.so;
ln -sf $(SRCDS)/bin/libtier0.so libtier0.so;
else
ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so;
ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so;
endif
$(MAKE) -f Makefile sourcemod
check:
if [ "$(ENGSET)" = "false" ]; then \
echo "You must supply ENGINE=left4dead or ENGINE=orangebox or ENGINE=original"; \
exit 1; \
fi
sourcemod: check $(OBJ_LINUX)
$(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -shared -ldl -lm -o$(BIN_DIR)/$(BINARY)
debug:
$(MAKE) -f Makefile all DEBUG=true
default: all
clean: check
rm -rf $(BIN_DIR)/*.o
rm -rf $(BIN_DIR)/$(BINARY)

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -28,39 +28,36 @@
*/ */
#include "MemoryUtils.h" #include "MemoryUtils.h"
#include "ShareSys.h"
#ifdef PLATFORM_LINUX #ifdef PLATFORM_LINUX
#include <fcntl.h> #include <fcntl.h>
#include <link.h> #include <link.h>
#include <sys/mman.h> #include <sys/mman.h>
#define PAGE_SIZE 4096
#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#endif
#ifdef PLATFORM_APPLE
#include <mach-o/dyld_images.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#endif #endif
//MemoryUtils g_MemUtils; MemoryUtils g_MemUtils;
#if 0
MemoryUtils::MemoryUtils() MemoryUtils::MemoryUtils()
{ {
#ifdef PLATFORM_APPLE #ifdef PLATFORM_WINDOWS
/* Get pointer to struct that describes all loaded mach-o images in process */ SYSTEM_INFO info;
struct nlist list[2]; GetSystemInfo(&info);
memset(list, 0, sizeof(list));
list[0].n_un.n_name = (char *)"_dyld_all_image_infos"; m_PageSize = info.dwPageSize;
nlist("/usr/lib/dyld", list);
m_ImageList = (struct dyld_all_image_infos *)list[0].n_value; #elif defined PLATFORM_POSIX
m_PageSize = sysconf(_SC_PAGE_SIZE);
#endif #endif
} }
#endif
MemoryUtils::~MemoryUtils() MemoryUtils::~MemoryUtils()
{ {
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #ifdef PLATFORM_LINUX
for (size_t i = 0; i < m_SymTables.size(); i++) for (size_t i = 0; i < m_SymTables.size(); i++)
{ {
delete m_SymTables[i]; delete m_SymTables[i];
@ -69,10 +66,10 @@ MemoryUtils::~MemoryUtils()
#endif #endif
} }
/*void MemoryUtils::OnSourceModAllInitialized() void MemoryUtils::OnSourceModAllInitialized()
{ {
sharesys->AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
}*/ }
void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len) void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
{ {
@ -116,7 +113,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
return GetProcAddress((HMODULE)handle, symbol); return GetProcAddress((HMODULE)handle, symbol);
#elif defined PLATFORM_LINUX #elif defined PLATFORM_LINUX
struct link_map *dlmap; struct link_map *dlmap;
@ -131,14 +128,12 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
uint32_t symbol_count; uint32_t symbol_count;
LibSymbolTable *libtable; LibSymbolTable *libtable;
SymbolTable *table; SymbolTable *table;
AddrTable *table2;
Symbol *symbol_entry; Symbol *symbol_entry;
dlmap = (struct link_map *)handle; dlmap = (struct link_map *)handle;
symtab_hdr = NULL; symtab_hdr = NULL;
strtab_hdr = NULL; strtab_hdr = NULL;
table = NULL; table = NULL;
table2 = NULL;
/* See if we already have a symbol table for this library */ /* See if we already have a symbol table for this library */
for (size_t i = 0; i < m_SymTables.size(); i++) for (size_t i = 0; i < m_SymTables.size(); i++)
@ -147,7 +142,6 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
if (libtable->lib_base == dlmap->l_addr) if (libtable->lib_base == dlmap->l_addr)
{ {
table = &libtable->table; table = &libtable->table;
table2 = &libtable->table2;
break; break;
} }
} }
@ -157,11 +151,9 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
{ {
libtable = new LibSymbolTable(); libtable = new LibSymbolTable();
libtable->table.Initialize(); libtable->table.Initialize();
libtable->table2.Initialize();
libtable->lib_base = dlmap->l_addr; libtable->lib_base = dlmap->l_addr;
libtable->last_pos = 0; libtable->last_pos = 0;
table = &libtable->table; table = &libtable->table;
table2 = &libtable->table2;
m_SymTables.push_back(libtable); m_SymTables.push_back(libtable);
} }
@ -245,173 +237,23 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol)
/* Caching symbols as we go along */ /* Caching symbols as we go along */
cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value)); cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
table2->InternSymbol(sym_name, strlen(sym_name), (void *)(dlmap->l_addr + sym.st_value));
if (strcmp(symbol, sym_name) == 0) if (strcmp(symbol, sym_name) == 0)
{ {
symbol_entry = cur_sym; symbol_entry = cur_sym;
libtable->last_pos = ++i; libtable->last_pos = ++i;
// break; break;
} }
} }
libtable->last_pos = symbol_count;
munmap(file_hdr, dlstat.st_size); munmap(file_hdr, dlstat.st_size);
return symbol_entry ? symbol_entry->address : NULL; return symbol_entry ? symbol_entry->address : NULL;
#elif defined PLATFORM_APPLE
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;
const char *strtab;
uint32_t loadcmd_count;
uint32_t symbol_count;
LibSymbolTable *libtable;
SymbolTable *table;
Symbol *symbol_entry;
dlbase = 0;
image_count = m_ImageList->infoArrayCount;
linkedit_hdr = NULL;
symtab_hdr = NULL;
table = NULL;
/* Loop through mach-o images in process.
* We can skip index 0 since that is just the executable.
*/
for (uint32_t i = 1; i < image_count; i++)
{
const struct dyld_image_info &info = m_ImageList->infoArray[i];
/* "Load" each one until we get a matching handle */
void *h = dlopen(info.imageFilePath, RTLD_NOLOAD);
if (h == handle)
{
dlbase = (uintptr_t)info.imageLoadAddress;
dlclose(h);
break;
}
dlclose(h);
}
if (!dlbase)
{
/* Uh oh, we couldn't find a matching handle */
return NULL;
}
/* See if we already have a symbol table for this library */
for (size_t i = 0; i < m_SymTables.size(); i++)
{
libtable = m_SymTables[i];
if (libtable->lib_base == dlbase)
{
table = &libtable->table;
break;
}
}
/* If we don't have a symbol table for this library, then create one */
if (table == NULL)
{
libtable = new LibSymbolTable();
libtable->table.Initialize();
libtable->lib_base = dlbase;
libtable->last_pos = 0;
table = &libtable->table;
m_SymTables.push_back(libtable);
}
/* See if the symbol is already cached in our table */
symbol_entry = table->FindSymbol(symbol, strlen(symbol));
if (symbol_entry != NULL)
{
return symbol_entry->address;
}
/* 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));
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)
{
struct segment_command *seg = (struct segment_command *)loadcmds;
if (strcmp(seg->segname, "__LINKEDIT") == 0)
{
linkedit_hdr = seg;
if (symtab_hdr)
{
break;
}
}
}
else if (loadcmds->cmd == LC_SYMTAB)
{
symtab_hdr = (struct symtab_command *)loadcmds;
if (linkedit_hdr)
{
break;
}
}
/* Load commands are not of a fixed size which is why we add the size */
loadcmds = (struct load_command *)((uintptr_t)loadcmds + loadcmds->cmdsize);
}
if (!linkedit_hdr || !symtab_hdr || !symtab_hdr->symoff || !symtab_hdr->stroff)
{
/* Uh oh, no symbol table */
return NULL;
}
linkedit_addr = dlbase + linkedit_hdr->vmaddr;
symtab = (struct nlist *)(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];
/* Ignore the prepended underscore on all symbols, so +1 here */
const char *sym_name = strtab + sym.n_un.n_strx + 1;
Symbol *cur_sym;
/* Skip symbols that are undefined */
if (sym.n_sect == NO_SECT)
{
continue;
}
/* Caching symbols as we go along */
cur_sym = table->InternSymbol(sym_name, strlen(sym_name), (void *)(dlbase + sym.n_value));
if (strcmp(symbol, sym_name) == 0)
{
symbol_entry = cur_sym;
libtable->last_pos = ++i;
break;
}
}
return symbol_entry ? symbol_entry->address : NULL;
#endif #endif
} }
bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
{ {
uintptr_t baseAddr; unsigned long baseAddr;
if (libPtr == NULL) if (libPtr == NULL)
{ {
@ -431,7 +273,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
return false; return false;
} }
baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase); baseAddr = reinterpret_cast<unsigned long>(info.AllocationBase);
/* All this is for our insane sanity checks :o */ /* All this is for our insane sanity checks :o */
dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr); dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr);
@ -480,7 +322,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
} }
/* This is for our insane sanity checks :o */ /* This is for our insane sanity checks :o */
baseAddr = reinterpret_cast<uintptr_t>(info.dli_fbase); baseAddr = reinterpret_cast<unsigned long>(info.dli_fbase);
file = reinterpret_cast<Elf32_Ehdr *>(baseAddr); file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
/* Check ELF magic */ /* Check ELF magic */
@ -511,105 +353,21 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
phdrCount = file->e_phnum; phdrCount = file->e_phnum;
phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff); phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
/* Add up the memory sizes of segments marked as PT_LOAD as those are the only ones that should be in memory */
for (uint16_t i = 0; i < phdrCount; i++) for (uint16_t i = 0; i < phdrCount; i++)
{ {
Elf32_Phdr &hdr = phdr[i]; Elf32_Phdr &hdr = phdr[i];
/* We only really care about the segment with executable code */ if (hdr.p_type == PT_LOAD)
if (hdr.p_type == PT_LOAD && hdr.p_flags == (PF_X|PF_R))
{ {
/* From glibc, elf/dl-load.c: lib.memorySize += hdr.p_memsz;
* c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
* & ~(GLRO(dl_pagesize) - 1));
*
* In glibc, the segment file size is aligned up to the nearest page size and
* added to the virtual address of the segment. We just want the size here.
*/
lib.memorySize = PAGE_ALIGN_UP(hdr.p_filesz);
break;
} }
} }
#elif defined PLATFORM_APPLE
Dl_info info;
struct mach_header *file;
struct segment_command *seg;
uint32_t cmd_count;
if (!dladdr(libPtr, &info))
{
return false;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
}
/* This is for our insane sanity checks :o */
baseAddr = (uintptr_t)info.dli_fbase;
file = (struct mach_header *)baseAddr;
/* Check Mach-O magic */
if (file->magic != MH_MAGIC)
{
return false;
}
/* Check architecture (32-bit/x86) */
if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL)
{
return false;
}
/* For our purposes, this must be a dynamic library */
if (file->filetype != MH_DYLIB)
{
return false;
}
cmd_count = file->ncmds;
seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header));
/* Add up memory sizes of mapped segments */
for (uint32_t i = 0; i < cmd_count; i++)
{
if (seg->cmd == LC_SEGMENT)
{
lib.memorySize += seg->vmsize;
}
seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
}
#endif #endif
lib.baseAddress = reinterpret_cast<void *>(baseAddr); lib.baseAddress = reinterpret_cast<void *>(baseAddr);
return true; return true;
} }
const char* MemoryUtils::ResolveAddr(void *handle, void* addr)
{
struct link_map *dlmap;
dlmap = (struct link_map *)handle;
LibSymbolTable *libtable;
AddrTable *table2;
for (size_t i = 0; i < m_SymTables.size(); i++)
{
libtable = m_SymTables[i];
if (libtable->lib_base == dlmap->l_addr)
{
table2 = &libtable->table2;
break;
}
}
if (table2 == NULL) return NULL;
Symbol *sym = table2->FindAddr(addr);
return sym ? sym->buffer() : NULL;
}

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 sw=4 tw=99 noet :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2011 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2000 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -30,9 +30,9 @@
#ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ #ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
#define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_ #define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
#include "common_logic.h"
#include <IMemoryUtils.h> #include <IMemoryUtils.h>
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #include "sm_globals.h"
#ifdef PLATFORM_LINUX
#include <sh_vector.h> #include <sh_vector.h>
#include "sm_symtable.h" #include "sm_symtable.h"
@ -41,21 +41,19 @@ using namespace SourceHook;
using namespace SourceMod; using namespace SourceMod;
#ifdef PLATFORM_APPLE
#include <CoreServices/CoreServices.h>
#endif
struct DynLibInfo struct DynLibInfo
{ {
void *baseAddress; void *baseAddress;
size_t memorySize; size_t memorySize;
}; };
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #ifdef PLATFORM_LINUX
typedef uint32_t Elf32_Addr;
struct LibSymbolTable struct LibSymbolTable
{ {
SymbolTable table; SymbolTable table;
uintptr_t lib_base; Elf32_Addr lib_base;
uint32_t last_pos; uint32_t last_pos;
}; };
#endif #endif
@ -65,7 +63,6 @@ class MemoryUtils :
public SMGlobalClass public SMGlobalClass
{ {
public: public:
MemoryUtils();
~MemoryUtils(); ~MemoryUtils();
public: // SMGlobalClass public: // SMGlobalClass
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
@ -74,14 +71,9 @@ public: // IMemoryUtils
void *ResolveSymbol(void *handle, const char *symbol); void *ResolveSymbol(void *handle, const char *symbol);
public: public:
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
#if defined PLATFORM_LINUX || defined PLATFORM_APPLE #ifdef PLATFORM_LINUX
private: private:
CVector<LibSymbolTable *> m_SymTables; CVector<LibSymbolTable *> m_SymTables;
#ifdef PLATFORM_APPLE
struct dyld_all_image_infos *m_ImageList;
SInt32 m_OSXMajor;
SInt32 m_OSXMinor;
#endif
#endif #endif
}; };

View File

@ -38,8 +38,10 @@
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "MenuStyle_Valve.h" #include "MenuStyle_Valve.h"
#include "ShareSys.h"
#include "HandleSys.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "logic_bridge.h" #include "Translator.h"
MenuManager g_Menus; MenuManager g_Menus;
VoteMenuHandler s_VoteHandler; VoteMenuHandler s_VoteHandler;
@ -54,24 +56,24 @@ MenuManager::MenuManager()
void MenuManager::OnSourceModAllInitialized() void MenuManager::OnSourceModAllInitialized()
{ {
sharesys->AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
HandleAccess access; HandleAccess access;
handlesys->InitAccessDefaults(NULL, &access); g_HandleSys.InitAccessDefaults(NULL, &access);
/* Deny cloning to menus */ /* Deny cloning to menus */
access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY; access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
m_MenuType = handlesys->CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL); m_MenuType = g_HandleSys.CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL);
/* Also deny deletion to styles */ /* Also deny deletion to styles */
access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY; access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
m_StyleType = handlesys->CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL); m_StyleType = g_HandleSys.CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
} }
void MenuManager::OnSourceModAllShutdown() void MenuManager::OnSourceModAllShutdown()
{ {
handlesys->RemoveType(m_MenuType, g_pCoreIdent); g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
handlesys->RemoveType(m_StyleType, g_pCoreIdent); g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
} }
void MenuManager::OnHandleDestroy(HandleType_t type, void *object) void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
@ -110,7 +112,7 @@ Handle_t MenuManager::CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner)
return BAD_HANDLE; return BAD_HANDLE;
} }
return handlesys->CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL); return g_HandleSys.CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL);
} }
Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style) Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
@ -120,7 +122,7 @@ Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
return BAD_HANDLE; return BAD_HANDLE;
} }
return handlesys->CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL); return g_HandleSys.CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL);
} }
HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu) HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
@ -130,7 +132,7 @@ HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
sec.pIdentity = g_pCoreIdent; sec.pIdentity = g_pCoreIdent;
sec.pOwner = NULL; sec.pOwner = NULL;
return handlesys->ReadHandle(handle, m_MenuType, &sec, (void **)menu); return g_HandleSys.ReadHandle(handle, m_MenuType, &sec, (void **)menu);
} }
HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style) HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
@ -140,7 +142,7 @@ HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
sec.pIdentity = g_pCoreIdent; sec.pIdentity = g_pCoreIdent;
sec.pOwner = g_pCoreIdent; sec.pOwner = g_pCoreIdent;
return handlesys->ReadHandle(handle, m_StyleType, &sec, (void **)style); return g_HandleSys.ReadHandle(handle, m_StyleType, &sec, (void **)style);
} }
bool MenuManager::SetDefaultStyle(IMenuStyle *style) bool MenuManager::SetDefaultStyle(IMenuStyle *style)
@ -229,7 +231,6 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
unsigned int pgn = menu->GetPagination(); unsigned int pgn = menu->GetPagination();
unsigned int maxItems = style->GetMaxPageItems(); unsigned int maxItems = style->GetMaxPageItems();
bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT; bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
bool novoteButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_NOVOTE) == MENUFLAG_BUTTON_NOVOTE;
if (pgn != MENU_NO_PAGINATION) if (pgn != MENU_NO_PAGINATION)
{ {
@ -240,11 +241,6 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
maxItems--; maxItems--;
} }
if (novoteButton)
{
maxItems--;
}
/* This is very not allowed! */ /* This is very not allowed! */
if (maxItems < 2) if (maxItems < 2)
{ {
@ -308,7 +304,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
{ {
ItemDrawInfo &dr = drawItems[foundItems].draw; ItemDrawInfo &dr = drawItems[foundItems].draw;
/* Is the item valid? */ /* Is the item valid? */
if (menu->GetItemInfo(i, &dr, client) != NULL) if (menu->GetItemInfo(i, &dr) != NULL)
{ {
/* Ask the user to change the style, if necessary */ /* Ask the user to change the style, if necessary */
mh->OnMenuDrawItem(menu, client, i, dr.style); mh->OnMenuDrawItem(menu, client, i, dr.style);
@ -398,7 +394,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
} }
while (++lastItem < totalItems) while (++lastItem < totalItems)
{ {
if (menu->GetItemInfo(lastItem, &dr, client) != NULL) if (menu->GetItemInfo(lastItem, &dr) != NULL)
{ {
mh->OnMenuDrawItem(menu, client, lastItem, dr.style); mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style)) if (IsSlotItem(panel, dr.style))
@ -420,7 +416,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord
lastItem--; lastItem--;
while (lastItem != 0) while (lastItem != 0)
{ {
if (menu->GetItemInfo(lastItem, &dr, client) != NULL) if (menu->GetItemInfo(lastItem, &dr) != NULL)
{ {
mh->OnMenuDrawItem(menu, client, lastItem, dr.style); mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
if (IsSlotItem(panel, dr.style)) if (IsSlotItem(panel, dr.style))
@ -440,20 +436,6 @@ skip_search:
/* Draw the item according to the order */ /* Draw the item according to the order */
menu_slots_t *slots = md.slots; menu_slots_t *slots = md.slots;
unsigned int position = 0; /* Keep track of the last position */ unsigned int position = 0; /* Keep track of the last position */
if (novoteButton)
{
char text[50];
if (!logicore.CoreTranslate(text, sizeof(text), "%T", 2, NULL, "No Vote", &client))
{
ke::SafeStrcpy(text, sizeof(text), "No Vote");
}
ItemDrawInfo dr(text, 0);
position = panel->DrawItem(dr);
slots[position].type = ItemSel_Exit;
position++;
}
if (order == ItemOrder_Ascending) if (order == ItemOrder_Ascending)
{ {
md.item_on_page = drawItems[0].position; md.item_on_page = drawItems[0].position;
@ -570,7 +552,7 @@ skip_search:
/* If there are no control options, /* If there are no control options,
* Instead just pad with invisible slots. * Instead just pad with invisible slots.
*/ */
if (!displayNext && !displayPrev) if (!displayPrev && !displayPrev)
{ {
padItem.style = ITEMDRAW_NOTEXT; padItem.style = ITEMDRAW_NOTEXT;
} }
@ -604,9 +586,9 @@ skip_search:
{ {
if (exitBackButton) if (exitBackButton)
{ {
if (!logicore.CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Back", &client)) if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Back", &client))
{ {
ke::SafeStrcpy(text, sizeof(text), "Back"); UTIL_Format(text, sizeof(text), "Back");
} }
dr.style = ITEMDRAW_CONTROL; dr.style = ITEMDRAW_CONTROL;
position = panel->DrawItem(dr); position = panel->DrawItem(dr);
@ -614,9 +596,9 @@ skip_search:
} }
else else
{ {
if (!logicore.CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Previous", &client)) if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Previous", &client))
{ {
ke::SafeStrcpy(text, sizeof(text), "Previous"); UTIL_Format(text, sizeof(text), "Previous");
} }
dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL; dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
position = panel->DrawItem(dr); position = panel->DrawItem(dr);
@ -635,9 +617,9 @@ skip_search:
/* NEXT */ /* NEXT */
if (displayNext || canDrawDisabled) if (displayNext || canDrawDisabled)
{ {
if (!logicore.CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Next", &client)) if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Next", &client))
{ {
ke::SafeStrcpy(text, sizeof(text), "Next"); UTIL_Format(text, sizeof(text), "Next");
} }
dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL; dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
position = panel->DrawItem(dr); position = panel->DrawItem(dr);
@ -666,9 +648,9 @@ skip_search:
/* EXIT */ /* EXIT */
if (exitButton) if (exitButton)
{ {
if (!logicore.CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Exit", &client)) if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Exit", &client))
{ {
ke::SafeStrcpy(text, sizeof(text), "Exit"); UTIL_Format(text, sizeof(text), "Exit");
} }
dr.style = ITEMDRAW_CONTROL; dr.style = ITEMDRAW_CONTROL;
position = panel->DrawItem(dr); position = panel->DrawItem(dr);

View File

@ -37,6 +37,7 @@
#include <sh_stack.h> #include <sh_stack.h>
#include <sh_list.h> #include <sh_list.h>
#include <sh_string.h> #include <sh_string.h>
#include "sm_memtable.h"
#include "sm_globals.h" #include "sm_globals.h"
using namespace SourceMod; using namespace SourceMod;

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,12 +34,11 @@
#include "MenuStyle_Base.h" #include "MenuStyle_Base.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "MenuManager.h" #include "MenuManager.h"
#include "HandleSys.h"
#include "CellRecipientFilter.h" #include "CellRecipientFilter.h"
#if defined MENU_DEBUG #if defined MENU_DEBUG
#include "Logger.h" #include "Logger.h"
#endif #endif
#include "logic_bridge.h"
#include "AutoHandleRooter.h"
BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE) BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
{ {
@ -323,25 +322,15 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
if (pCollideable) if (pCollideable)
{ {
const Vector & pos = pCollideable->GetCollisionOrigin(); const Vector & pos = pCollideable->GetCollisionOrigin();
enginesound->EmitSound(filter, enginesound->EmitSound(filter,
client, client,
CHAN_AUTO, CHAN_AUTO,
#if SOURCE_ENGINE >= SE_PORTAL2
sound,
-1,
#endif
sound, sound,
VOL_NORM, VOL_NORM,
ATTN_NORM, ATTN_NORM,
#if SOURCE_ENGINE >= SE_PORTAL2
0,
#endif
0, 0,
PITCH_NORM, PITCH_NORM,
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS \
|| SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_BMS || SOURCE_ENGINE == SE_TF2
0,
#endif
&pos); &pos);
} }
} }
@ -388,9 +377,6 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
RemoveClientFromWatch(client); RemoveClientFromWatch(client);
} }
Handle_t hndl = menu ? menu->GetHandle() : BAD_HANDLE;
AutoHandleRooter ahr(hndl);
if (cancel) if (cancel)
{ {
mh->OnMenuCancel(menu, client, reason); mh->OnMenuCancel(menu, client, reason);
@ -605,7 +591,7 @@ bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
} }
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
m_pStyle(pStyle), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false),
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false),
m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT) m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
{ {
@ -628,135 +614,94 @@ Handle_t CBaseMenu::GetHandle()
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw) bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
{ {
if (m_Pagination == (unsigned)MENU_NO_PAGINATION if (m_Pagination == (unsigned)MENU_NO_PAGINATION
&& m_items.length() >= m_pStyle->GetMaxPageItems()) && m_items.size() >= m_pStyle->GetMaxPageItems())
{ {
return false; return false;
} }
CItem item(m_items.length()); CItem item;
item.info = info; item.infoString = m_Strings.AddString(info);
if (draw.display) if (draw.display)
item.display = new ke::AString(draw.display); {
item.displayString = m_Strings.AddString(draw.display);
}
item.style = draw.style; item.style = draw.style;
m_items.append(ke::Move(item)); m_items.push_back(item);
return true; return true;
} }
bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw) bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
{ {
if (m_Pagination == (unsigned)MENU_NO_PAGINATION && if (m_Pagination == (unsigned)MENU_NO_PAGINATION
m_items.length() >= m_pStyle->GetMaxPageItems()) && m_items.size() >= m_pStyle->GetMaxPageItems())
{ {
return false; return false;
} }
if (position >= m_items.length()) if (position >= m_items.size())
{
return false; return false;
}
CItem item(position); CItem item;
item.info = info; item.infoString = m_Strings.AddString(info);
if (draw.display) if (draw.display)
item.display = new ke::AString(draw.display); {
item.displayString = m_Strings.AddString(draw.display);
}
item.style = draw.style; item.style = draw.style;
m_items.insert(position, ke::Move(item)); CVector<CItem>::iterator iter = m_items.iterAt(position);
m_items.insert(iter, item);
return true; return true;
} }
bool CBaseMenu::RemoveItem(unsigned int position) bool CBaseMenu::RemoveItem(unsigned int position)
{ {
if (position >= m_items.length()) if (position >= m_items.size())
{
return false; return false;
}
m_items.erase(m_items.iterAt(position));
if (m_items.size() == 0)
{
m_Strings.Reset();
}
m_items.remove(position);
return true; return true;
} }
void CBaseMenu::RemoveAllItems() void CBaseMenu::RemoveAllItems()
{ {
m_items.clear(); m_items.clear();
m_Strings.Reset();
} }
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */, int client/* =0 */) const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
{ {
if (position >= m_items.length()) if (position >= m_items.size())
return NULL;
if (client > 0 && position < m_RandomMaps[client].length())
{ {
position = m_RandomMaps[client][position]; return NULL;
} }
if (draw) if (draw)
{ {
draw->display = m_items[position].display->chars(); draw->display = m_Strings.GetString(m_items[position].displayString);
draw->style = m_items[position].style; draw->style = m_items[position].style;
} }
return m_items[position].info.chars(); return m_Strings.GetString(m_items[position].infoString);
}
void CBaseMenu::ShufflePerClient(int start, int stop)
{
// limit map len to 255 items since it's using uint8
int length = MIN(GetItemCount(), 255);
if (stop >= 0)
length = MIN(length, stop);
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
// populate per-client map ...
m_RandomMaps[i].resize(length);
for (int j = 0; j < length; j++)
m_RandomMaps[i][j] = j;
// ... and random shuffle it
for (int j = length - 1; j > start; j--)
{
int x = rand() % (j - start + 1) + start;
uint8_t tmp = m_RandomMaps[i][x];
m_RandomMaps[i][x] = m_RandomMaps[i][j];
m_RandomMaps[i][j] = tmp;
}
}
}
void CBaseMenu::SetClientMapping(int client, int *array, int length)
{
length = MIN(length, 255);
m_RandomMaps[client].resize(length);
for (int i = 0; i < length; i++)
{
m_RandomMaps[client][i] = array[i];
}
}
bool CBaseMenu::IsPerClientShuffled()
{
for (int i = 1; i < SM_MAXPLAYERS + 1; i++)
{
if(m_RandomMaps[i].length() > 0)
return true;
}
return false;
}
unsigned int CBaseMenu::GetRealItemIndex(int client, unsigned int position)
{
if (client > 0 && position < m_RandomMaps[client].length())
{
position = m_RandomMaps[client][position];
return m_items[position].index;
}
return position;
} }
unsigned int CBaseMenu::GetItemCount() unsigned int CBaseMenu::GetItemCount()
{ {
return m_items.length(); return m_items.size();
} }
bool CBaseMenu::SetPagination(unsigned int itemsPerPage) bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
@ -789,12 +734,12 @@ IMenuStyle *CBaseMenu::GetDrawStyle()
void CBaseMenu::SetDefaultTitle(const char *message) void CBaseMenu::SetDefaultTitle(const char *message)
{ {
m_Title = message; m_Title.assign(message);
} }
const char *CBaseMenu::GetDefaultTitle() const char *CBaseMenu::GetDefaultTitle()
{ {
return m_Title.chars(); return m_Title.c_str();
} }
void CBaseMenu::Cancel() void CBaseMenu::Cancel()
@ -861,7 +806,7 @@ void CBaseMenu::InternalDelete()
m_hHandle = BAD_HANDLE; m_hHandle = BAD_HANDLE;
m_bDeleting = true; m_bDeleting = true;
handlesys->FreeHandle(hndl, &sec); g_HandleSys.FreeHandle(hndl, &sec);
} }
m_pHandler->OnMenuDestroy(this); m_pHandler->OnMenuDestroy(this);
@ -886,5 +831,7 @@ IMenuHandler *CBaseMenu::GetHandler()
unsigned int CBaseMenu::GetBaseMemUsage() unsigned int CBaseMenu::GetBaseMemUsage()
{ {
return m_Title.length() + (m_items.length() * sizeof(CItem)); return m_Title.size()
+ m_Strings.GetMemTable()->GetMemUsage()
+ (m_items.size() * sizeof(CItem));
} }

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,50 +34,29 @@
#include <IMenuManager.h> #include <IMenuManager.h>
#include <IPlayerHelpers.h> #include <IPlayerHelpers.h>
#include <am-autoptr.h> #include <sh_string.h>
#include <am-string.h> #include <sh_vector.h>
#include <am-vector.h> #include "sm_memtable.h"
#include "sm_fastlink.h" #include "sm_fastlink.h"
using namespace SourceMod; using namespace SourceMod;
using namespace SourceHook;
class CItem class CItem
{ {
public: public:
CItem(unsigned int index) CItem()
{ {
this->index = index; infoString = -1;
displayString = -1;
style = 0; style = 0;
access = 0; access = 0;
} }
CItem(CItem &&other)
: info(ke::Move(other.info)),
display(ke::Move(other.display))
{
index = other.index;
style = other.style;
access = other.access;
}
CItem & operator =(CItem &&other)
{
index = other.index;
info = ke::Move(other.info);
display = ke::Move(other.display);
style = other.style;
access = other.access;
return *this;
}
public: public:
unsigned int index; int infoString;
ke::AString info; int displayString;
ke::AutoPtr<ke::AString> display;
unsigned int style; unsigned int style;
unsigned int access; unsigned int access;
private:
CItem(const CItem &other) = delete;
CItem &operator =(const CItem &other) = delete;
}; };
class CBaseMenuPlayer class CBaseMenuPlayer
@ -142,7 +121,7 @@ public:
virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw); virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw);
virtual bool RemoveItem(unsigned int position); virtual bool RemoveItem(unsigned int position);
virtual void RemoveAllItems(); virtual void RemoveAllItems();
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL, int client=0); virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL);
virtual unsigned int GetItemCount(); virtual unsigned int GetItemCount();
virtual bool SetPagination(unsigned int itemsPerPage); virtual bool SetPagination(unsigned int itemsPerPage);
virtual unsigned int GetPagination(); virtual unsigned int GetPagination();
@ -156,18 +135,15 @@ public:
virtual unsigned int GetMenuOptionFlags(); virtual unsigned int GetMenuOptionFlags();
virtual void SetMenuOptionFlags(unsigned int flags); virtual void SetMenuOptionFlags(unsigned int flags);
virtual IMenuHandler *GetHandler(); 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(); unsigned int GetBaseMemUsage();
private: private:
void InternalDelete(); void InternalDelete();
protected: protected:
ke::AString m_Title; String m_Title;
IMenuStyle *m_pStyle; IMenuStyle *m_pStyle;
BaseStringTable m_Strings;
unsigned int m_Pagination; unsigned int m_Pagination;
ke::Vector<CItem> m_items; CVector<CItem> m_items;
bool m_bShouldDelete; bool m_bShouldDelete;
bool m_bCancelling; bool m_bCancelling;
IdentityToken_t *m_pOwner; IdentityToken_t *m_pOwner;
@ -176,7 +152,6 @@ protected:
Handle_t m_hHandle; Handle_t m_hHandle;
IMenuHandler *m_pHandler; IMenuHandler *m_pHandler;
unsigned int m_nFlags; unsigned int m_nFlags;
ke::Vector<uint8_t> m_RandomMaps[SM_MAXPLAYERS+1];
}; };
#endif //_INCLUDE_MENUSTYLE_BASE_H #endif //_INCLUDE_MENUSTYLE_BASE_H

View File

@ -32,20 +32,11 @@
#include "MenuStyle_Radio.h" #include "MenuStyle_Radio.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "UserMessages.h" #include "UserMessages.h"
#include <IGameConfigs.h> #include "GameConfigs.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#if defined MENU_DEBUG #if defined MENU_DEBUG
#include "Logger.h" #include "Logger.h"
#endif #endif
#include "logic_bridge.h"
#ifdef USE_PROTOBUF_USERMESSAGES
#include <google/protobuf/descriptor.h>
#endif
#if SOURCE_ENGINE == SE_CSGO
#include <game/shared/csgo/protobuf/cstrike15_usermessages.pb.h>
#endif
extern const char *g_RadioNumTable[]; extern const char *g_RadioNumTable[];
CRadioStyle g_RadioMenuStyle; CRadioStyle g_RadioMenuStyle;
@ -53,14 +44,6 @@ int g_ShowMenuId = -1;
bool g_bRadioInit = false; bool g_bRadioInit = false;
unsigned int g_RadioMenuTimeout = 0; unsigned int g_RadioMenuTimeout = 0;
// back, next, exit
#define MAX_PAGINATION_OPTIONS 3
#define MAX_MENUSLOT_KEYS 10
static unsigned int s_RadioMaxPageItems = MAX_MENUSLOT_KEYS;
CRadioStyle::CRadioStyle() CRadioStyle::CRadioStyle()
{ {
m_players = new CRadioMenuPlayer[256+1]; m_players = new CRadioMenuPlayer[256+1];
@ -83,10 +66,6 @@ void CRadioStyle::OnSourceModLevelChange(const char *mapName)
} }
g_bRadioInit = true; g_bRadioInit = true;
// Always register the style. Use IsSupported() to check for validity before use.
g_Menus.AddStyle(this);
const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg"); const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg");
if (!msg || msg[0] == '\0') if (!msg || msg[0] == '\0')
{ {
@ -110,18 +89,7 @@ void CRadioStyle::OnSourceModLevelChange(const char *mapName)
g_RadioMenuTimeout = 0; g_RadioMenuTimeout = 0;
} }
const char *items = g_pGameConf->GetKeyValue("RadioMenuMaxPageItems"); g_Menus.AddStyle(this);
if (items != NULL)
{
int value = atoi(items);
// Only override the mostly-safe default if it's a sane value
if (value > MAX_PAGINATION_OPTIONS && value <= MAX_MENUSLOT_KEYS)
{
s_RadioMaxPageItems = value;
}
}
g_Menus.SetDefaultStyle(this); g_Menus.SetDefaultStyle(this);
g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false); g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
@ -166,22 +134,13 @@ static unsigned int g_last_holdtime = 0;
static unsigned int g_last_client_count = 0; static unsigned int g_last_client_count = 0;
static int g_last_clients[256]; static int g_last_clients[256];
#ifdef USE_PROTOBUF_USERMESSAGES
void CRadioStyle::OnUserMessage(int msg_id, protobuf::Message &msg, IRecipientFilter *pFilter)
#else
void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
#endif
{ {
int count = pFilter->GetRecipientCount(); int count = pFilter->GetRecipientCount();
#if SOURCE_ENGINE == SE_CSGO
int c = ((CCSUsrMsg_ShowMenu &)msg).display_time();
#else
bf_read br(bf->GetBasePointer(), 3); bf_read br(bf->GetBasePointer(), 3);
br.ReadWord(); br.ReadWord();
int c = br.ReadChar(); int c = br.ReadChar();
#endif
g_last_holdtime = (c == -1) ? 0 : (unsigned)c; g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
@ -229,7 +188,7 @@ IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwn
unsigned int CRadioStyle::GetMaxPageItems() unsigned int CRadioStyle::GetMaxPageItems()
{ {
return s_RadioMaxPageItems; return 10;
} }
const char *CRadioStyle::GetStyleName() const char *CRadioStyle::GetStyleName()
@ -258,6 +217,15 @@ CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
return display; return display;
} }
IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys)
{
CRadioDisplay *pPanel = MakeRadioDisplay(NULL);
pPanel->DirectSet(str, keys);
return pPanel;
}
void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display) void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
{ {
m_FreeDisplays.push(display); m_FreeDisplays.push(display);
@ -324,12 +292,11 @@ void CRadioDisplay::Reset()
keys = 0; keys = 0;
} }
bool CRadioDisplay::DirectSet(const char *str) void CRadioDisplay::DirectSet(const char *str, int keymap)
{ {
m_Title.clear(); m_Title.clear();
m_BufferText.assign(str); m_BufferText.assign(str);
keys = keymap;
return true;
} }
unsigned int CRadioDisplay::GetCurrentKey() unsigned int CRadioDisplay::GetCurrentKey()
@ -339,7 +306,7 @@ unsigned int CRadioDisplay::GetCurrentKey()
bool CRadioDisplay::SetCurrentKey(unsigned int key) bool CRadioDisplay::SetCurrentKey(unsigned int key)
{ {
if (key < m_NextPos || m_NextPos > s_RadioMaxPageItems) if (key < m_NextPos || m_NextPos > 10)
{ {
return false; return false;
} }
@ -375,7 +342,7 @@ void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item) unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
{ {
if (m_NextPos > s_RadioMaxPageItems || !CanDrawItem(item.style)) if (m_NextPos > 10 || !CanDrawItem(item.style))
{ {
return 0; return 0;
} }
@ -450,7 +417,7 @@ void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
{ {
if (title[0] != '\0') if (title[0] != '\0')
{ {
display_len = ke::SafeSprintf(display_pkt, display_len = UTIL_Format(display_pkt,
sizeof(display_pkt), sizeof(display_pkt),
"%s\n%s", "%s\n%s",
title, title,
@ -458,8 +425,9 @@ void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
} }
else else
{ {
display_len = ke::SafeStrcpy(display_pkt, display_len = UTIL_Format(display_pkt,
sizeof(display_pkt), sizeof(display_pkt),
"%s",
text); text);
} }
display_keys = keys; display_keys = keys;
@ -467,7 +435,7 @@ void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
void CRadioMenuPlayer::Radio_Refresh() void CRadioMenuPlayer::Radio_Refresh()
{ {
cell_t players[1] = { (cell_t)m_index }; cell_t players[1] = {m_index};
char *ptr = display_pkt; char *ptr = display_pkt;
char save = 0; char save = 0;
size_t len = display_len; size_t len = display_len;
@ -483,14 +451,6 @@ void CRadioMenuPlayer::Radio_Refresh()
time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime); time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
} }
#if SOURCE_ENGINE == SE_CSGO
// TODO: find what happens past 240 on CS:GO
CCSUsrMsg_ShowMenu *msg = (CCSUsrMsg_ShowMenu *)g_UserMsgs.StartProtobufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
msg->set_bits_valid_slots(display_keys);
msg->set_display_time(time);
msg->set_menu_string(ptr);
g_UserMsgs.EndMessage();
#else
while (true) while (true)
{ {
if (len > 240) if (len > 240)
@ -498,8 +458,7 @@ void CRadioMenuPlayer::Radio_Refresh()
save = ptr[240]; save = ptr[240];
ptr[240] = '\0'; ptr[240] = '\0';
} }
bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
bf_write *buffer = g_UserMsgs.StartBitBufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
buffer->WriteWord(display_keys); buffer->WriteWord(display_keys);
buffer->WriteChar(time ? time : -1); buffer->WriteChar(time ? time : -1);
buffer->WriteByte( (len > 240) ? 1 : 0 ); buffer->WriteByte( (len > 240) ? 1 : 0 );
@ -516,7 +475,6 @@ void CRadioMenuPlayer::Radio_Refresh()
break; break;
} }
} }
#endif
display_last_refresh = gpGlobals->curtime; display_last_refresh = gpGlobals->curtime;
} }
@ -552,7 +510,6 @@ unsigned int CRadioDisplay::GetApproxMemUsage()
CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner) CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
{ {
m_Pagination = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
} }
bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr) bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
@ -586,7 +543,6 @@ bool CRadioMenu::DisplayAtItem(int client,
return false; return false;
} }
AutoHandleRooter ahr(this->GetHandle());
return g_RadioMenuStyle.DoClientMenu(client, return g_RadioMenuStyle.DoClientMenu(client,
this, this,
start_item, start_item,
@ -594,17 +550,6 @@ bool CRadioMenu::DisplayAtItem(int client,
time); time);
} }
bool CRadioMenu::SetPagination(unsigned int itemsPerPage)
{
const unsigned int maxPerPage = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
if (itemsPerPage > maxPerPage)
{
return false;
}
return CBaseMenu::SetPagination(itemsPerPage);
}
void CRadioMenu::Cancel_Finally() void CRadioMenu::Cancel_Finally()
{ {
g_RadioMenuStyle.CancelMenu(this); g_RadioMenuStyle.CancelMenu(this);

View File

@ -37,12 +37,10 @@
#include "MenuStyle_Base.h" #include "MenuStyle_Base.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include <IPlayerHelpers.h> #include <IPlayerHelpers.h>
#include "UserMessages.h" #include <IUserMessages.h>
#include "sm_fastlink.h" #include "sm_fastlink.h"
#include <sh_stack.h> #include <sh_stack.h>
#include <compat_wrappers.h> #include <compat_wrappers.h>
#include "logic/common_logic.h"
#include "AutoHandleRooter.h"
using namespace SourceMod; using namespace SourceMod;
@ -67,11 +65,7 @@ private:
class CRadioStyle : class CRadioStyle :
public BaseMenuStyle, public BaseMenuStyle,
public SMGlobalClass, public SMGlobalClass,
#ifdef USE_PROTOBUF_USERMESSAGES public IUserMessageListener
public IProtobufUserMessageListener
#else
public IBitBufUserMessageListener
#endif
{ {
public: public:
CRadioStyle(); CRadioStyle();
@ -90,11 +84,7 @@ public: //IMenuStyle
unsigned int GetMaxPageItems(); unsigned int GetMaxPageItems();
unsigned int GetApproxMemUsage(); unsigned int GetApproxMemUsage();
public: //IUserMessageListener public: //IUserMessageListener
#ifdef USE_PROTOBUF_USERMESSAGES
void OnUserMessage(int msg_id, protobuf::Message &msg, IRecipientFilter *pFilter);
#else
void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter); void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter);
#endif
void OnUserMessageSent(int msg_id); void OnUserMessageSent(int msg_id);
public: public:
bool IsSupported(); bool IsSupported();
@ -103,6 +93,7 @@ public:
CRadioDisplay *MakeRadioDisplay(CRadioMenu *menu=NULL); CRadioDisplay *MakeRadioDisplay(CRadioMenu *menu=NULL);
void FreeRadioDisplay(CRadioDisplay *display); void FreeRadioDisplay(CRadioDisplay *display);
CRadioMenuPlayer *GetRadioMenuPlayer(int client); CRadioMenuPlayer *GetRadioMenuPlayer(int client);
IMenuPanel *MakeRadioDisplay(const char *str, int keys);
private: private:
CRadioMenuPlayer *m_players; CRadioMenuPlayer *m_players;
CStack<CRadioDisplay *> m_FreeDisplays; CStack<CRadioDisplay *> m_FreeDisplays;
@ -130,7 +121,8 @@ public: //IMenuPanel
bool SetCurrentKey(unsigned int key); bool SetCurrentKey(unsigned int key);
int GetAmountRemaining(); int GetAmountRemaining();
unsigned int GetApproxMemUsage(); unsigned int GetApproxMemUsage();
bool DirectSet(const char *str); public:
void DirectSet(const char *str, int keymap);
private: private:
String m_BufferText; String m_BufferText;
String m_Title; String m_Title;
@ -150,7 +142,6 @@ public:
unsigned int time, unsigned int time,
unsigned int start_item, unsigned int start_item,
IMenuHandler *alt_handler/* =NULL */); IMenuHandler *alt_handler/* =NULL */);
bool SetPagination(unsigned int itemsPerPage);
void Cancel_Finally(); void Cancel_Finally();
unsigned int GetApproxMemUsage(); unsigned int GetApproxMemUsage();
}; };

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -32,6 +32,7 @@
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "MenuStyle_Valve.h" #include "MenuStyle_Valve.h"
#include "Translator.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "ConCmdManager.h" #include "ConCmdManager.h"
@ -66,14 +67,14 @@ bool ValveMenuStyle::OnClientCommand(int client, const char *cmdname, const CCom
void ValveMenuStyle::OnSourceModAllInitialized() void ValveMenuStyle::OnSourceModAllInitialized()
{ {
g_Players.AddClientListener(this); g_Players.AddClientListener(this);
SH_ADD_HOOK(IServerPluginHelpers, CreateMessage, serverpluginhelpers, SH_MEMBER(this, &ValveMenuStyle::HookCreateMessage), false); SH_ADD_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers); g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers);
} }
void ValveMenuStyle::OnSourceModShutdown() void ValveMenuStyle::OnSourceModShutdown()
{ {
SH_RELEASE_CALLCLASS(g_pSPHCC); SH_RELEASE_CALLCLASS(g_pSPHCC);
SH_REMOVE_HOOK(IServerPluginHelpers, CreateMessage, serverpluginhelpers, SH_MEMBER(this, &ValveMenuStyle::HookCreateMessage), false); SH_REMOVE_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
g_Players.RemoveClientListener(this); g_Players.RemoveClientListener(this);
} }
@ -293,7 +294,7 @@ unsigned int CValveMenuDisplay::DrawItem(const ItemDrawInfo &item)
} }
char buffer[255]; char buffer[255];
ke::SafeSprintf(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display); UTIL_Format(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display);
KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true); KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true);
ki->SetString("command", g_OptionCmdTable[m_NextPos]); ki->SetString("command", g_OptionCmdTable[m_NextPos]);
@ -378,7 +379,7 @@ bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr)
{ {
if (option == MenuOption_IntroMessage) if (option == MenuOption_IntroMessage)
{ {
ke::SafeStrcpy(m_IntroMsg, sizeof(m_IntroMsg), (const char *)valuePtr); strncopy(m_IntroMsg, (const char *)valuePtr, sizeof(m_IntroMsg));
return true; return true;
} else if (option == MenuOption_IntroColor) { } else if (option == MenuOption_IntroColor) {
unsigned int *array = (unsigned int *)valuePtr; unsigned int *array = (unsigned int *)valuePtr;
@ -404,7 +405,6 @@ bool CValveMenu::DisplayAtItem(int client,
return false; return false;
} }
AutoHandleRooter ahr(this->GetHandle());
return g_ValveMenuStyle.DoClientMenu(client, this, start_item, alt_handler ? alt_handler : m_pHandler, time); return g_ValveMenuStyle.DoClientMenu(client, this, start_item, alt_handler ? alt_handler : m_pHandler, time);
} }

View File

@ -39,8 +39,6 @@
#include "KeyValues.h" #include "KeyValues.h"
#include "sm_fastlink.h" #include "sm_fastlink.h"
#include <compat_wrappers.h> #include <compat_wrappers.h>
#include "logic/common_logic.h"
#include "AutoHandleRooter.h"
using namespace SourceMod; using namespace SourceMod;
@ -77,7 +75,6 @@ public: //IMenuStyle
IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner); IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
unsigned int GetMaxPageItems(); unsigned int GetMaxPageItems();
unsigned int GetApproxMemUsage(); unsigned int GetApproxMemUsage();
bool IsSupported() { return true; }
private: private:
void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin); void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin);
private: private:
@ -108,7 +105,6 @@ public:
bool SetCurrentKey(unsigned int key); bool SetCurrentKey(unsigned int key);
int GetAmountRemaining(); int GetAmountRemaining();
unsigned int GetApproxMemUsage(); unsigned int GetApproxMemUsage();
bool DirectSet(const char *str) { return false; }
private: private:
KeyValues *m_pKv; KeyValues *m_pKv;
unsigned int m_NextPos; unsigned int m_NextPos;

View File

@ -39,8 +39,7 @@
#include <HalfLife2.h> #include <HalfLife2.h>
#include <mathlib.h> #include <mathlib.h>
#include <const.h> #include <const.h>
#include <ITranslator.h> #include <Translator.h>
#include "logic_bridge.h"
float g_next_vote = 0.0f; float g_next_vote = 0.0f;
@ -514,22 +513,20 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite
/* Check by our item count, NOT the vote array size */ /* Check by our item count, NOT the vote array size */
if (item < m_Items) if (item < m_Items)
{ {
unsigned int index = menu->GetRealItemIndex(client, item); m_ClientVotes[client] = item;
m_ClientVotes[client] = index; m_Votes[item]++;
m_Votes[index]++;
m_NumVotes++; m_NumVotes++;
if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool()) if (sm_vote_chat.GetBool() || sm_vote_console.GetBool())
{ {
static char buffer[1024]; static char buffer[1024];
ItemDrawInfo dr; ItemDrawInfo dr;
menu->GetItemInfo(item, &dr, client); menu->GetItemInfo(item, &dr);
if (sm_vote_console.GetBool()) if (sm_vote_console.GetBool())
{ {
int target = SOURCEMOD_SERVER_LANGUAGE; int target = SOURCEMOD_SERVER_LANGUAGE;
logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For", CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For", &target, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
&target, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
Engine_LogPrintWrapper(buffer); Engine_LogPrintWrapper(buffer);
} }
@ -541,17 +538,15 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite
CPlayer *pPlayer = g_Players.GetPlayerByIndex(i); CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
assert(pPlayer); assert(pPlayer);
if (pPlayer->IsInGame() && !pPlayer->IsFakeClient()) if (pPlayer->IsInGame())
{ {
if (m_Revoting[client]) if (m_Revoting[client])
{ {
logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Changed Vote", CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Changed Vote", &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
&i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
} }
else else
{ {
logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For", CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For", &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
&i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
} }
if (sm_vote_chat.GetBool()) if (sm_vote_chat.GetBool())
@ -561,7 +556,7 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite
if (sm_vote_client_console.GetBool()) if (sm_vote_client_console.GetBool())
{ {
ClientConsolePrint(pPlayer->GetEdict(), buffer); engine->ClientPrintf(pPlayer->GetEdict(), buffer);
} }
} }
} }
@ -639,8 +634,7 @@ void VoteMenuHandler::DrawHintProgress()
{ {
if (g_Players.GetPlayerByIndex(i)->IsInGame()) if (g_Players.GetPlayerByIndex(i)->IsInGame())
{ {
logicore.CoreTranslate(buffer, sizeof(buffer), "%T%s", 6, NULL, "Vote Count", CoreTranslate(buffer, sizeof(buffer), "%T%s", 6, NULL, "Vote Count", &i, &m_NumVotes, &m_TotalClients, &iTimeRemaining, &m_leaderList);
&i, &m_NumVotes, &m_TotalClients, &iTimeRemaining, &m_leaderList);
g_HL2.HintTextMsg(i, buffer); g_HL2.HintTextMsg(i, buffer);
} }
} }

379
core/NativeInvoker.cpp Normal file
View File

@ -0,0 +1,379 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourcePawn
* Copyright (C) 2004-2009 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 "NativeInvoker.h"
#include "ShareSys.h"
NativeInterface g_NInvoke;
NativeInvoker::NativeInvoker()
{
}
NativeInvoker::~NativeInvoker()
{
}
const char *NativeInterface::GetInterfaceName()
{
return SMINTERFACE_NINVOKE_NAME;
}
unsigned int NativeInterface::GetInterfaceVersion()
{
return SMINTERFACE_NINVOKE_VERSION;
}
void NativeInterface::OnSourceModAllInitialized()
{
g_ShareSys.AddInterface(NULL, &g_NInvoke);
}
IPluginRuntime *NativeInterface::CreateRuntime(const char *name, size_t bytes)
{
return g_pSourcePawn2->CreateEmptyRuntime(name, bytes);
}
INativeInvoker *NativeInterface::CreateInvoker()
{
return new NativeInvoker();
}
bool NativeInvoker::Start(IPluginContext *pContext, const char *name)
{
NativeEntry *entry;
entry = g_ShareSys.FindNative(name);
if (entry == NULL)
{
return false;
}
native = NULL;
if (entry->replacement.owner != NULL)
{
native = entry->replacement.func;
}
else if (entry->owner != NULL)
{
native = entry->func;
}
if (native == NULL)
{
return false;
}
this->pContext = pContext;
m_curparam = 0;
m_errorstate = SP_ERROR_NONE;
return true;
}
cell_t 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 = *(cell_t *)&number;
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 (pContext == NULL)
{
return;
}
m_errorstate = SP_ERROR_NONE;
m_curparam = 0;
pContext = NULL;
native = NULL;
}
int NativeInvoker::SetError(int err)
{
m_errorstate = err;
return err;
}
int NativeInvoker::Invoke(cell_t *result)
{
int err = SP_ERROR_NONE;
if (pContext == NULL)
{
return SP_ERROR_INVALID_NATIVE;
}
if (m_errorstate != SP_ERROR_NONE)
{
err = m_errorstate;
Cancel();
return err;
}
cell_t tresult;
if (result == NULL)
{
result = &tresult;
}
//This is for re-entrancy!
IPluginContext *ctx = pContext;
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;
bool docopies = true;
if (numparams)
{
//Save the info locally, then reset it for re-entrant calls.
memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
}
m_curparam = 0;
pContext = NULL;
/* Initialize 0th parameter */
_temp_params[0] = numparams;
/* Browse the parameters and build arrays */
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 */
if ((err = ctx->HeapAlloc(temp_info[i].size,
&temp_info[i].local_addr,
&temp_info[i].phys_addr))
!= SP_ERROR_NONE)
{
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 */
if ((err = ctx->HeapAlloc(cells,
&temp_info[i].local_addr,
&temp_info[i].phys_addr))
!= SP_ERROR_NONE)
{
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)
{
if ((err = ctx->StringToLocalUTF8(temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr,
NULL))
!= SP_ERROR_NONE)
{
break;
}
}
/* 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
{
if ((err = ctx->StringToLocal(temp_info[i].local_addr,
temp_info[i].size,
(const char *)temp_info[i].orig_addr))
!= SP_ERROR_NONE)
{
break;
}
}
}
} /* 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 (err == SP_ERROR_NONE)
{
*result = native(ctx, _temp_params);
if (ctx->GetLastNativeError() != SP_ERROR_NONE)
{
docopies = false;
ctx->ClearLastNativeError();
}
}
else
{
docopies = false;
}
/* i should be equal to the last valid parameter + 1 */
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 ((err = ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
{
return err;
}
}
return err;
}

98
core/NativeInvoker.h Normal file
View File

@ -0,0 +1,98 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2009 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_SOURCEMOD_NATIVE_INVOKER_H_
#define _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_
#include "sm_globals.h"
#include <INativeInvoker.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 INativeInvoker
{
public:
NativeInvoker();
~NativeInvoker();
public: /* ICallable */
int PushCell(cell_t cell);
int PushCellByRef(cell_t *cell, int flags=SM_PARAM_COPYBACK);
int PushFloat(float number);
int PushFloatByRef(float *number, int flags=SM_PARAM_COPYBACK);
int PushArray(cell_t *inarray, unsigned int cells, int flags=0);
int PushString(const char *string);
int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags);
void Cancel();
public: /* INativeInvoker */
bool Start(IPluginContext *pContext, const char *name);
int Invoke(cell_t *result);
private:
int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
int SetError(int err);
private:
IPluginContext *pContext;
SPVM_NATIVE_FUNC native;
cell_t m_params[SP_MAX_EXEC_PARAMS];
ParamInfo m_info[SP_MAX_EXEC_PARAMS];
unsigned int m_curparam;
int m_errorstate;
};
class NativeInterface :
public INativeInterface,
public SMGlobalClass
{
public: /* SMGlobalClass */
void OnSourceModAllInitialized();
public: /* SMInterface */
unsigned int GetInterfaceVersion();
const char *GetInterfaceName();
public: /* INativeInvoker */
IPluginRuntime *CreateRuntime(const char *name, size_t bytes);
INativeInvoker *CreateInvoker();
};
extern NativeInterface g_NInvoke;
#endif /* _INCLUDE_SOURCEMOD_NATIVE_INVOKER_H_ */

144
core/NativeOwner.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "NativeOwner.h"
#include "ShareSys.h"
#include "PluginSys.h"
CNativeOwner::CNativeOwner() : m_nMarkSerial(0)
{
}
void CNativeOwner::SetMarkSerial(unsigned int serial)
{
m_nMarkSerial = serial;
}
unsigned int CNativeOwner::GetMarkSerial()
{
return m_nMarkSerial;
}
void CNativeOwner::AddDependent(CPlugin *pPlugin)
{
m_Dependents.push_back(pPlugin);
}
void CNativeOwner::AddWeakRef(const WeakNative & ref)
{
m_WeakRefs.push_back(ref);
}
void CNativeOwner::AddNatives(const sp_nativeinfo_t *natives)
{
NativeEntry *pEntry;
for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++)
{
if ((pEntry = g_ShareSys.AddNativeToCache(this, &natives[i])) == NULL)
{
continue;
}
m_Natives.push_back(pEntry);
}
}
void CNativeOwner::PropogateMarkSerial(unsigned int serial)
{
CNativeOwner *pOwner;
List<CPlugin *>::iterator iter;
for (iter = m_Dependents.begin();
iter != m_Dependents.end();
iter++)
{
pOwner = (*iter);
pOwner->SetMarkSerial(serial);
}
}
void CNativeOwner::UnbindWeakRef(const WeakNative & ref)
{
sp_native_t *native;
IPluginContext *pContext;
pContext = ref.pl->GetBaseContext();
if ((pContext->GetNativeByIndex(ref.idx, &native)) == SP_ERROR_NONE)
{
/* If there is no reference, the native must be unbound */
if (ref.entry == NULL)
{
native->status = SP_NATIVE_UNBOUND;
native->pfn = NULL;
}
/* If we've cached a reference, it's a core native we can
* rebind back to its original (this was a replacement).
*/
else
{
native->pfn = ref.entry->func;
}
}
}
void CNativeOwner::DropEverything()
{
NativeEntry *pEntry;
List<WeakNative>::iterator iter;
List<NativeEntry *>::iterator ntv_iter;
/* Unbind and remove all weak references to us */
iter = m_WeakRefs.begin();
while (iter != m_WeakRefs.end())
{
UnbindWeakRef((*iter));
iter = m_WeakRefs.erase(iter);
}
/* Unmark our replacement natives */
ntv_iter = m_ReplacedNatives.begin();
while (ntv_iter != m_ReplacedNatives.end())
{
pEntry = (*ntv_iter);
pEntry->replacement.func = NULL;
pEntry->replacement.owner = NULL;
ntv_iter = m_ReplacedNatives.erase(ntv_iter);
}
/* Strip all of our natives from the cache */
ntv_iter = m_Natives.begin();
while (ntv_iter != m_Natives.end())
{
g_ShareSys.ClearNativeFromCache(this, (*ntv_iter)->name);
ntv_iter = m_Natives.erase(ntv_iter);
}
}
void CNativeOwner::DropWeakRefsTo(CPlugin *pPlugin)
{
List<WeakNative>::iterator iter;
iter = m_WeakRefs.begin();
while (iter != m_WeakRefs.end())
{
WeakNative & ref = (*iter);
if (ref.pl == pPlugin)
{
iter = m_WeakRefs.erase(iter);
}
else
{
iter++;
}
}
}
void CNativeOwner::DropRefsTo(CPlugin *pPlugin)
{
m_Dependents.remove(pPlugin);
DropWeakRefsTo(pPlugin);
}
void CNativeOwner::AddReplacedNative(NativeEntry *pEntry)
{
m_ReplacedNatives.push_back(pEntry);
}

59
core/NativeOwner.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _INCLUDE_SOURCEMOD_NATIVE_OWNER_H_
#define _INCLUDE_SOURCEMOD_NATIVE_OWNER_H_
#include <sp_vm_types.h>
#include <sh_list.h>
class CPlugin;
struct NativeEntry;
struct WeakNative
{
WeakNative(CPlugin *plugin, uint32_t index) :
pl(plugin), idx(index), entry(NULL)
{
pl = plugin;
idx = index;
}
WeakNative(CPlugin *plugin, uint32_t index, NativeEntry *pEntry) :
pl(plugin), idx(index), entry(pEntry)
{
pl = plugin;
idx = index;
}
CPlugin *pl;
uint32_t idx;
NativeEntry *entry;
};
using namespace SourceHook;
class CNativeOwner
{
public:
CNativeOwner();
public:
virtual void DropEverything();
public:
void AddNatives(const sp_nativeinfo_t *info);
public:
void SetMarkSerial(unsigned int serial);
unsigned int GetMarkSerial();
void PropogateMarkSerial(unsigned int serial);
public:
void AddDependent(CPlugin *pPlugin);
void AddWeakRef(const WeakNative & ref);
void DropRefsTo(CPlugin *pPlugin);
void AddReplacedNative(NativeEntry *pEntry);
private:
void DropWeakRefsTo(CPlugin *pPlugin);
void UnbindWeakRef(const WeakNative & ref);
protected:
List<CPlugin *> m_Dependents;
unsigned int m_nMarkSerial;
List<WeakNative> m_WeakRefs;
List<NativeEntry *> m_Natives;
List<NativeEntry *> m_ReplacedNatives;
};
#endif //_INCLUDE_SOURCEMOD_NATIVE_OWNER_H_

View File

@ -31,14 +31,10 @@
#include "NextMap.h" #include "NextMap.h"
#include "Logger.h" #include "Logger.h"
#include "HalfLife2.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "sourcehook.h" #include "sourcehook.h"
#include "logic_bridge.h" #include "sm_srvcmds.h"
#include "compat_wrappers.h"
#include <time.h>
#include <bridge/include/ILogger.h>
NextMapManager g_NextMap; NextMapManager g_NextMap;
@ -50,14 +46,20 @@ SH_DECL_HOOK4_void(IVEngineServer, ChangeLevel, SH_NOATTRIB, 0, const char *, co
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
#else #elif SOURCE_ENGINE == SE_DARKMESSIAH
SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false); SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
#else
# if SH_IMPL_VERSION >= 4
extern int __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
# else
extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
#endif
extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
#endif #endif
ConCommand *changeLevelCmd = NULL; ConCommand *changeLevelCmd = NULL;
ConVar sm_nextmap("sm_nextmap", "", FCVAR_NOTIFY); ConVar sm_nextmap("sm_nextmap", "", FCVAR_NOTIFY);
ConVar sm_maphistory_size("sm_maphistory_size", "20");
bool g_forcedChange = false; bool g_forcedChange = false;
@ -66,13 +68,13 @@ void NextMapManager::OnSourceModAllInitialized_Post()
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false); SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#else #else
SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false); SH_ADD_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false);
#endif #endif
ConCommand *pCmd = FindCommand("changelevel"); ConCommand *pCmd = FindCommand("changelevel");
if (pCmd != NULL) if (pCmd != NULL)
{ {
SH_ADD_HOOK(ConCommand, Dispatch, pCmd, SH_STATIC(CmdChangeLevelCallback), false); SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CmdChangeLevelCallback, false);
changeLevelCmd = pCmd; changeLevelCmd = pCmd;
} }
} }
@ -82,12 +84,12 @@ void NextMapManager::OnSourceModShutdown()
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false); SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
#else #else
SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false); SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false);
#endif #endif
if (changeLevelCmd != NULL) if (changeLevelCmd != NULL)
{ {
SH_REMOVE_HOOK(ConCommand, Dispatch, changeLevelCmd, SH_STATIC(CmdChangeLevelCallback), false); SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, changeLevelCmd, CmdChangeLevelCallback, false);
} }
SourceHook::List<MapChangeData *>::iterator iter; SourceHook::List<MapChangeData *>::iterator iter;
@ -107,7 +109,7 @@ const char *NextMapManager::GetNextMap()
bool NextMapManager::SetNextMap(const char *map) bool NextMapManager::SetNextMap(const char *map)
{ {
if (!g_HL2.IsMapValid(map)) if (!engine->IsMapValid(map))
{ {
return false; return false;
} }
@ -125,21 +127,21 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const
{ {
if (g_forcedChange) if (g_forcedChange)
{ {
logger->LogMessage("[SM] Changed map to \"%s\"", map); g_Logger.LogMessage("[SM] Changed map to \"%s\"", map);
RETURN_META(MRES_IGNORED); RETURN_META(MRES_IGNORED);
} }
const char *newmap = sm_nextmap.GetString(); const char *newmap = sm_nextmap.GetString();
if (newmap[0] == 0 || !g_HL2.IsMapValid(newmap)) if (newmap[0] == 0 || !engine->IsMapValid(newmap))
{ {
RETURN_META(MRES_IGNORED); RETURN_META(MRES_IGNORED);
} }
logger->LogMessage("[SM] Changed map to \"%s\"", newmap); g_Logger.LogMessage("[SM] Changed map to \"%s\"", newmap);
ke::SafeStrcpy(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), newmap); UTIL_Format(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), newmap);
ke::SafeStrcpy(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "Normal level change"); UTIL_Format(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), "Normal level change");
#if SOURCE_ENGINE != SE_DARKMESSIAH #if SOURCE_ENGINE != SE_DARKMESSIAH
RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::ChangeLevel, (newmap, unknown)); RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::ChangeLevel, (newmap, unknown));
@ -162,36 +164,33 @@ void NextMapManager::OnSourceModLevelChange( const char *mapName )
{ {
/* Something intercepted the mapchange */ /* Something intercepted the mapchange */
char newReason[255]; char newReason[255];
ke::SafeSprintf(newReason, sizeof(newReason), "%s (Map overridden)", m_tempChangeInfo.m_changeReason); UTIL_Format(newReason, sizeof(newReason), "%s (Map overridden)", m_tempChangeInfo.m_changeReason);
m_mapHistory.push_back(new MapChangeData(lastMap, newReason, m_tempChangeInfo.startTime)); m_mapHistory.push_back(new MapChangeData(lastMap, newReason, m_tempChangeInfo.startTime));
} }
int historydiff = sm_maphistory_size.GetInt(); /* TODO: Should this be customizable? */
if (historydiff > 0) if (m_mapHistory.size() > 20)
{ {
historydiff -= m_mapHistory.size(); SourceHook::List<MapChangeData *>::iterator iter;
} else if (historydiff < 0) iter = m_mapHistory.begin();
{
historydiff = (m_mapHistory.size() * -1);
}
for (SourceHook::List<MapChangeData *>::iterator iter = m_mapHistory.begin(); historydiff++ < 0; iter = m_mapHistory.erase(iter))
{
delete (MapChangeData *)*iter; delete (MapChangeData *)*iter;
m_mapHistory.erase(iter);
} }
} }
m_tempChangeInfo.m_mapName[0] ='\0'; m_tempChangeInfo.m_mapName[0] ='\0';
m_tempChangeInfo.m_changeReason[0] = '\0'; m_tempChangeInfo.m_changeReason[0] = '\0';
m_tempChangeInfo.startTime = time(NULL); m_tempChangeInfo.startTime = time(NULL);
ke::SafeStrcpy(lastMap, sizeof(lastMap), mapName); UTIL_Format(lastMap, sizeof(lastMap), mapName);
} }
void NextMapManager::ForceChangeLevel( const char *mapName, const char* changeReason ) void NextMapManager::ForceChangeLevel( const char *mapName, const char* changeReason )
{ {
/* Store the mapname and reason */ /* Store the mapname and reason */
ke::SafeStrcpy(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), mapName); UTIL_Format(m_tempChangeInfo.m_mapName, sizeof(m_tempChangeInfo.m_mapName), mapName);
ke::SafeStrcpy(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), changeReason); UTIL_Format(m_tempChangeInfo.m_changeReason, sizeof(m_tempChangeInfo.m_changeReason), changeReason);
/* Change level and skip our hook */ /* Change level and skip our hook */
g_forcedChange = true; g_forcedChange = true;
@ -221,7 +220,7 @@ void CmdChangeLevelCallback()
if (g_NextMap.m_tempChangeInfo.m_mapName[0] == '\0') if (g_NextMap.m_tempChangeInfo.m_mapName[0] == '\0')
{ {
ke::SafeStrcpy(g_NextMap.m_tempChangeInfo.m_mapName, sizeof(g_NextMap.m_tempChangeInfo.m_mapName), command.Arg(1)); UTIL_Format(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"); UTIL_Format(g_NextMap.m_tempChangeInfo.m_changeReason, sizeof(g_NextMap.m_tempChangeInfo.m_changeReason), "changelevel Command");
} }
} }

View File

@ -33,17 +33,16 @@
#define _INCLUDE_SOURCEMOD_NEXTMAP_H_ #define _INCLUDE_SOURCEMOD_NEXTMAP_H_
#include "sm_globals.h" #include "sm_globals.h"
#include <eiface.h> #include "eiface.h"
#include "sh_list.h" #include "sh_list.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include <amtl/am-string.h>
struct MapChangeData struct MapChangeData
{ {
MapChangeData(const char *mapName, const char *changeReason, time_t time) MapChangeData(const char *mapName, const char *changeReason, time_t time)
{ {
ke::SafeStrcpy(m_mapName, sizeof(m_mapName), mapName); UTIL_Format(m_mapName, sizeof(m_mapName), mapName);
ke::SafeStrcpy(m_changeReason, sizeof(m_changeReason), changeReason); UTIL_Format(m_changeReason, sizeof(m_changeReason), changeReason);
startTime = time; startTime = time;
} }
@ -54,7 +53,7 @@ struct MapChangeData
startTime = 0; startTime = 0;
} }
char m_mapName[PLATFORM_MAX_PATH]; char m_mapName[32];
char m_changeReason[100]; char m_changeReason[100];
time_t startTime; time_t startTime;
}; };

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -29,11 +29,9 @@
* Version: $Id$ * Version: $Id$
*/ */
#include "common_logic.h"
#include "PhraseCollection.h" #include "PhraseCollection.h"
#include "Translator.h" #include "Translator.h"
#include "sprintf.h" #include "sm_stringutil.h"
#include <am-string.h>
CPhraseCollection::CPhraseCollection() CPhraseCollection::CPhraseCollection()
{ {
@ -56,7 +54,7 @@ IPhraseFile *CPhraseCollection::AddPhraseFile(const char *filename)
char full_name[PLATFORM_MAX_PATH]; char full_name[PLATFORM_MAX_PATH];
/* No compat shim here. The user should have read the doc. */ /* No compat shim here. The user should have read the doc. */
ke::SafeSprintf(full_name, sizeof(full_name), "%s.txt", filename); UTIL_Format(full_name, sizeof(full_name), "%s.txt", filename);
fid = g_Translator.FindOrAddPhraseFile(full_name); fid = g_Translator.FindOrAddPhraseFile(full_name);
pFile = g_Translator.GetFileByIndex(fid); pFile = g_Translator.GetFileByIndex(fid);
@ -89,19 +87,6 @@ IPhraseFile *CPhraseCollection::GetFile(unsigned int file)
return m_Files[file]; return m_Files[file];
} }
bool CPhraseCollection::TranslationPhraseExists(const char *key)
{
for (size_t i = 0; i < m_Files.size(); i++)
{
if (m_Files[i]->TranslationPhraseExists(key))
{
return true;
}
}
return false;
}
TransError CPhraseCollection::FindTranslation(const char *key, unsigned int langid, Translation *pTrans) TransError CPhraseCollection::FindTranslation(const char *key, unsigned int langid, Translation *pTrans)
{ {
size_t i; size_t i;

View File

@ -49,7 +49,6 @@ public:
unsigned int GetFileCount(); unsigned int GetFileCount();
IPhraseFile *GetFile(unsigned int file); IPhraseFile *GetFile(unsigned int file);
void Destroy(); void Destroy();
bool TranslationPhraseExists(const char *key);
TransError FindTranslation(const char *key, unsigned int langid, Translation *pTrans); TransError FindTranslation(const char *key, unsigned int langid, Translation *pTrans);
bool FormatString( bool FormatString(
char *buffer, char *buffer,

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
* vim: set ts=4 : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2015 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* ============================================================================= * =============================================================================
* *
* This program is free software; you can redistribute it and/or modify it under * This program is free software; you can redistribute it and/or modify it under
@ -38,20 +38,13 @@
#include <IForwardSys.h> #include <IForwardSys.h>
#include <IPlayerHelpers.h> #include <IPlayerHelpers.h>
#include <IAdminSystem.h> #include <IAdminSystem.h>
#include <ITranslator.h>
#include <sh_string.h> #include <sh_string.h>
#include <sh_list.h> #include <sh_list.h>
#include <sh_vector.h> #include <sh_vector.h>
#include <am-string.h>
#include <am-deque.h>
#include "ConVarManager.h" #include "ConVarManager.h"
#include <steam/steamclientpublic.h>
using namespace SourceHook; using namespace SourceHook;
class IClient;
#define PLAYER_LIFE_UNKNOWN 0 #define PLAYER_LIFE_UNKNOWN 0
#define PLAYER_LIFE_ALIVE 1 #define PLAYER_LIFE_ALIVE 1
#define PLAYER_LIFE_DEAD 2 #define PLAYER_LIFE_DEAD 2
@ -63,7 +56,7 @@ union serial_t
uint32_t value; uint32_t value;
struct struct
{ {
uint32_t index : 8; uint8_t index;
uint32_t serial : 24; uint32_t serial : 24;
} bits; } bits;
}; };
@ -76,98 +69,68 @@ public:
public: public:
const char *GetName(); const char *GetName();
const char *GetIPAddress(); const char *GetIPAddress();
const char *GetAuthString(bool validated = true); const char *GetAuthString();
unsigned int GetSteamAccountID(bool validated = true);
const CSteamID &GetSteamId(bool validated = true);
uint64_t GetSteamId64(bool validated = true) { return GetSteamId(validated).ConvertToUint64(); }
const char *GetSteam2Id(bool validated = true);
const char *GetSteam3Id(bool validated = true);
edict_t *GetEdict(); edict_t *GetEdict();
bool IsInGame(); bool IsInGame();
bool WasCountedAsInGame(); bool WasCountedAsInGame();
bool IsConnected(); bool IsConnected();
bool IsAuthorized(); bool IsAuthorized();
bool IsFakeClient(); bool IsFakeClient();
bool IsSourceTV() const;
bool IsReplay() const;
void SetAdminId(AdminId id, bool temporary); void SetAdminId(AdminId id, bool temporary);
AdminId GetAdminId(); AdminId GetAdminId();
void Kick(const char *str); void Kick(const char *str);
bool IsInKickQueue(); bool IsInKickQueue();
IPlayerInfo *GetPlayerInfo(); IPlayerInfo *GetPlayerInfo();
unsigned int GetLanguageId(); unsigned int GetLanguageId();
void SetLanguageId(unsigned int id);
int GetUserId(); int GetUserId();
bool RunAdminCacheChecks(); bool RunAdminCacheChecks();
void NotifyPostAdminChecks(); void NotifyPostAdminChecks();
unsigned int GetSerial(); unsigned int GetSerial();
int GetIndex() const;
void PrintToConsole(const char *pMsg);
void ClearAdmin();
public: public:
void DoBasicAdminChecks(); void DoBasicAdminChecks();
void MarkAsBeingKicked(); void MarkAsBeingKicked();
int GetLifeState(); int GetLifeState();
// This can be NULL for fakeclients due to limitations in our impl
IClient *GetIClient() const;
private: private:
void Initialize(const char *name, const char *ip, edict_t *pEntity); void Initialize(const char *name, const char *ip, edict_t *pEntity);
void Connect(); void Connect();
void Disconnect(); void Disconnect();
void SetName(const char *name); void SetName(const char *name);
void DumpAdmin(bool deleting); void DumpAdmin(bool deleting);
void UpdateAuthIds(); void Authorize(const char *auth);
void Authorize();
void Authorize_Post(); void Authorize_Post();
void DoPostConnectAuthorization(); void DoPostConnectAuthorization();
bool IsAuthStringValidated();
bool SetEngineString();
bool SetCSteamID();
void ClearNetchannelQueue(void);
private: private:
bool m_IsConnected = false; bool m_IsConnected;
bool m_IsInGame = false; bool m_IsInGame;
bool m_IsAuthorized = false; bool m_IsAuthorized;
bool m_bIsInKickQueue = false; bool m_bIsInKickQueue;
String m_Name; String m_Name;
String m_Ip; String m_Ip;
String m_IpNoPort; String m_IpNoPort;
ke::AString m_AuthID; String m_AuthID;
ke::AString m_Steam2Id; AdminId m_Admin;
ke::AString m_Steam3Id; bool m_TempAdmin;
AdminId m_Admin = INVALID_ADMIN_ID; edict_t *m_pEdict;
bool m_TempAdmin = false; IPlayerInfo *m_Info;
edict_t *m_pEdict = nullptr;
IPlayerInfo *m_Info = nullptr;
IClient *m_pIClient = nullptr;
String m_LastPassword; String m_LastPassword;
bool m_bAdminCheckSignalled = false; bool m_bAdminCheckSignalled;
int m_iIndex; int m_iIndex;
unsigned int m_LangId = SOURCEMOD_LANGUAGE_ENGLISH; unsigned int m_LangId;
int m_UserId = -1; int m_UserId;
bool m_bFakeClient = false; bool m_bFakeClient;
bool m_bIsSourceTV = false;
bool m_bIsReplay = false;
serial_t m_Serial; serial_t m_Serial;
CSteamID m_SteamId;
#if SOURCE_ENGINE == SE_CSGO
QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie;
#endif
ke::Deque<ke::AString> m_PrintfBuffer;
}; };
class PlayerManager : class PlayerManager :
public SMGlobalClass, public SMGlobalClass,
public IPlayerManager, public IPlayerManager,
public IGameEventListener2 public IConVarChangeListener
{ {
friend class CPlayer; friend class CPlayer;
public: public:
PlayerManager(); PlayerManager();
~PlayerManager(); ~PlayerManager();
public: //SMGlobalClass public: //SMGlobalClass
void OnSourceModStartup(bool late) override;
void OnSourceModAllInitialized(); void OnSourceModAllInitialized();
void OnSourceModShutdown(); void OnSourceModShutdown();
void OnSourceModLevelEnd(); void OnSourceModLevelEnd();
@ -176,6 +139,8 @@ public: //SMGlobalClass
public: public:
CPlayer *GetPlayerByIndex(int client) const; CPlayer *GetPlayerByIndex(int client) const;
void RunAuthChecks(); void RunAuthChecks();
void ClearAdminId(AdminId id);
void ClearAllAdmins();
public: public:
bool OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); bool OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen);
bool OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); bool OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen);
@ -184,18 +149,11 @@ public:
void OnClientDisconnect_Post(edict_t *pEntity); void OnClientDisconnect_Post(edict_t *pEntity);
#if SOURCE_ENGINE >= SE_ORANGEBOX #if SOURCE_ENGINE >= SE_ORANGEBOX
void OnClientCommand(edict_t *pEntity, const CCommand &args); void OnClientCommand(edict_t *pEntity, const CCommand &args);
#if SOURCE_ENGINE >= SE_EYE
void OnClientCommandKeyValues(edict_t *pEntity, KeyValues *pCommand);
void OnClientCommandKeyValues_Post(edict_t *pEntity, KeyValues *pCommand);
#endif
#else #else
void OnClientCommand(edict_t *pEntity); void OnClientCommand(edict_t *pEntity);
#endif #endif
void OnClientSettingsChanged(edict_t *pEntity); void OnClientSettingsChanged(edict_t *pEntity);
//void OnClientSettingsChanged_Pre(edict_t *pEntity); //void OnClientSettingsChanged_Pre(edict_t *pEntity);
void OnServerHibernationUpdate(bool bHibernating);
void OnClientPrintf(edict_t *pEdict, const char *szMsg);
void OnPrintfFrameAction(unsigned int serial);
public: //IPlayerManager public: //IPlayerManager
void AddClientListener(IClientListener *listener); void AddClientListener(IClientListener *listener);
void RemoveClientListener(IClientListener *listener); void RemoveClientListener(IClientListener *listener);
@ -203,7 +161,6 @@ public: //IPlayerManager
IGamePlayer *GetGamePlayer(edict_t *pEdict); IGamePlayer *GetGamePlayer(edict_t *pEdict);
int GetMaxClients(); int GetMaxClients();
int GetNumPlayers(); int GetNumPlayers();
int GetNumClients();
int GetClientOfUserId(int userid); int GetClientOfUserId(int userid);
bool IsServerActivated(); bool IsServerActivated();
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags); int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
@ -212,10 +169,8 @@ public: //IPlayerManager
void UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler); void UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler);
void ProcessCommandTarget(cmd_target_info_t *info); void ProcessCommandTarget(cmd_target_info_t *info);
int GetClientFromSerial(unsigned int serial); int GetClientFromSerial(unsigned int serial);
void ClearAdminId(AdminId id); public: // IConVarChangeListener
void RecheckAnyAdmins(); void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
public: // IGameEventListener2
void FireGameEvent(IGameEvent *pEvent);
public: public:
inline int MaxClients() inline int MaxClients()
{ {
@ -232,29 +187,20 @@ public:
bool CheckSetAdmin(int index, CPlayer *pPlayer, AdminId id); bool CheckSetAdmin(int index, CPlayer *pPlayer, AdminId id);
bool CheckSetAdminName(int index, CPlayer *pPlayer, AdminId id); bool CheckSetAdminName(int index, CPlayer *pPlayer, AdminId id);
const char *GetPassInfoVar(); const char *GetPassInfoVar();
void RecheckAnyAdmins();
unsigned int GetReplyTo(); unsigned int GetReplyTo();
unsigned int SetReplyTo(unsigned int reply); unsigned int SetReplyTo(unsigned int reply);
void MaxPlayersChanged(int newvalue = -1); void MaxPlayersChanged(int newvalue = -1);
inline bool InClientCommandKeyValuesHook()
{
return m_bInCCKVHook;
}
#if SOURCE_ENGINE == SE_CSGO
bool HandleConVarQuery(QueryCvarCookie_t cookie, int client, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue);
#endif
private: private:
void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax); void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax);
void InvalidatePlayer(CPlayer *pPlayer); void InvalidatePlayer(CPlayer *pPlayer);
private: private:
List<IClientListener *> m_hooks; List<IClientListener *> m_hooks;
IForward *m_clconnect; IForward *m_clconnect;
IForward *m_clconnect_post;
IForward *m_cldisconnect; IForward *m_cldisconnect;
IForward *m_cldisconnect_post; IForward *m_cldisconnect_post;
IForward *m_clputinserver; IForward *m_clputinserver;
IForward *m_clcommand; IForward *m_clcommand;
IForward *m_clcommandkv;
IForward *m_clcommandkv_post;
IForward *m_clinfochanged; IForward *m_clinfochanged;
IForward *m_clauth; IForward *m_clauth;
IForward *m_onActivate; IForward *m_onActivate;
@ -263,23 +209,12 @@ private:
int *m_UserIdLookUp; int *m_UserIdLookUp;
int m_maxClients; int m_maxClients;
int m_PlayerCount; int m_PlayerCount;
int m_PlayersSinceActive; bool m_FirstPass;
bool m_bServerActivated;
unsigned int *m_AuthQueue; unsigned int *m_AuthQueue;
String m_PassInfoVar; String m_PassInfoVar;
bool m_QueryLang; bool m_QueryLang;
bool m_bAuthstringValidation; // are we validating admins with steam before authorizing?
bool m_bIsListenServer; bool m_bIsListenServer;
int m_ListenClient; int m_ListenClient;
bool m_bIsSourceTVActive;
bool m_bIsReplayActive;
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 #if SOURCE_ENGINE >= SE_ORANGEBOX

349
core/PluginInfoDatabase.cpp Normal file
View File

@ -0,0 +1,349 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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 <stdarg.h>
#include <string.h>
#include "PluginInfoDatabase.h"
#include "PluginSys.h"
void PluginSettings::Init()
{
name = -1;
pause_val = false;
type_val = PluginType_MapUpdated;
optarray = -1;
opts_num = 0;
opts_size = 0;
blockload_val = false;
}
/**
* :TODO: write the logger, make these errors log instead of being static
* NOTE: once we do that, we will have to change some code to ignore sections
*/
CPluginInfoDatabase::CPluginInfoDatabase()
{
m_strtab = NULL;
m_infodb_count = 0;
m_infodb_size = 0;
m_infodb = -1;
}
CPluginInfoDatabase::~CPluginInfoDatabase()
{
delete m_strtab;
}
void CPluginInfoDatabase::ReadSMC_ParseStart()
{
/* Create or reset our string table */
if (m_strtab)
{
m_strtab->Reset();
} else {
m_strtab = new BaseStringTable(1024);
}
/* Set our internal states to the beginning */
in_plugins = false;
cur_plugin = -1;
in_options = false;
m_infodb_size = 0;
m_infodb_count = 0;
m_infodb = -1;
}
SMCResult CPluginInfoDatabase::MakeError(const char *fmt, ...)
{
char buffer[512];
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
m_errmsg = m_strtab->AddString(buffer);
return SMCResult_HaltFail;
}
unsigned int CPluginInfoDatabase::GetSettingsNum()
{
return m_infodb_count;
}
PluginSettings *CPluginInfoDatabase::GetSettingsIfMatch(unsigned int index, const char *filename)
{
BaseMemTable *memtab = m_strtab->GetMemTable();
int *table = (int *)memtab->GetAddress(m_infodb);
if (!table || index >= m_infodb_count)
{
return NULL;
}
PluginSettings *plugin = (PluginSettings *)memtab->GetAddress(table[index]);
const char *name = m_strtab->GetString(plugin->name);
if (!name)
{
return NULL;
}
if (!g_PluginSys.TestAliasMatch(name, filename))
{
return NULL;
}
return plugin;
}
void CPluginInfoDatabase::GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val)
{
PluginOpts *table = (PluginOpts *)m_strtab->GetMemTable()->GetAddress(settings->optarray);
if (!table)
{
*key = NULL;
*val = NULL;
return;
}
if (opt_num >= settings->opts_num)
{
*key = NULL;
*val = NULL;
return;
}
*key = m_strtab->GetString(table[opt_num].key);
*val = m_strtab->GetString(table[opt_num].val);
}
SMCResult CPluginInfoDatabase::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
{
if (cur_plugin != -1)
{
PluginSettings *plugin = (PluginSettings *)m_strtab->GetMemTable()->GetAddress(cur_plugin);
if (!in_options)
{
if (strcmp(key, "pause") == 0)
{
if (strcasecmp(value, "yes") == 0)
{
plugin->pause_val = true;
}
else
{
plugin->pause_val = false;
}
}
else if (strcmp(key, "lifetime") == 0)
{
if (strcasecmp(value, "private") == 0)
{
plugin->type_val = PluginType_Private;
}
else if (strcasecmp(value, "mapsync") == 0)
{
plugin->type_val = PluginType_MapUpdated;
}
else if (strcasecmp(value, "maponly") == 0)
{
plugin->type_val = PluginType_MapOnly;
}
else if (strcasecmp(value, "global") == 0)
{
plugin->type_val = PluginType_Global;
}
else
{
return MakeError("Unknown value for key \"lifetime\": \"%s\"", value);
}
}
else if (strcmp(key, "blockload") == 0)
{
plugin->blockload_val = true;
}
else
{
return MakeError("Unknown property key: \"%s\"", key);
}
}
else
{
/* Cache every option, valid or not */
int keyidx = m_strtab->AddString(key);
int validx = m_strtab->AddString(value);
PluginOpts *table;
BaseMemTable *memtab = m_strtab->GetMemTable();
plugin = (PluginSettings *)memtab->GetAddress(cur_plugin);
if (plugin->opts_num + 1 > plugin->opts_size)
{
unsigned int oldsize = plugin->opts_size;
if (oldsize == 0)
{
//right now we don't have many
plugin->opts_size = 2;
}
else
{
plugin->opts_size *= 2;
}
int newidx = memtab->CreateMem(plugin->opts_size * sizeof(PluginOpts), (void **)&table);
/* in case it resized */
plugin = (PluginSettings *)memtab->GetAddress(cur_plugin);
if (plugin->optarray != -1)
{
void *oldtable = memtab->GetAddress(plugin->optarray);
memcpy(table, oldtable, oldsize * sizeof(PluginOpts));
}
plugin->optarray = newidx;
}
else
{
table = (PluginOpts *)memtab->GetAddress(plugin->optarray);
}
PluginOpts *opt = &table[plugin->opts_num++];
opt->key = keyidx;
opt->val = validx;
}
}
else if (in_plugins)
{
return MakeError("Unknown property key: \"%s\"", key);
}
else
{
/* Ignore anything we don't know about! */
}
return SMCResult_Continue;
}
SMCResult CPluginInfoDatabase::ReadSMC_LeavingSection(const SMCStates *states)
{
if (in_plugins)
{
if (cur_plugin != -1)
{
if (in_options)
{
in_options = false;
}
else
{
/* If the plugin is ending, add it to the table */
BaseMemTable *memtab = m_strtab->GetMemTable();
int *table;
if (m_infodb_count + 1 > m_infodb_size)
{
unsigned int oldsize = m_infodb_size;
if (!m_infodb_size)
{
m_infodb_size = 8;
}
else
{
m_infodb_size *= 2;
}
int newidx = memtab->CreateMem(m_infodb_size * sizeof(int), (void **)&table);
if (m_infodb != -1)
{
void *oldtable = (int *)memtab->GetAddress(m_infodb);
memcpy(table, oldtable, oldsize * sizeof(int));
}
m_infodb = newidx;
}
else
{
table = (int *)memtab->GetAddress(m_infodb);
}
/* Assign to table and scrap the current plugin */
table[m_infodb_count++] = cur_plugin;
cur_plugin = -1;
}
}
else
{
in_plugins = false;
}
}
return SMCResult_Continue;
}
SMCResult CPluginInfoDatabase::ReadSMC_NewSection(const SMCStates *states, const char *name)
{
if (!in_plugins)
{
/* If we're not in the main Plugins section, and we don't get it for the name, error out */
if (strcmp(name, "Plugins") != 0)
{
return MakeError("Unknown root section: \"%s\"", name);
}
else
{
/* Otherwise set our states */
in_plugins = true;
cur_plugin = -1;
in_options = false;
}
}
else
{
if (cur_plugin == -1)
{
/* If we get a plugin node and we don't have a current plugin, create a new one */
PluginSettings *plugin;
int i_name = m_strtab->AddString(name);
cur_plugin = m_strtab->GetMemTable()->CreateMem(sizeof(PluginSettings), (void **)&plugin);
plugin->Init();
plugin->name = i_name;
in_options = false;
}
else
{
if (!in_options && strcmp(name, "Options") == 0)
{
in_options = true;
}
else
{
return MakeError("Unknown plugin sub-section: \"%s\"", name);
}
}
}
return SMCResult_Continue;
}

106
core/PluginInfoDatabase.h Normal file
View File

@ -0,0 +1,106 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_
#define _INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_
/**
* This file parses plugin_settings.cfg and stores the information in cached memory.
* It provides simplistic abstraction to retrieving the info.
* :TODO: currently untested
*/
#include "sm_memtable.h"
#include "ITextParsers.h"
#include "IPluginSys.h"
#include "sm_globals.h"
struct PluginOpts
{
int key;
int val;
};
struct PluginSettings
{
void Init();
int name;
bool pause_val;
PluginType type_val;
int optarray;
size_t opts_num;
size_t opts_size;
bool blockload_val;
};
class CPluginInfoDatabase : public ITextListener_SMC
{
public:
CPluginInfoDatabase();
~CPluginInfoDatabase();
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);
public:
/**
* Returns the number of plugin settings available.
*/
unsigned int GetSettingsNum();
/**
* Given an index, returns the plugin settings block if the filename matches.
* Otherwise, returns NULL.
*/
PluginSettings *GetSettingsIfMatch(unsigned int index, const char *filename);
/**
* Given a plugin settings struct and an index,
* returns the given JIT key/value option pair at that index.
* If the input is invalid, key and val will be set to NULL.
*/
void GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val);
private:
SMCResult MakeError(const char *fmt, ...);
private:
BaseStringTable *m_strtab;
int m_errmsg;
bool in_plugins;
bool in_options;
int m_infodb;
size_t m_infodb_count;
size_t m_infodb_size;
int cur_plugin;
};
#endif //_INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_

2746
core/PluginSys.cpp Normal file

File diff suppressed because it is too large Load Diff

475
core/PluginSys.h Normal file
View File

@ -0,0 +1,475 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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_SOURCEMOD_PLUGINSYSTEM_H_
#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <IPluginSys.h>
#include <IHandleSys.h>
#include <IForwardSys.h>
#include <sh_list.h>
#include <sh_stack.h>
#include <sh_vector.h>
#include <sh_string.h>
#include "sm_globals.h"
#include "PluginInfoDatabase.h"
#include "sm_trie.h"
#include "sourcemod.h"
#include <IRootConsoleMenu.h>
#if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
#include "convar_sm_l4d.h"
#elif (SOURCE_ENGINE == SE_ORANGEBOX) || (SOURCE_ENGINE == SE_ORANGEBOXVALVE)
#include "convar_sm_ob.h"
#else
#include "convar_sm.h"
#endif
#include "ITranslator.h"
#include "NativeOwner.h"
#include "ShareSys.h"
class CPlayer;
using namespace SourceHook;
/**
* NOTES:
*
* UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall
* picture of how PluginSys works, but things like dependencies and fake natives have
* complicated things quite a bit.
*
* Currently this system needs a lot of work but it's good skeletally. Plugin creation
* is done without actually compiling anything. This is done by Load functions in the
* manager. This will need a rewrite when we add context switching.
*
* The plugin object itself has a few things to note. The most important is that it stores
* a table of function objects. The manager marshals allocation and freeing of these objects.
* The plugin object can be in erroneous states, they are:
* Plugin_Error --> Some error occurred any time during or after compilation.
* This error can be cleared since the plugin itself is valid.
* However, the state itself being set prevents any runtime action.
* Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it.
*
* If a plugin fails to load externally, it is never added to the internal tracker. However,
* plugins that failed to load from the internal loading mechanism are always tracked. This
* allows users to see which automatically loaded plugins failed, and makes the interface a bit
* more flexible.
*
* Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid
* for execution. SourceMod is a two pass system, and even though the second pass is not implemented
* yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When
* implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
*
* The two pass loading mechanism is described below. Modules/natives are not implemented yet.
* PASS ONE: All loadable plugins are found and have the following steps performed:
* 1. Loading and compilation is attempted.
* 2. If successful, all natives from Core are added.
* 3. OnPluginLoad() is called.
* 4. If failed, any user natives are scrapped and the process halts here.
* 5. If successful, the plugin is ready for Pass 2.
* INTERMEDIATE:
* 1. All forced modules are loaded.
* PASS TWO: All loaded plugins are found and have these steps performed:
* 1. Any modules referenced in the plugin that are not already loaded, are loaded.
* 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
* 3. If any natives are unresolved, check if they are found in the user-natives pool.
* 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter.
* 5. If the filter fails, the plugin is marked as failed.
* 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
* Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
* PASS THREE (not a real pass):
* 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
*/
enum LoadRes
{
LoadRes_Successful,
LoadRes_AlreadyLoaded,
LoadRes_Failure,
LoadRes_SilentFailure,
LoadRes_NeverLoad
};
enum APLRes
{
APLRes_Success,
APLRes_Failure,
APLRes_SilentFailure
};
struct AutoConfig
{
String autocfg;
String folder;
bool create;
};
class CPlugin;
class CPlugin :
public IPlugin,
public CNativeOwner
{
friend class CPluginManager;
friend class CFunction;
public:
CPlugin(const char *file);
~CPlugin();
public:
PluginType GetType();
SourcePawn::IPluginContext *GetBaseContext();
sp_context_t *GetContext();
void *GetPluginStructure();
const char *GetFilename();
bool IsDebugging();
PluginStatus GetStatus();
bool IsSilentlyFailed();
void SetSilentlyFailed(bool sf);
const sm_plugininfo_t *GetPublicInfo();
bool SetPauseState(bool paused);
unsigned int GetSerial();
IdentityToken_t *GetIdentity();
unsigned int CalcMemUsage();
bool SetProperty(const char *prop, void *ptr);
bool GetProperty(const char *prop, void **ptr, bool remove=false);
void DropEverything();
SourcePawn::IPluginRuntime *GetRuntime();
public:
/**
* Creates a plugin object with default values.
* If an error buffer is specified, and an error occurs, the error will be copied to the buffer
* and NULL will be returned.
* If an error buffer is not specified, the error will be copied to an internal buffer and
* a valid (but error-stated) CPlugin will be returned.
*/
static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
public:
/**
* Sets an error state on the plugin
*/
void SetErrorState(PluginStatus status, const char *error_fmt, ...);
/**
* Initializes the plugin's identity information
*/
void InitIdentity();
/**
* Calls the OnPluginLoad function, and sets any failed states if necessary.
* NOTE: Valid pre-states are: Plugin_Created
* NOTE: If validated, plugin state is changed to Plugin_Loaded
*
* If the error buffer is NULL, the error message is cached locally.
*/
APLRes Call_AskPluginLoad(char *error, size_t maxlength);
/**
* Calls the OnPluginStart function.
* NOTE: Valid pre-states are: Plugin_Created
* NOTE: Post-state will be Plugin_Running
*/
void Call_OnPluginStart();
/**
* Calls the OnPluginEnd function.
*/
void Call_OnPluginEnd();
/**
* Calls the OnAllPluginsLoaded function.
*/
void Call_OnAllPluginsLoaded();
/**
* Returns true if a plugin is usable.
*/
bool IsRunnable();
/**
* Get languages info.
*/
IPhraseCollection *GetPhrases();
public:
/**
* Returns the modification time during last plugin load.
*/
time_t GetTimeStamp();
/**
* Returns the current modification time of the plugin file.
*/
time_t GetFileTimeStamp();
/**
* Returns true if the plugin was running, but is now invalid.
*/
bool WasRunning();
Handle_t GetMyHandle();
bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
void AddConfig(bool autoCreate, const char *cfg, const char *folder);
unsigned int GetConfigCount();
AutoConfig *GetConfig(unsigned int i);
inline void AddLibrary(const char *name)
{
m_Libraries.push_back(name);
}
void LibraryActions(bool dropping);
void SyncMaxClients(int max_clients);
protected:
bool UpdateInfo();
void SetTimeStamp(time_t t);
void DependencyDropped(CPlugin *pOwner);
private:
PluginType m_type;
char m_filename[PLATFORM_MAX_PATH];
PluginStatus m_status;
bool m_bSilentlyFailed;
unsigned int m_serial;
sm_plugininfo_t m_info;
char m_errormsg[256];
time_t m_LastAccess;
IdentityToken_t *m_ident;
Handle_t m_handle;
bool m_WasRunning;
IPhraseCollection *m_pPhrases;
List<String> m_RequiredLibs;
List<String> m_Libraries;
Trie *m_pProps;
bool m_FakeNativesMissing;
bool m_LibraryMissing;
CVector<AutoConfig *> m_configs;
bool m_bGotAllLoaded;
int m_FileVersion;
char m_DateTime[256];
IPluginRuntime *m_pRuntime;
IPluginContext *m_pContext;
sp_pubvar_t *m_MaxClientsVar;
};
class CPluginManager :
public IPluginManager,
public SMGlobalClass,
public IHandleTypeDispatch,
public IRootConsoleCommand
{
friend class CPlugin;
public:
CPluginManager();
~CPluginManager();
public:
/* Implements iterator class */
class CPluginIterator : public IPluginIterator
{
public:
CPluginIterator(List<CPlugin *> *mylist);
virtual ~CPluginIterator();
virtual bool MorePlugins();
virtual IPlugin *GetPlugin();
virtual void NextPlugin();
void Release();
public:
void Reset();
private:
List<CPlugin *> *mylist;
List<CPlugin *>::iterator current;
};
friend class CPluginManager::CPluginIterator;
public: //IPluginManager
IPlugin *LoadPlugin(const char *path,
bool debug,
PluginType type,
char error[],
size_t maxlength,
bool *wasloaded);
bool UnloadPlugin(IPlugin *plugin);
IPlugin *FindPluginByContext(const sp_context_t *ctx);
unsigned int GetPluginCount();
IPluginIterator *GetPluginIterator();
void AddPluginsListener(IPluginsListener *listener);
void RemovePluginsListener(IPluginsListener *listener);
public: //SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModShutdown();
void OnSourceModMaxPlayersChanged(int newvalue);
public: //IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object);
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
public: //IRootConsoleCommand
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
public:
/**
* Loads all plugins not yet loaded
*/
void LoadAll_FirstPass(const char *config, const char *basedir);
/**
* Runs the second loading pass for all plugins
*/
void LoadAll_SecondPass();
/**
* Tests a plugin file mask against a local folder.
* The alias is searched backwards from localdir - i.e., given this input:
* csdm/ban csdm/ban
* ban csdm/ban
* csdm/ban optional/csdm/ban
* All of these will return true for an alias match.
* Wildcards are allowed in the filename.
*/
bool TestAliasMatch(const char *alias, const char *localdir);
/**
* Returns whether anything loaded will be a late load.
*/
bool IsLateLoadTime() const;
/**
* Converts a Handle to an IPlugin if possible.
*/
IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
/**
* Finds a plugin based on its index. (starts on index 1)
*/
CPlugin *GetPluginByOrder(int num);
int GetOrderOfPlugin(IPlugin *pl);
/**
* Internal version of FindPluginByContext()
*/
CPlugin *GetPluginByCtx(const sp_context_t *ctx);
/**
* Gets status text for a status code
*/
const char *GetStatusText(PluginStatus status);
/**
* Reload or update plugins on level shutdown.
*/
void ReloadOrUnloadPlugins();
/**
* Add public functions from all running or paused
* plugins to the specified forward if the names match.
*/
void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
/**
* Iterates through plugins to call OnAllPluginsLoaded.
*/
void AllPluginsLoaded();
CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
void Shutdown();
void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop);
bool LibraryExists(const char *lib);
bool ReloadPlugin(CPlugin *pl);
void UnloadAll();
CPlugin *FindPluginByConsoleArg(const char *arg);
void SyncMaxClients(int max_clients);
void ListPluginsToClient(CPlayer *player, const CCommand &args);
private:
LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength);
void LoadAutoPlugin(const char *plugin);
/**
* Recursively loads all plugins in the given directory.
*/
void LoadPluginsFromDir(const char *basedir, const char *localdir);
/**
* Adds a plugin object. This is wrapped by LoadPlugin functions.
*/
void AddPlugin(CPlugin *pPlugin);
/**
* Runs the second loading pass on a plugin.
*/
bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
/**
* Runs an extension pass on a plugin.
*/
bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
/**
* Manages required natives.
*/
bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength);
void _SetPauseState(CPlugin *pPlugin, bool pause);
protected:
/**
* Caching internal objects
*/
void ReleaseIterator(CPluginIterator *iter);
public:
inline IdentityToken_t *GetIdentity()
{
return m_MyIdent;
}
private:
void TryRefreshDependencies(CPlugin *pOther);
private:
List<IPluginsListener *> m_listeners;
List<CPlugin *> m_plugins;
CStack<CPluginManager::CPluginIterator *> m_iters;
CPluginInfoDatabase m_PluginInfo;
Trie *m_LoadLookup;
bool m_AllPluginsLoaded;
IdentityToken_t *m_MyIdent;
/* Dynamic native stuff */
List<FakeNative *> m_Natives;
bool m_LoadingLocked;
};
extern CPluginManager g_PluginSys;
#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_

611
core/ShareSys.cpp Normal file
View File

@ -0,0 +1,611 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 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 "ShareSys.h"
#include "HandleSys.h"
#include "ExtensionSys.h"
#include "LibrarySys.h"
#include "PluginSys.h"
#include "sm_stringutil.h"
ShareSystem g_ShareSys;
static unsigned int g_mark_serial = 0;
ShareSystem::ShareSystem()
{
m_IdentRoot.ident = 0;
m_TypeRoot = 0;
m_IfaceType = 0;
m_CoreType = 0;
}
IdentityToken_t *ShareSystem::CreateCoreIdentity()
{
if (!m_CoreType)
{
m_CoreType = CreateIdentType("CORE");
}
return CreateIdentity(m_CoreType, this);
}
void ShareSystem::Initialize()
{
TypeAccess sec;
g_HandleSys.InitAccessDefaults(&sec, NULL);
sec.ident = GetIdentRoot();
m_TypeRoot = g_HandleSys.CreateType("Identity", this, 0, &sec, NULL, NULL, NULL);
m_IfaceType = g_HandleSys.CreateType("Interface", this, 0, NULL, NULL, GetIdentRoot(), NULL);
/* Initialize our static identity handle */
m_IdentRoot.ident = g_HandleSys.CreateHandle(m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL);
/* Add the Handle System and others... they are too innocent and pure to do it themselves */
AddInterface(NULL, &g_HandleSys);
AddInterface(NULL, &g_LibSys);
}
void ShareSystem::OnSourceModShutdown()
{
if (m_CoreType)
{
g_HandleSys.RemoveType(m_CoreType, GetIdentRoot());
}
g_HandleSys.RemoveType(m_IfaceType, GetIdentRoot());
g_HandleSys.RemoveType(m_TypeRoot, GetIdentRoot());
}
IdentityType_t ShareSystem::FindIdentType(const char *name)
{
HandleType_t type;
if (g_HandleSys.FindHandleType(name, &type))
{
if (g_HandleSys.TypeCheck(type, m_TypeRoot))
{
return type;
}
}
return 0;
}
IdentityType_t ShareSystem::CreateIdentType(const char *name)
{
if (!m_TypeRoot)
{
return 0;
}
return g_HandleSys.CreateType(name, this, m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL);
}
void ShareSystem::OnHandleDestroy(HandleType_t type, void *object)
{
/* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */
}
IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr)
{
if (!m_TypeRoot)
{
return 0;
}
/* :TODO: Cache? */
IdentityToken_t *pToken = new IdentityToken_t;
HandleSecurity sec;
sec.pOwner = sec.pIdentity = GetIdentRoot();
pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true);
pToken->ptr = ptr;
pToken->type = type;
return pToken;
}
bool ShareSystem::AddInterface(IExtension *myself, SMInterface *iface)
{
if (!iface)
{
return false;
}
IfaceInfo info;
info.owner = myself;
info.iface = iface;
m_Interfaces.push_back(info);
return true;
}
bool ShareSystem::RequestInterface(const char *iface_name,
unsigned int iface_vers,
IExtension *myself,
SMInterface **pIface)
{
/* See if the interface exists */
List<IfaceInfo>::iterator iter;
SMInterface *iface;
IExtension *iface_owner;
bool found = false;
for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++)
{
IfaceInfo &info = (*iter);
iface = info.iface;
if (strcmp(iface->GetInterfaceName(), iface_name) == 0)
{
if (iface->GetInterfaceVersion() == iface_vers
|| iface->IsVersionCompatible(iface_vers))
{
iface_owner = info.owner;
found = true;
break;
}
}
}
if (!found)
{
return false;
}
/* Add a dependency node */
if (iface_owner)
{
IfaceInfo info;
info.iface = iface;
info.owner = iface_owner;
g_Extensions.BindDependency(myself, &info);
}
if (pIface)
{
*pIface = iface;
}
return true;
}
void ShareSystem::AddNatives(IExtension *myself, const sp_nativeinfo_t *natives)
{
CNativeOwner *pOwner;
pOwner = g_Extensions.GetNativeOwner(myself);
pOwner->AddNatives(natives);
}
void ShareSystem::DestroyIdentity(IdentityToken_t *identity)
{
HandleSecurity sec;
sec.pOwner = GetIdentRoot();
sec.pIdentity = GetIdentRoot();
g_HandleSys.FreeHandle(identity->ident, &sec);
delete identity;
}
void ShareSystem::DestroyIdentType(IdentityType_t type)
{
g_HandleSys.RemoveType(type, GetIdentRoot());
}
void ShareSystem::RemoveInterfaces(IExtension *pExtension)
{
List<IfaceInfo>::iterator iter = m_Interfaces.begin();
while (iter != m_Interfaces.end())
{
if ((*iter).owner == pExtension)
{
iter = m_Interfaces.erase(iter);
}
else
{
iter++;
}
}
}
void ShareSystem::AddDependency(IExtension *myself, const char *filename, bool require, bool autoload)
{
g_Extensions.AddDependency(myself, filename, require, autoload);
}
void ShareSystem::RegisterLibrary(IExtension *myself, const char *name)
{
g_Extensions.AddLibrary(myself, name);
}
void ShareSystem::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives)
{
unsigned int i;
NativeEntry *pEntry;
CNativeOwner *pOwner;
pOwner = g_Extensions.GetNativeOwner(myself);
for (i = 0; natives[i].func != NULL && natives[i].name != NULL; i++)
{
if ((pEntry = FindNative(natives[i].name)) == NULL)
{
continue;
}
if (pEntry->owner != g_pCoreNatives)
{
continue;
}
if (pEntry->replacement.owner != NULL)
{
continue;
}
/* Now it's safe to add the override */
pEntry->replacement.func = natives[i].func;
pEntry->replacement.owner = pOwner;
pOwner->AddReplacedNative(pEntry);
}
}
NativeEntry *ShareSystem::FindNative(const char *name)
{
NativeEntry **ppEntry;
if ((ppEntry = m_NtvCache.retrieve(name)) == NULL)
{
return NULL;
}
return *ppEntry;
}
void ShareSystem::BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly)
{
NativeEntry *pEntry;
sp_native_t *native;
uint32_t i, native_count;
IPluginContext *pContext;
pContext = pPlugin->GetBaseContext();
/* Generate a new serial ID, mark our dependencies with it. */
g_mark_serial++;
pPlugin->PropogateMarkSerial(g_mark_serial);
native_count = pContext->GetNativesNum();
for (i = 0; i < native_count; i++)
{
if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
{
continue;
}
/* If we're bound, check if there is a replacement available.
* If not, this native is totally finalized.
*/
if (native->status == SP_NATIVE_BOUND)
{
pEntry = (NativeEntry *)native->user;
assert(pEntry != NULL);
if (pEntry->replacement.owner == NULL
|| (pEntry->replacement.owner != NULL
&& pEntry->replacement.func == native->pfn))
{
continue;
}
}
/* Otherwise, the native must be in our cache. */
else if ((pEntry = FindNative(native->name)) == NULL)
{
continue;
}
if (bCoreOnly && pEntry->owner != g_pCoreNatives)
{
continue;
}
BindNativeToPlugin(pPlugin, native, i, pEntry);
}
}
void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry)
{
uint32_t i;
sp_native_t *native;
IPluginContext *pContext;
pContext = pPlugin->GetBaseContext();
if (pContext->FindNativeByName(pEntry->name, &i) != SP_ERROR_NONE)
{
return;
}
if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
{
return;
}
if (native->status == SP_NATIVE_BOUND)
{
return;
}
BindNativeToPlugin(pPlugin, native, i, pEntry);
}
void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin,
sp_native_t *native,
uint32_t index,
NativeEntry *pEntry)
{
/* Mark as bound... we do the rest next. */
native->status = SP_NATIVE_BOUND;
native->user = pEntry;
/* See if a replacement is available. */
if (pEntry->replacement.owner != NULL)
{
/* Perform a replacement bind. */
native->pfn = pEntry->replacement.func;
pEntry->replacement.owner->AddWeakRef(WeakNative(pPlugin, index, pEntry));
}
else
{
/* Perform a normal bind. */
native->pfn = pEntry->func;
/* We don't bother with dependency crap if the owner is Core. */
if (pEntry->owner != g_pCoreNatives)
{
/* The native is optional, this is a special case */
if ((native->flags & SP_NTVFLAG_OPTIONAL) == SP_NTVFLAG_OPTIONAL)
{
/* Only add if there is a valid owner. */
if (pEntry->owner != NULL)
{
pEntry->owner->AddWeakRef(WeakNative(pPlugin, index));
}
else
{
native->status = SP_NATIVE_UNBOUND;
}
}
/* Otherwise, we're a strong dependent and not a weak one */
else
{
/* See if this has already been marked as a dependent.
* If it has, it means this relationship has already occurred,
* and there is no reason to do it again.
*/
if (pEntry->owner != pPlugin
&& pEntry->owner->GetMarkSerial() != g_mark_serial)
{
/* This has not been marked as a dependency yet */
//pPlugin->AddDependency(pEntry->owner);
pEntry->owner->AddDependent(pPlugin);
pEntry->owner->SetMarkSerial(g_mark_serial);
}
}
}
}
}
NativeEntry *ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv)
{
NativeEntry *pEntry;
if ((pEntry = FindNative(ntv->name)) == NULL)
{
pEntry = new NativeEntry;
pEntry->owner = pOwner;
pEntry->name = ntv->name;
pEntry->func = ntv->func;
pEntry->replacement.func = NULL;
pEntry->replacement.owner = NULL;
pEntry->fake = NULL;
m_NtvCache.insert(ntv->name, pEntry);
return pEntry;
}
if (pEntry->owner != NULL)
{
return NULL;
}
pEntry->owner = pOwner;
pEntry->func = ntv->func;
pEntry->name = ntv->name;
return pEntry;
}
void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name)
{
NativeEntry *pEntry;
if ((pEntry = FindNative(name)) == NULL)
{
return;
}
if (pEntry->owner != pOwner)
{
return;
}
if (pEntry->fake != NULL)
{
g_pSourcePawn2->DestroyFakeNative(pEntry->func);
delete pEntry->fake;
pEntry->fake = NULL;
}
pEntry->func = NULL;
pEntry->name = NULL;
pEntry->owner = NULL;
pEntry->replacement.func = NULL;
pEntry->replacement.owner = NULL;
}
NativeEntry *ShareSystem::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
{
FakeNative *pFake;
NativeEntry *pEntry;
SPVM_NATIVE_FUNC gate;
if ((pEntry = FindNative(name)) != NULL && pEntry->owner != NULL)
{
return NULL;
}
pFake = new FakeNative;
if ((gate = g_pSourcePawn2->CreateFakeNative(func, pFake)) == NULL)
{
delete pFake;
return NULL;
}
if (pEntry == NULL)
{
pEntry = new NativeEntry;
m_NtvCache.insert(name, pEntry);
}
pFake->call = pFunc;
pFake->ctx = pFunc->GetParentContext();
strncopy(pFake->name, name, sizeof(pFake->name));
pEntry->fake = pFake;
pEntry->func = gate;
pEntry->name = pFake->name;
pEntry->owner = g_PluginSys.GetPluginByCtx(pFake->ctx->GetContext());
pEntry->replacement.func = NULL;
pEntry->replacement.owner = NULL;
return pEntry;
}
void ShareSystem::AddCapabilityProvider(IExtension *myself, IFeatureProvider *provider,
const char *name)
{
if (m_caps.retrieve(name) != NULL)
return;
Capability cap;
cap.ext = myself;
cap.provider = provider;
m_caps.insert(name, cap);
}
void ShareSystem::DropCapabilityProvider(IExtension *myself, IFeatureProvider *provider,
const char *name)
{
Capability *pCap = m_caps.retrieve(name);
if (pCap == NULL)
return;
if (pCap->ext != myself || pCap->provider != provider)
return;
m_caps.remove(name);
}
FeatureStatus ShareSystem::TestFeature(IPluginRuntime *pRuntime, FeatureType feature,
const char *name)
{
switch (feature)
{
case FeatureType_Native:
return TestNative(pRuntime, name);
case FeatureType_Capability:
return TestCap(name);
default:
break;
}
return FeatureStatus_Unknown;
}
FeatureStatus ShareSystem::TestNative(IPluginRuntime *pRuntime, const char *name)
{
uint32_t index;
if (pRuntime->FindNativeByName(name, &index) == SP_ERROR_NONE)
{
sp_native_t *native;
if (pRuntime->GetNativeByIndex(index, &native) == SP_ERROR_NONE)
{
if (native->status == SP_NATIVE_BOUND)
return FeatureStatus_Available;
else
return FeatureStatus_Unknown;
}
}
NativeEntry *entry = FindNative(name);
if (entry == NULL)
return FeatureStatus_Unknown;
if ((entry->replacement.owner != NULL || entry->owner != NULL) &&
(entry->replacement.func != NULL || entry->func != NULL))
{
return FeatureStatus_Available;
}
else
{
return FeatureStatus_Unavailable;
}
}
FeatureStatus ShareSystem::TestCap(const char *name)
{
Capability *cap = m_caps.retrieve(name);
if (cap == NULL)
return FeatureStatus_Unknown;
return cap->provider->GetFeatureStatus(FeatureType_Capability, name);
}

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 sw=4 tw=99 noet : * vim: set ts=4 :
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -34,14 +34,10 @@
#include <IShareSys.h> #include <IShareSys.h>
#include <IHandleSys.h> #include <IHandleSys.h>
#include <am-string.h>
#include <am-utility.h>
#include <am-refcounting.h>
#include <sh_list.h> #include <sh_list.h>
#include <sm_stringhashmap.h> #include <sm_trie_tpl.h>
#include <sm_namehashset.h> #include "sm_globals.h"
#include "common_logic.h" #include "sourcemod.h"
#include "Native.h"
using namespace SourceHook; using namespace SourceHook;
@ -67,6 +63,29 @@ struct IfaceInfo
class CNativeOwner; class CNativeOwner;
class CPlugin; class CPlugin;
struct NativeEntry;
struct ReplaceNative
{
CNativeOwner *owner;
SPVM_NATIVE_FUNC func;
};
struct FakeNative
{
char name[64];
IPluginContext *ctx;
IPluginFunction *call;
};
struct NativeEntry
{
CNativeOwner *owner;
SPVM_NATIVE_FUNC func;
const char *name;
ReplaceNative replacement;
FakeNative *fake;
};
struct Capability struct Capability
{ {
@ -119,25 +138,25 @@ public:
return &m_IdentRoot; return &m_IdentRoot;
} }
public: public:
void BeginBindingFor(CPlugin *pPlugin);
void BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly); void BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly);
void BindNativeToPlugin(CPlugin *pPlugin, const ke::RefPtr<Native> &pEntry); void BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry);
ke::AlreadyRefed<Native> AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func); NativeEntry *AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
ke::RefPtr<Native> FindNative(const char *name); NativeEntry *FindNative(const char *name);
private: private:
ke::AlreadyRefed<Native> AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv); NativeEntry *AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv);
void ClearNativeFromCache(CNativeOwner *pOwner, const char *name); void ClearNativeFromCache(CNativeOwner *pOwner, const char *name);
void BindNativeToPlugin(CPlugin *pPlugin, const sp_native_t *ntv, uint32_t index, const ke::RefPtr<Native> &pEntry); void BindNativeToPlugin(CPlugin *pPlugin,
sp_native_t *ntv,
uint32_t index,
NativeEntry *pEntry);
private: private:
typedef NameHashSet<ke::RefPtr<Native>, Native> NativeCache;
List<IfaceInfo> m_Interfaces; List<IfaceInfo> m_Interfaces;
HandleType_t m_TypeRoot; HandleType_t m_TypeRoot;
IdentityToken_t m_IdentRoot; IdentityToken_t m_IdentRoot;
HandleType_t m_IfaceType; HandleType_t m_IfaceType;
IdentityType_t m_CoreType; IdentityType_t m_CoreType;
NativeCache m_NtvCache; KTrie<NativeEntry *> m_NtvCache;
StringHashMap<Capability> m_caps; KTrie<Capability> m_caps;
}; };
extern ShareSystem g_ShareSys; extern ShareSystem g_ShareSys;

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