[Half-Life AMXX] / msglogging.sma Repository:
ViewVC logotype

Annotation of /msglogging.sma

Parent Directory Parent Directory | Revision Log 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