Parent Directory
|
Revision Log
Revision 1 - (view) (download)
1 : | ian | 1 | /* Message Logging 1.17 by Damaged Soul |
2 : | |||
3 : | AMX Mod X Version: 1.75 and above | ||
4 : | Supported Mods: All | ||
5 : | |||
6 : | This file is provided as is (no warranties). | ||
7 : | |||
8 : | *************** | ||
9 : | * Description * | ||
10 : | *************** | ||
11 : | This plugin allows for the logging of any or all messages sent by the HL engine or mod (such as | ||
12 : | DeathMsg, CurWeapon, etc) It also allows these messages to be filtered by entity. | ||
13 : | |||
14 : | Information that is logged includes: | ||
15 : | - Message name | ||
16 : | - Number of message arguments | ||
17 : | - Message ID | ||
18 : | - Message destination (i.e. Broadcast, One, All, etc) | ||
19 : | - Message origin | ||
20 : | - Entity that received the message | ||
21 : | - Entity classname | ||
22 : | - Entity netname | ||
23 : | - Every argument value and type | ||
24 : | |||
25 : | ******************** | ||
26 : | * Required Modules * | ||
27 : | ******************** | ||
28 : | Fakemeta | ||
29 : | |||
30 : | ********* | ||
31 : | * Usage * | ||
32 : | ********* | ||
33 : | Console Commands: | ||
34 : | amx_msglog <command> [argument] | ||
35 : | - Displays help for logging engine/mod messages when no command is given | ||
36 : | - Commands: | ||
37 : | start [msg name or id] | ||
38 : | - Starts logging given message or all if no argument | ||
39 : | stop [msg name or id] | ||
40 : | - Stops logging given message or all if no argument | ||
41 : | list [page] | ||
42 : | - Displays list of messages and their logging status | ||
43 : | |||
44 : | Cvars: | ||
45 : | amx_ml_filter [Default Value: 1] | ||
46 : | - Allows for filtering messages by entity index | ||
47 : | - Set this to the index of the entity for which you want to log messages | ||
48 : | - If this is set to 0, message logging will be done for all entities | ||
49 : | |||
50 : | amx_ml_logmode [Default Value: 1] | ||
51 : | - Determines where to log message information | ||
52 : | - Set this to 0 to log information to the standard AMX Mod X log file | ||
53 : | - Set this to 1 to log information to a separate file (messages.log) in the log directory | ||
54 : | |||
55 : | Examples: | ||
56 : | To log the DeathMsg message: | ||
57 : | amx_msglog start DeathMsg OR amx_msglog start 83 | ||
58 : | |||
59 : | To stop logging the DeathMsg message: | ||
60 : | amx_msglog stop DeathMsg OR amx_msglog stop 83 | ||
61 : | |||
62 : | To log all messages except the DeathMsg message: | ||
63 : | amx_msglog start | ||
64 : | amx_msglog stop DeathMsg OR amx_msglog stop 83 | ||
65 : | |||
66 : | To log messages only sent to third player of the server: | ||
67 : | amx_ml_filter 3 | ||
68 : | |||
69 : | To log messages sent to all entities: | ||
70 : | amx_ml_filter 0 | ||
71 : | |||
72 : | ******************* | ||
73 : | * Version History * | ||
74 : | ******************* | ||
75 : | 1.17 [Feb. 11, 2007] | ||
76 : | - Fixed: Long arguments were being reported as bytes (Thanks XxAvalanchexX) | ||
77 : | |||
78 : | 1.16 [Oct. 4, 2006] | ||
79 : | - Fixed: String arguments of messages that contained format paramaters (such as %s) caused | ||
80 : | runtime error 25 (thanks sawce :avast:) | ||
81 : | - Tabs and new-lines in string arguments of messages are now converted to ^t and ^n | ||
82 : | |||
83 : | 1.15 [July 4, 2006] | ||
84 : | - Now uses the Fakemeta module instead of Engine | ||
85 : | - Now uses vformat instead of the deprecated format_args when logging information | ||
86 : | - Very minor optimizations | ||
87 : | |||
88 : | 1.11 [May 11, 2006] | ||
89 : | - Fixed: String arguments of messages were being logged as numbers | ||
90 : | |||
91 : | 1.10 [Apr. 23, 2006] | ||
92 : | - Minor optimizations including the use of pcvar natives to improve performance a bit | ||
93 : | - Much of the logged text has been rewritten in an attempt to make it more like the Half-Life | ||
94 : | log standard | ||
95 : | - Now when using the start or stop commands on a specified message, both the message name and | ||
96 : | ID are printed instead of just whatever was given as the argument | ||
97 : | - Added: amx_ml_logmode cvar for controlling where to log message information | ||
98 : | - Fixed: When no arguments were given to amx_msglog, usage information was not printed | ||
99 : | |||
100 : | 1.03 [Oct. 26, 2004] | ||
101 : | - Public release | ||
102 : | - Fixed: Entity filter wasn't actually checking for valid entities correctly (thanks JGHG) | ||
103 : | |||
104 : | 1.02 [Oct. 25, 2004] | ||
105 : | - Fixed: If logging had been started for a message, stopped, then started again, same message | ||
106 : | was logged twice. | ||
107 : | - Fixed: If message name or ID was invalid, started/stopped logging all messages instead | ||
108 : | |||
109 : | 1.01 [Oct. 23, 2004] | ||
110 : | - Fixed: List command was not reporting correct logging status | ||
111 : | - Fixed: Filter was incorrectly filtering messages if amx_ml_filter was 0 | ||
112 : | |||
113 : | 1.00 [Oct. 19, 2004] | ||
114 : | - Initial version | ||
115 : | */ | ||
116 : | |||
117 : | #include <amxmodx> | ||
118 : | #include <amxmisc> | ||
119 : | #include <fakemeta> | ||
120 : | |||
121 : | // Plugin information constants | ||
122 : | new const PLUGIN[] = "Message Logging" | ||
123 : | new const AUTHOR[] = "Damaged Soul" | ||
124 : | new const VERSION[] = "1.16" | ||
125 : | |||
126 : | #define MAX_ENGINE_MESSAGES 63 // Max messages reserved by engine (DO NOT MODIFY) | ||
127 : | #define MAX_POSSIBLE_MESSAGES 255 // Max possible messages for mod, is 255 really the limit? | ||
128 : | |||
129 : | #define MSGLOG_STOPPED 0 // Logging status: Stopped | ||
130 : | #define MSGLOG_STARTED 1 // Logging status: Started | ||
131 : | |||
132 : | // Cvar pointers | ||
133 : | new g_cvarFilter, g_cvarLogMode | ||
134 : | |||
135 : | // Filename of separate log file for message info | ||
136 : | new const LOGFILE_NAME[] = "messages.log" | ||
137 : | |||
138 : | // Is message currently being hooked for logging? | ||
139 : | new bool:g_msgLogging[MAX_POSSIBLE_MESSAGES + 1] = {false} | ||
140 : | // Is message registered to message_forward? | ||
141 : | new bool:g_msgRegistered[MAX_POSSIBLE_MESSAGES + 1] = {false} | ||
142 : | // Stores the names of messages, indexed by message ID | ||
143 : | new g_msgCache[MAX_POSSIBLE_MESSAGES + 1][32] | ||
144 : | |||
145 : | // Max messages allowed by mod | ||
146 : | new g_maxMessages = MAX_ENGINE_MESSAGES | ||
147 : | |||
148 : | new const NULL_STR[] = "<NULL>" | ||
149 : | |||
150 : | /* Initialize plugin; Register commands and cvars */ | ||
151 : | public plugin_init() | ||
152 : | { | ||
153 : | register_plugin(PLUGIN, VERSION, AUTHOR) | ||
154 : | |||
155 : | g_cvarFilter = register_cvar("amx_ml_filter", "1") | ||
156 : | g_cvarLogMode = register_cvar("amx_ml_logmode", "1") | ||
157 : | |||
158 : | register_concmd("amx_msglog", "cmd_msglog", ADMIN_ADMIN, | ||
159 : | "<command> [argument] - displays help for logging engine/mod messages") | ||
160 : | |||
161 : | g_maxMessages = generate_msg_table() | ||
162 : | } | ||
163 : | |||
164 : | /* Handles command amx_msglog */ | ||
165 : | public cmd_msglog(id, level, cid) { | ||
166 : | if (!cmd_access(id, level, cid, 1)) | ||
167 : | return PLUGIN_HANDLED | ||
168 : | |||
169 : | new argCmd[6] | ||
170 : | read_argv(1, argCmd, 5) | ||
171 : | remove_quotes(argCmd) | ||
172 : | |||
173 : | if (equali(argCmd, "start") || equali(argCmd, "stop")) // start or stop commands | ||
174 : | { | ||
175 : | new argMsg[32] | ||
176 : | read_argv(2, argMsg, 31) | ||
177 : | remove_quotes(argMsg) | ||
178 : | |||
179 : | new msgid = str_to_msgid(argMsg) | ||
180 : | |||
181 : | if (is_msg_valid(msgid)) | ||
182 : | { | ||
183 : | new status = get_msglog_status(msgid) | ||
184 : | |||
185 : | if (argCmd[2] == 'a') // start <message> | ||
186 : | { | ||
187 : | if (status == MSGLOG_STOPPED) | ||
188 : | { | ||
189 : | set_msglog_status(msgid, MSGLOG_STARTED) | ||
190 : | log_msgf("Logging started for message (%s ^"%d^")", g_msgCache[msgid], msgid) | ||
191 : | } | ||
192 : | else | ||
193 : | console_print(id, "Logging has already been started for message (%s ^"%d^")", g_msgCache[msgid], msgid) | ||
194 : | } | ||
195 : | else // stop <message> | ||
196 : | { | ||
197 : | if (status == MSGLOG_STARTED) | ||
198 : | { | ||
199 : | set_msglog_status(msgid, MSGLOG_STOPPED) | ||
200 : | log_msgf("Logging stopped for message (%s ^"%d^")", g_msgCache[msgid], msgid) | ||
201 : | } | ||
202 : | else | ||
203 : | console_print(id, "Logging has already been stopped for message (%s ^"%d^")", g_msgCache[msgid], msgid) | ||
204 : | } | ||
205 : | } | ||
206 : | else | ||
207 : | { | ||
208 : | // If msg name or ID isn't blank, then at this point we have an invalid msg | ||
209 : | if (argMsg[0]) | ||
210 : | { | ||
211 : | console_print(id, "%s is not a valid message name or ID", argMsg) | ||
212 : | return PLUGIN_HANDLED | ||
213 : | } | ||
214 : | |||
215 : | if (argCmd[2] == 'a') // start | ||
216 : | { | ||
217 : | set_msglog_status(0, MSGLOG_STARTED) | ||
218 : | log_msgf("Logging started for all messages") | ||
219 : | } | ||
220 : | else // stop | ||
221 : | { | ||
222 : | set_msglog_status(0, MSGLOG_STOPPED) | ||
223 : | log_msgf("Logging stopped for all messages") | ||
224 : | } | ||
225 : | } | ||
226 : | } | ||
227 : | else if (equali(argCmd, "list")) // list command | ||
228 : | { | ||
229 : | new argStart[4] | ||
230 : | new start = read_argv(2, argStart, 3) ? str_to_num(argStart) : 1 | ||
231 : | |||
232 : | if (start < 1) | ||
233 : | start = 1 | ||
234 : | else if (start > g_maxMessages) | ||
235 : | start = g_maxMessages | ||
236 : | |||
237 : | new end = start + 9 | ||
238 : | if (end > g_maxMessages) | ||
239 : | end = g_maxMessages | ||
240 : | |||
241 : | new logstatus[8], msgname[32] | ||
242 : | |||
243 : | console_print(id, "^n------------ Message Logging Statuses -------------") | ||
244 : | console_print(id, " %-31s %s", "Message Name", "Status") | ||
245 : | |||
246 : | for (new i = start; i <= end; i++) | ||
247 : | { | ||
248 : | copy(msgname, 31, g_msgCache[i]) | ||
249 : | |||
250 : | if (!msgname[0]) | ||
251 : | copy(msgname, 31, "<Unknown>") | ||
252 : | |||
253 : | copy(logstatus, 7, g_msgLogging[i] ? "LOGGING" : "IDLE") | ||
254 : | |||
255 : | console_print(id, "%3d: %-31s %s", i, msgname, logstatus) | ||
256 : | } | ||
257 : | |||
258 : | console_print(id, "Entries %d - %d of %d", start, end, g_maxMessages) | ||
259 : | |||
260 : | if (end < g_maxMessages) | ||
261 : | console_print(id, "-------- Use 'amx_msglog list %d' for more --------", end + 1) | ||
262 : | else | ||
263 : | console_print(id,"-------- Use 'amx_msglog list 1' for beginning --------") | ||
264 : | |||
265 : | } | ||
266 : | else | ||
267 : | { | ||
268 : | // Display usage information | ||
269 : | console_print(id, "Usage: amx_msglog <command> [argument]") | ||
270 : | console_print(id, "Valid commands are: ") | ||
271 : | |||
272 : | console_print(id, " start [msg name or id] - Starts logging given message or all if no argument") | ||
273 : | console_print(id, " stop [msg name or id] - Stops logging given message or all if no argument") | ||
274 : | console_print(id, " list [page] - Displays list of messages and their logging status") | ||
275 : | } | ||
276 : | |||
277 : | return PLUGIN_HANDLED | ||
278 : | } | ||
279 : | |||
280 : | /* Forward for hooked messages */ | ||
281 : | public message_forward(msgid, msgDest, msgEnt) { | ||
282 : | if (!g_msgLogging[msgid]) return PLUGIN_CONTINUE | ||
283 : | |||
284 : | new entFilter = get_pcvar_num(g_cvarFilter) | ||
285 : | |||
286 : | /* If value of amx_ml_filter isn't a valid entity index (0 is accepted in order to log ALL) | ||
287 : | Then stop all logging */ | ||
288 : | if (entFilter != 0 && !pev_valid(entFilter)) { | ||
289 : | set_msglog_status(0, MSGLOG_STOPPED) | ||
290 : | |||
291 : | log_msgf("Logging stopped for all messages because entity index ^"%d^" is not valid", entFilter) | ||
292 : | return PLUGIN_CONTINUE | ||
293 : | } | ||
294 : | |||
295 : | // If not filtering by entity and the receiver entity doesn't match the filter, don't log message | ||
296 : | if (entFilter != 0 && msgEnt != 0 && msgEnt != entFilter) | ||
297 : | return PLUGIN_CONTINUE | ||
298 : | |||
299 : | new msgname[32], id[4], argcount, dest[15], Float:msgOrigin[3], entStr[7], entClassname[32], entNetname[32] | ||
300 : | |||
301 : | // Get message name | ||
302 : | copy(msgname, 31, g_msgCache[msgid]) | ||
303 : | |||
304 : | // If message has no name, then set the name to message ID | ||
305 : | if (!msgname[0]) | ||
306 : | { | ||
307 : | num_to_str(msgid, id, 3) | ||
308 : | copy(msgname, 31, id) | ||
309 : | } | ||
310 : | |||
311 : | // Get number of message arguments | ||
312 : | argcount = get_msg_args() | ||
313 : | |||
314 : | // Determine the destination of the message | ||
315 : | switch (msgDest) { | ||
316 : | case MSG_BROADCAST: | ||
317 : | copy(dest, 9, "Broadcast") | ||
318 : | case MSG_ONE: | ||
319 : | copy(dest, 3, "One") | ||
320 : | case MSG_ALL: | ||
321 : | copy(dest, 3, "All") | ||
322 : | case MSG_INIT: | ||
323 : | copy(dest, 4, "Init") | ||
324 : | case MSG_PVS: | ||
325 : | copy(dest, 3, "PVS") | ||
326 : | case MSG_PAS: | ||
327 : | copy(dest, 3, "PAS") | ||
328 : | case MSG_PVS_R: | ||
329 : | copy(dest, 12, "PVS Reliable") | ||
330 : | case MSG_PAS_R: | ||
331 : | copy(dest, 12, "PAS Reliable") | ||
332 : | case MSG_ONE_UNRELIABLE: | ||
333 : | copy(dest, 14, "One Unreliable") | ||
334 : | case MSG_SPEC: | ||
335 : | copy(dest, 4, "Spec") | ||
336 : | default: | ||
337 : | copy(dest, 7, "Unknown") | ||
338 : | } | ||
339 : | |||
340 : | // Get the origin of the message (only truly valid for PVS through PAS_R) | ||
341 : | get_msg_origin(msgOrigin) | ||
342 : | |||
343 : | // Get the receiving entity's classname and netname | ||
344 : | if (msgEnt != 0) { | ||
345 : | num_to_str(msgEnt, entStr, 6) | ||
346 : | pev(msgEnt, pev_classname, entClassname, 31) | ||
347 : | pev(msgEnt, pev_netname, entNetname, 31) | ||
348 : | |||
349 : | if (!entNetname[0]) | ||
350 : | copy(entNetname, 31, NULL_STR) | ||
351 : | } else { | ||
352 : | copy(entStr, 6, NULL_STR) | ||
353 : | copy(entClassname, 6, NULL_STR) | ||
354 : | copy(entNetname, 6, NULL_STR) | ||
355 : | } | ||
356 : | |||
357 : | // Log message information (MessageBegin) | ||
358 : | log_msgf("MessageBegin (%s ^"%d^") (Destination ^"%s<%d>^") (Args ^"%d^") (Entity ^"%s^") (Classname ^"%s^") (Netname ^"%s^") (Origin ^"%f %f %f^")", | ||
359 : | msgname, msgid, dest, msgDest, argcount, entStr, entClassname, entNetname, msgOrigin[0], msgOrigin[1], msgOrigin[2]) | ||
360 : | |||
361 : | static str[256] | ||
362 : | |||
363 : | // Log all argument data | ||
364 : | if (argcount > 0) | ||
365 : | { | ||
366 : | for (new i = 1; i <= argcount; i++) { | ||
367 : | switch (get_msg_argtype(i)) { | ||
368 : | case ARG_BYTE: | ||
369 : | log_msgf("Arg %d (Byte ^"%d^")", i, get_msg_arg_int(i)) | ||
370 : | case ARG_CHAR: | ||
371 : | log_msgf("Arg %d (Char ^"%d^")", i, get_msg_arg_int(i)) | ||
372 : | case ARG_SHORT: | ||
373 : | log_msgf("Arg %d (Short ^"%d^")", i, get_msg_arg_int(i)) | ||
374 : | case ARG_LONG: | ||
375 : | log_msgf("Arg %d (Long ^"%d^")", i, get_msg_arg_int(i)) | ||
376 : | case ARG_ANGLE: | ||
377 : | log_msgf("Arg %d (Angle ^"%f^")", i, get_msg_arg_float(i)) | ||
378 : | case ARG_COORD: | ||
379 : | log_msgf("Arg %d (Coord ^"%f^")", i, get_msg_arg_float(i)) | ||
380 : | case ARG_STRING: | ||
381 : | { | ||
382 : | get_msg_arg_string(i, str, 255) | ||
383 : | |||
384 : | replace_all(str, 254, "^t", "^^t") | ||
385 : | replace_all(str, 254, "^n", "^^n") | ||
386 : | replace_all(str, 254, "%", "%%") | ||
387 : | |||
388 : | log_msgf("Arg %d (String ^"%s^")", i, str) | ||
389 : | } | ||
390 : | case ARG_ENTITY: | ||
391 : | log_msgf("Arg %d (Entity ^"%d^")", i, get_msg_arg_int(i)) | ||
392 : | default: | ||
393 : | log_msgf("Arg %d (Unknown ^"%d^")", i, get_msg_arg_int(i)) | ||
394 : | } | ||
395 : | } | ||
396 : | } | ||
397 : | else | ||
398 : | { | ||
399 : | log_msgf("Message contains no arguments") | ||
400 : | } | ||
401 : | |||
402 : | // Log that the message ended (MessageEnd) | ||
403 : | log_msgf("MessageEnd (%s ^"%d^")", msgname, msgid) | ||
404 : | |||
405 : | return PLUGIN_CONTINUE | ||
406 : | } | ||
407 : | |||
408 : | /***************** Other Functions *****************/ | ||
409 : | |||
410 : | /* Fills g_msgCache with message names for faster look up | ||
411 : | Return value: Number of messages that are valid */ | ||
412 : | generate_msg_table() { | ||
413 : | for (new i = MAX_ENGINE_MESSAGES + 1; i <= MAX_POSSIBLE_MESSAGES; i++) | ||
414 : | { | ||
415 : | // Store the message name in the cache for faster lookup | ||
416 : | if (!get_user_msgname(i, g_msgCache[i], 31)) | ||
417 : | return i - 1 | ||
418 : | } | ||
419 : | |||
420 : | return MAX_POSSIBLE_MESSAGES | ||
421 : | } | ||
422 : | |||
423 : | /* Returns true if msgid is a valid message */ | ||
424 : | bool:is_msg_valid(msgid) | ||
425 : | { | ||
426 : | return (msgid > 0 && msgid <= g_maxMessages) | ||
427 : | } | ||
428 : | |||
429 : | /* Returns message ID from string */ | ||
430 : | str_to_msgid(const str[]) { | ||
431 : | new n = str_to_num(str) | ||
432 : | |||
433 : | if (n <= 0 ) | ||
434 : | return get_user_msgid(str) | ||
435 : | |||
436 : | return n | ||
437 : | } | ||
438 : | |||
439 : | /* Gets logging status of message */ | ||
440 : | get_msglog_status(msgid) | ||
441 : | { | ||
442 : | return g_msgLogging[msgid] ? MSGLOG_STARTED : MSGLOG_STOPPED | ||
443 : | } | ||
444 : | |||
445 : | /* Sets logging status of message | ||
446 : | If msgid = 0, status will be applied to all messages */ | ||
447 : | set_msglog_status(msgid, status) | ||
448 : | { | ||
449 : | if (msgid > 0) // Individual message | ||
450 : | { | ||
451 : | g_msgLogging[msgid] = (status == MSGLOG_STARTED) | ||
452 : | if (!g_msgRegistered[msgid]) | ||
453 : | { | ||
454 : | register_message(msgid, "message_forward") | ||
455 : | g_msgRegistered[msgid] = true | ||
456 : | } | ||
457 : | |||
458 : | } | ||
459 : | else // ALL messages | ||
460 : | { | ||
461 : | for (new i = 1; i <= g_maxMessages; i++) | ||
462 : | { | ||
463 : | g_msgLogging[i] = (status == MSGLOG_STARTED) | ||
464 : | if (status == MSGLOG_STARTED) | ||
465 : | { | ||
466 : | if (!g_msgRegistered[i]) | ||
467 : | { | ||
468 : | register_message(i, "message_forward") | ||
469 : | g_msgRegistered[i] = true | ||
470 : | } | ||
471 : | } | ||
472 : | } | ||
473 : | } | ||
474 : | } | ||
475 : | |||
476 : | /* Writes messages to log file depending on value of amx_ml_logmode */ | ||
477 : | log_msgf(const fmt[], {Float,Sql,Result,_}:...) | ||
478 : | { | ||
479 : | static buffer[512] | ||
480 : | vformat(buffer, 511, fmt, 2) | ||
481 : | |||
482 : | if (get_pcvar_num(g_cvarLogMode)) | ||
483 : | log_to_file(LOGFILE_NAME, buffer) | ||
484 : | else | ||
485 : | log_amx(buffer) | ||
486 : | } |
Contact | ViewVC Help |
Powered by ViewVC 1.0.4 |