diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1da14b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.vs
+Debug
+Release
+*.vcxproj.user
diff --git a/frag_parser.c b/frag_parser.c
index 2ecd48e..5472d0d 100644
--- a/frag_parser.c
+++ b/frag_parser.c
@@ -203,6 +203,12 @@ static void InitFragDefs(void)
goto nextline; \
}
+#define FRAGS_CHECKARGS3(x, y, z) \
+ if (c != x && c != y && c != z) { \
+ Sys_PrintError("Fragfile warning (line %d): %d, %d or %d tokens expected\n", line, x, y, z); \
+ goto nextline; \
+ }
+
#define FRAGS_CHECK_VERSION_DEFINED() \
if (!gotversion) { \
Sys_PrintError("Fragfile error: #FRAGFILE VERSION must be defined at the head of the file\n"); \
@@ -345,7 +351,7 @@ qbool LoadFragFile(char *filename, qbool quiet)
if (!strcasecmp(Cmd_Argv(1), "WEAPON_CLASS") || !strcasecmp(Cmd_Argv(1), "WC"))
{
- FRAGS_CHECKARGS2(4, 5);
+ FRAGS_CHECKARGS3(4, 5, 6);
token = Cmd_Argv(2);
if (!strcasecmp(token, "NOWEAPON") || !strcasecmp(token, "NONE") || !strcasecmp(token, "NULL"))
@@ -675,8 +681,12 @@ void Frags_Parse(mvd_info_t *mvd, char *fragmessage, int level)
case mt_suicide :
{
mvd->fragstats[p1->pnum].suicides++;
- p1->death_count++;
- Log_Event(&logger, mvd, LOG_DEATH, p1->pnum);
+
+ // If it's a normal suicide then it'll get picked up by health < 0
+ if (!mess->wclass_index) {
+ p1->death_count++;
+ Log_Event(&logger, mvd, LOG_DEATH, p1->pnum);
+ }
break;
}
case mt_fragged :
diff --git a/logger.c b/logger.c
index a24b4f7..1c462f3 100644
--- a/logger.c
+++ b/logger.c
@@ -6,7 +6,7 @@
logger_t logger;
-char *LogVarValueAsString(mvd_info_t *mvd, const char *varname, int player_num);
+char *LogVarValueAsString(mvd_info_t *mvd, const char *varname, int player_num, encoding_mode_t encoding);
#define LOG_CHECK_NUMARGS(num) \
{ \
@@ -102,6 +102,14 @@ static log_eventlogger_type_t Log_ParseEventloggerType(const char *event_logger_
{
return LOG_MATCHEND_ALL;
}
+ else if (!strcasecmp(event_logger_type, "MATCHEND_ALL_BETWEEN"))
+ {
+ return LOG_MATCHEND_ALL_BETWEEN;
+ }
+ else if (!strcasecmp(event_logger_type, "MATCHEND_FINAL"))
+ {
+ return LOG_MATCHEND_FINAL;
+ }
else if (!strcasecmp(event_logger_type, "DEMOSTART"))
{
return LOG_DEMOSTART;
@@ -404,7 +412,7 @@ qbool Log_ParseOutputTemplates(logger_t *logger, const char *template_file)
return true;
}
-char *Log_ExpandTemplateString(logger_t *logger, mvd_info_t *mvd, const char *template_string, int player_num, int *output_len)
+char *Log_ExpandTemplateString(logger_t *logger, mvd_info_t *mvd, const char *template_string, int player_num, int *output_len, encoding_mode_t encoding)
{
char var_value[1024];
char tmp[1024];
@@ -458,7 +466,7 @@ char *Log_ExpandTemplateString(logger_t *logger, mvd_info_t *mvd, const char *te
// Expand the variable based on the current mvd context / player.
snprintf(tmp, min((var_end - var_start), sizeof(tmp)), "%s", var_start);
- strlcpy(var_value, LogVarValueAsString(mvd, tmp, player_num), sizeof(var_value));
+ strlcpy(var_value, LogVarValueAsString(mvd, tmp, player_num, encoding), sizeof(var_value));
// Add the value to the output string.
vv = var_value;
@@ -474,6 +482,10 @@ char *Log_ExpandTemplateString(logger_t *logger, mvd_info_t *mvd, const char *te
}
input = var_end;
+
+ if (!input[0]) {
+ break;
+ }
}
}
@@ -548,8 +560,8 @@ void Log_Event(logger_t *logger, mvd_info_t *mvd, log_eventlogger_type_t type, i
int p;
int player_start = player_num;
int player_count = 1;
- int output_len = 0;
- char *output = NULL;
+ int output_len[encoding_count] = { 0, 0, 0 };
+ char *output[encoding_count] = { NULL };
char *expanded_filename = NULL;
log_eventlogger_t *eventlogger = NULL;
log_outputfile_t *output_file = NULL;
@@ -582,7 +594,10 @@ void Log_Event(logger_t *logger, mvd_info_t *mvd, log_eventlogger_type_t type, i
}
// Expand the event loggers template string into an output string.
- output = Q_strdup(Log_ExpandTemplateString(logger, mvd, eventlogger->output_template_string, player_num, &output_len));
+ for (j = 0; j < encoding_count; ++j) {
+ output_len[j] = 0;
+ output[j] = Q_strdup(Log_ExpandTemplateString(logger, mvd, eventlogger->output_template_string, player_num, &output_len[j], j));
+ }
// See the above comment for setting player_count for explination.
// TODO : Make this more readable by making the stuff inside the for-loop a separate function, and calling it in two separate if-cases instead.
@@ -597,7 +612,7 @@ void Log_Event(logger_t *logger, mvd_info_t *mvd, log_eventlogger_type_t type, i
// and then write the output to the files.
for (j = 0; j < eventlogger->templates_count; j++)
{
- expanded_filename = Log_ExpandTemplateString(logger, mvd, eventlogger->outputfile_templates[j]->name, p, NULL);
+ expanded_filename = Log_ExpandTemplateString(logger, mvd, eventlogger->outputfile_templates[j]->name, p, NULL, normal_encoding);
// Search for the expanded filename in the output_hashtable.
output_file = Log_OutputFilesHashTable_GetValue(logger, expanded_filename);
@@ -609,11 +624,23 @@ void Log_Event(logger_t *logger, mvd_info_t *mvd, log_eventlogger_type_t type, i
output_file->filename = Q_strdup(expanded_filename);
output_file->file = fopen(expanded_filename, "w");
+ if (strstr(expanded_filename, ".xml")) {
+ output_file->encoding = xml_encoding;
+ }
+ else if (strstr(expanded_filename, ".json")) {
+ output_file->encoding = json_encoding;
+ }
+ else {
+ output_file->encoding = normal_encoding;
+ }
+
if (!output_file->file)
{
Q_free(output_file->filename);
Q_free(output_file);
- Q_free(output);
+ for (j = 0; j < encoding_count; ++j) {
+ Q_free(output[j]);
+ }
Sys_Error("Log_Event: Failed to open the file %s (expanded from %s) for output.\n", output_file->filename, eventlogger->outputfile_templates[j]->name);
}
@@ -644,17 +671,18 @@ void Log_Event(logger_t *logger, mvd_info_t *mvd, log_eventlogger_type_t type, i
num_written_to++;
}*/
- if (output_len > 0)
- {
+ if (output_len[output_file->encoding] > 0) {
// Write the expanded output to the file.
- fwrite(output, sizeof(char), output_len, output_file->file);
+ fwrite(output[output_file->encoding], sizeof(char), output_len[output_file->encoding], output_file->file);
fflush(output_file->file);
}
}
}
}
- Q_free(output);
+ for (j = 0; j < encoding_count; ++j) {
+ Q_free(output[j]);
+ }
}
void Log_OutputFilesHashTable_Clear(logger_t *logger)
@@ -684,6 +712,16 @@ void Log_OutputFilesHashTable_Clear(logger_t *logger)
// Log variables
// ============================================================================
+static char* LogVar_suicides(mvd_info_t* mvd, const char* varname, int player_num)
+{
+ return va("%d", mvd->fragstats[player_num].suicides);
+}
+
+static char* LogVar_teamkills(mvd_info_t* mvd, const char* varname, int player_num)
+{
+ return va("%d", mvd->fragstats[player_num].teamkills);
+}
+
static char *LogVar_name(mvd_info_t *mvd, const char *varname, int player_num)
{
return mvd->players[player_num].name;
@@ -1161,6 +1199,12 @@ static char *LogVar_teamplay(mvd_info_t *mvd, const char *varname, int player_nu
}
static char *LogVar_map(mvd_info_t *mvd, const char *varname, int player_num)
+{
+ // to keep old templates correct: used to return mapname from svc_serverdata and then overwrite with file from serverinfo
+ return mvd->serverinfo.mapfile;
+}
+
+static char* LogVar_mapname(mvd_info_t* mvd, const char* varname, int player_num)
{
return mvd->serverinfo.mapname;
}
@@ -1254,6 +1298,7 @@ logvar_t logvar_list[] =
LOGVAR_DEFINE(matchtime, LOGVAR_DEMO),
LOGVAR_DEFINE(mvdframe, LOGVAR_DEMO),
LOGVAR_DEFINE(map, LOGVAR_DEMO),
+ LOGVAR_DEFINE(mapname, LOGVAR_DEMO),
LOGVAR_DEFINE(gamedir, LOGVAR_DEMO),
LOGVAR_DEFINE(maxfps, LOGVAR_DEMO),
LOGVAR_DEFINE(zext, LOGVAR_DEMO),
@@ -1335,7 +1380,9 @@ logvar_t logvar_list[] =
LOGVAR_DEFINE(topcolor, LOGVAR_PLAYER),
LOGVAR_DEFINE(bottomcolor, LOGVAR_PLAYER),
LOGVAR_DEFINE(droppedweapon, LOGVAR_PLAYER),
- LOGVAR_DEFINE(droppedweaponstr, LOGVAR_PLAYER)
+ LOGVAR_DEFINE(droppedweaponstr, LOGVAR_PLAYER),
+ LOGVAR_DEFINE(teamkills, LOGVAR_PLAYER),
+ LOGVAR_DEFINE(suicides, LOGVAR_PLAYER)
};
#define LOGVARS_LIST_SIZE (sizeof(logvar_list) / sizeof(logvar_t))
@@ -1380,7 +1427,140 @@ void LogVarHashTable_AddValue(logvar_t **hashtable, logvar_t *logvar)
}
}
-char *LogVarValueAsString(mvd_info_t *mvd, const char *varname, int player_num)
+#define MAX_STRINGS 128
+
+static char* json_string(const char* input)
+{
+ // >>>> like va(...) ... eugh
+ static char string[MAX_STRINGS][1024];
+ static int index = 0;
+ char* ch, * start;
+
+ index %= MAX_STRINGS;
+ // <<<<
+
+ start = ch = string[index++];
+ while (*input) {
+ unsigned char current = *input;
+
+ if (ch - start >= 1000) {
+ break;
+ }
+
+ if (current == '\\' || current == '"') {
+ *ch++ = '\\';
+ *ch++ = current;
+ }
+ else if (current == '\n') {
+ *ch++ = '\\';
+ *ch++ = 'n';
+ }
+ else if (current == '\r') {
+ *ch++ = '\\';
+ *ch++ = 'r';
+ }
+ else if (current == '\b') {
+ *ch++ = '\\';
+ *ch++ = 'b';
+ }
+ else if (current == '\t') {
+ *ch++ = '\\';
+ *ch++ = 't';
+ }
+ else if (current == '\f') {
+ *ch++ = '\\';
+ *ch++ = 'f';
+ }
+ else if (current < ' ' || current >= 128) {
+ *ch++ = '\\';
+ *ch++ = 'u';
+ *ch++ = '0';
+ *ch++ = '0';
+ if (current < 16) {
+ *ch++ = '0';
+ *ch++ = "0123456789ABCDEF"[(int)current];
+ }
+ else {
+ *ch++ = "0123456789ABCDEF"[((int)(current)) >> 4];
+ *ch++ = "0123456789ABCDEF"[((int)(current)) & 15];
+ }
+ }
+ else {
+ *ch++ = current;
+ }
+ ++input;
+ }
+ *ch = '\0';
+ return start;
+}
+
+char* xml_string(const char* original)
+{
+ static char string[MAX_STRINGS][1024];
+ static int index = 0;
+ int length = strlen(original);
+ int newlength = 0;
+ int i = 0;
+
+ index %= MAX_STRINGS;
+
+ memset(string[index], 0, sizeof(string[0]));
+
+ for (i = 0; i < length; ++i) {
+ unsigned char ch = (unsigned char)original[i];
+
+ if (ch == '<') {
+ if (newlength < sizeof(string[0]) - 4) {
+ string[index][newlength++] = '&';
+ string[index][newlength++] = 'l';
+ string[index][newlength++] = 't';
+ string[index][newlength++] = ';';
+ }
+ }
+ else if (ch == '>') {
+ if (newlength < sizeof(string[0]) - 4) {
+ string[index][newlength++] = '&';
+ string[index][newlength++] = 'g';
+ string[index][newlength++] = 't';
+ string[index][newlength++] = ';';
+ }
+ }
+ else if (ch == '"') {
+ if (newlength < sizeof(string[0]) - 5) {
+ string[index][newlength++] = '&';
+ string[index][newlength++] = '#';
+ string[index][newlength++] = '3';
+ string[index][newlength++] = '4';
+ string[index][newlength++] = ';';
+ }
+ }
+ else if (ch == '&') {
+ if (newlength < sizeof(string[0]) - 5) {
+ string[index][newlength++] = '&';
+ string[index][newlength++] = 'a';
+ string[index][newlength++] = 'm';
+ string[index][newlength++] = 'p';
+ string[index][newlength++] = ';';
+ }
+ }
+ else if (ch == '\'') {
+ if (newlength < sizeof(string[0]) - 5) {
+ string[index][newlength++] = '&';
+ string[index][newlength++] = '#';
+ string[index][newlength++] = '3';
+ string[index][newlength++] = '9';
+ string[index][newlength++] = ';';
+ }
+ }
+ else {
+ string[index][newlength++] = ch;
+ }
+ }
+
+ return string[index++];
+}
+
+char *LogVarValueAsString(mvd_info_t *mvd, const char *varname, int player_num, encoding_mode_t encoding)
{
logvar_t *logvar = LogVarHashTable_GetValue(logvar_hashtable, varname);
@@ -1407,7 +1587,19 @@ char *LogVarValueAsString(mvd_info_t *mvd, const char *varname, int player_num)
return "";
}
- return logvar->func(mvd, logvar->name, player_num);
+ if (encoding == xml_encoding) {
+ const char* source = logvar->func(mvd, logvar->name, player_num);
+
+ return xml_string(source);
+ }
+ else if (encoding == json_encoding) {
+ const char* source = logvar->func(mvd, logvar->name, player_num);
+
+ return json_string(source);
+ }
+ else {
+ return logvar->func(mvd, logvar->name, player_num);
+ }
}
void LogVarHashTable_Init(void)
diff --git a/logger.h b/logger.h
index bfffb77..246840f 100644
--- a/logger.h
+++ b/logger.h
@@ -7,11 +7,20 @@
#define LOG_OUTPUTFILES_HASHTABLE_SIZE 512
#define LOG_MAX_ID_LENGTH 32
+typedef enum encoding_mode_s {
+ normal_encoding,
+ json_encoding,
+ xml_encoding,
+
+ encoding_count
+} encoding_mode_t;
+
typedef struct log_outputfile_s
{
char *filename; // Expanded filename.
FILE *file; // The file pointer to the opened file.
unsigned long filename_hash; // A hash of the expanded filename.
+ encoding_mode_t encoding; // format to encode strings
struct log_outputfile_s *next; // Pointer to the next outputfile (if there's a collision in the hash table).
} log_outputfile_t;
@@ -29,7 +38,9 @@ typedef enum log_eventlogger_type_s
LOG_DEMOEND,
LOG_SPAWN,
LOG_ITEMPICKUP,
- LOG_WEAPONDROP
+ LOG_WEAPONDROP,
+ LOG_MATCHEND_FINAL,
+ LOG_MATCHEND_ALL_BETWEEN
} log_eventlogger_type_t;
typedef struct log_outputfile_template_s
diff --git a/mvd_parser.c b/mvd_parser.c
index 6e766cd..8257583 100644
--- a/mvd_parser.c
+++ b/mvd_parser.c
@@ -485,6 +485,7 @@ qbool MVD_Parser_StartParse(char *demopath, byte *mvdbuf, long filelen)
if (!mvd.serverinfo.match_ended)
{
int i;
+ qbool any = false;
Log_Event(&logger, &mvd, LOG_MATCHEND, -1);
@@ -496,8 +497,13 @@ qbool MVD_Parser_StartParse(char *demopath, byte *mvdbuf, long filelen)
continue;
}
+ if (any)
+ Log_Event(&logger, &mvd, LOG_MATCHEND_ALL_BETWEEN, i);
Log_Event(&logger, &mvd, LOG_MATCHEND_ALL, i);
+ any = true;
}
+
+ Log_Event(&logger, &mvd, LOG_MATCHEND_FINAL, -1);
}
Log_Event(&logger, &mvd, LOG_DEMOEND, -1);
diff --git a/mvdparser.sln b/mvdparser.sln
index 4cc9b95..d5e8e56 100644
--- a/mvdparser.sln
+++ b/mvdparser.sln
@@ -1,7 +1,9 @@
-Microsoft Visual Studio Solution File, Format Version 9.00
-# Visual Studio 2005
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mvdparser", "mvdparser.vcproj", "{116496EC-DD8D-4C48-8965-0D4961C76504}"
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30309.148
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mvdparser", "mvdparser.vcxproj", "{116496EC-DD8D-4C48-8965-0D4961C76504}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,4 +19,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B18BE4CC-108E-4DB5-9241-BC6FBCBA96EC}
+ EndGlobalSection
EndGlobal
diff --git a/mvdparser.vcxproj b/mvdparser.vcxproj
new file mode 100644
index 0000000..f101c90
--- /dev/null
+++ b/mvdparser.vcxproj
@@ -0,0 +1,122 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {116496EC-DD8D-4C48-8965-0D4961C76504}
+ mvdparser
+ Win32Proj
+
+
+
+ Application
+ v142
+ Unicode
+ true
+
+
+ Application
+ v142
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>16.0.30028.132
+
+
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ true
+
+
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ false
+
+
+
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+ Level3
+ EditAndContinue
+ CompileAsC
+
+
+ winmm.lib;%(AdditionalDependencies)
+ true
+ Console
+ 500000000
+ 500000000
+ MachineX86
+
+
+
+
+ WIN32;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+
+ Level3
+ ProgramDatabase
+
+
+ winmm.lib;%(AdditionalDependencies)
+ NotSet
+ $(OutDir)$(ProjectName).exe
+ false
+ Console
+ 500000000
+ 500000000
+ true
+ true
+ MachineX86
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mvdparser.vcxproj.filters b/mvdparser.vcxproj.filters
new file mode 100644
index 0000000..da8c11c
--- /dev/null
+++ b/mvdparser.vcxproj.filters
@@ -0,0 +1,81 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/net_msg.c b/net_msg.c
index e63dbf3..a17c8bb 100644
--- a/net_msg.c
+++ b/net_msg.c
@@ -4,6 +4,8 @@
#include "qw_protocol.h"
#include "net_msg.h"
+static qbool use_bigcoords = false;
+
sizebuf_t net_message;
int msg_readcount;
qbool msg_badread;
@@ -162,11 +164,17 @@ char *MSG_ReadStringLine(void)
float MSG_ReadCoord (void)
{
+ if (use_bigcoords) {
+ return MSG_ReadFloat();
+ }
return MSG_ReadShort() * (1.0f / 8);
}
float MSG_ReadAngle (void)
{
+ if (use_bigcoords) {
+ return MSG_ReadAngle16();
+ }
return MSG_ReadChar() * (360.0f / 256);
}
@@ -237,3 +245,8 @@ void MSG_ReadDeltaUsercmd(usercmd_t *from, usercmd_t *move, int protoversion)
move->msec = MSG_ReadByte (); // always sent
}
}
+
+void MSG_SetBigCoordSupport(qbool enabled)
+{
+ use_bigcoords = enabled;
+}
diff --git a/net_msg.h b/net_msg.h
index 0a5c9ca..01a4311 100644
--- a/net_msg.h
+++ b/net_msg.h
@@ -26,5 +26,6 @@ float MSG_ReadAngle16 (void);
#define CM_MSEC (1 << 7) // same as CM_ANGLE2
void MSG_ReadDeltaUsercmd(usercmd_t *from, usercmd_t *move, int protoversion);
+void MSG_SetBigCoordSupport(qbool enabled);
#endif // __NET_MSG_H__
diff --git a/netmsg_parser.c b/netmsg_parser.c
index 4f5af98..cbc8df6 100644
--- a/netmsg_parser.c
+++ b/netmsg_parser.c
@@ -659,6 +659,9 @@ static void NetMsg_Parser_Parse_svc_print(mvd_info_t *mvd)
strlcpy(str, MSG_ReadString(), sizeof(str));
// TODO : Check for frag messages spread over several svc_prints older mods/servers does this crap :(
+ if (str[strlen(str) - 1] == '\n') {
+ str[strlen(str) - 1] = '\0';
+ }
Sys_PrintDebug(5, "svc_print: (%s) RAW: %s\n", print_strings[level], str);
Sys_PrintDebug(1, "svc_print: (%s) %s\n", print_strings[level], Sys_RedToWhite(str));
@@ -711,6 +714,7 @@ static void NetMsg_Parser_Parse_svc_print(mvd_info_t *mvd)
else if (!strncmp(str, "The match is over", 17))
{
int i;
+ qbool any = false;
mvd->serverinfo.match_ended = true;
Log_Event(&logger, mvd, LOG_MATCHEND, -1);
@@ -723,8 +727,13 @@ static void NetMsg_Parser_Parse_svc_print(mvd_info_t *mvd)
continue;
}
+ if (any)
+ Log_Event(&logger, mvd, LOG_MATCHEND_ALL_BETWEEN, i);
Log_Event(&logger, mvd, LOG_MATCHEND_ALL, i);
+ any = true;
}
+
+ Log_Event(&logger, mvd, LOG_MATCHEND_FINAL, -1);
}
else if (strstr(str, "overtime follows"))
{
@@ -782,7 +791,7 @@ static void NetMsg_Parser_ParseServerInfo(mvd_info_t *mvd)
snprintf(mvd->serverinfo.mod, sizeof(mvd->serverinfo.mod), "KTX %s build %s", tmp, Info_ValueForKey(mvd->serverinfo.serverinfo, "xbuild"));
}
- strlcpy(mvd->serverinfo.mapname, Info_ValueForKey(mvd->serverinfo.serverinfo, "map"), sizeof(mvd->serverinfo.mapname));
+ strlcpy(mvd->serverinfo.mapfile, Info_ValueForKey(mvd->serverinfo.serverinfo, "map"), sizeof(mvd->serverinfo.mapfile));
strlcpy(mvd->serverinfo.serverversion, Info_ValueForKey(mvd->serverinfo.serverinfo, "*version"), sizeof(mvd->serverinfo.serverversion));
strlcpy(mvd->serverinfo.status, Info_ValueForKey(mvd->serverinfo.serverinfo, "status"), sizeof(mvd->serverinfo.status));
}
@@ -818,7 +827,26 @@ static void NetMsg_Parser_Parse_svc_setangle(void)
static void NetMsg_Parser_Parse_svc_serverdata(mvd_info_t *mvd)
{
- mvd->serverinfo.protocol_version = MSG_ReadLong();
+ int protocol;
+
+ while (true) {
+ protocol = MSG_ReadLong();
+ if (protocol == PROTOCOL_VERSION_FTE) {
+ mvd->extension_flags_fte1 = MSG_ReadLong();
+ MSG_SetBigCoordSupport(mvd->extension_flags_fte1 & FTE_PEXT_FLOATCOORDS);
+ }
+ else if (protocol == PROTOCOL_VERSION_FTE2) {
+ mvd->extension_flags_fte2 = MSG_ReadLong();
+ }
+ else if (protocol == PROTOCOL_VERSION_MVD1) {
+ mvd->extension_flags_mvd = MSG_ReadLong();
+ }
+ else {
+ break;
+ }
+ }
+
+ mvd->serverinfo.protocol_version = protocol;
mvd->serverinfo.servercount = MSG_ReadLong();
// Gamedir.
diff --git a/qw_protocol.h b/qw_protocol.h
index 055d989..3a171de 100644
--- a/qw_protocol.h
+++ b/qw_protocol.h
@@ -462,34 +462,35 @@ typedef struct movevars_s
typedef struct server_s
{
- char serverinfo[2 * MAX_SERVERINFO_STRING]; // Make it larger than allowed.
- int protocol_version;
- int servercount;
- float demotime;
- int timelimit;
- int fraglimit;
- int teamplay;
- int deathmatch;
- qbool watervis;
- char serverversion[MAX_INFO_KEY];
- int maxclients;
- int maxspectators;
- char mapname[MAX_INFO_KEY];
- int fpd;
- char status[MAX_INFO_KEY];
- char gamedir[MAX_QPATH];
- movevars_t movevars;
- qbool countdown;
- qbool match_started;
- qbool match_ended;
- qbool match_overtime;
- int overtime_minutes;
- int maxfps;
- int zext;
- char hostname[MAX_INFO_STRING];
- char mod[MAX_INFO_STRING];
- qbool player_timed_out;
- int player_timout_frame;
+ char serverinfo[2 * MAX_SERVERINFO_STRING]; // Make it larger than allowed.
+ int protocol_version;
+ int servercount;
+ float demotime;
+ int timelimit;
+ int fraglimit;
+ int teamplay;
+ int deathmatch;
+ qbool watervis;
+ char serverversion[MAX_INFO_KEY];
+ int maxclients;
+ int maxspectators;
+ char mapname[MAX_INFO_KEY]; // now the friendly name (Blood Run)
+ char mapfile[MAX_INFO_KEY]; // now the name of the file (ztndm3)
+ int fpd;
+ char status[MAX_INFO_KEY];
+ char gamedir[MAX_QPATH];
+ movevars_t movevars;
+ qbool countdown;
+ qbool match_started;
+ qbool match_ended;
+ qbool match_overtime;
+ int overtime_minutes;
+ int maxfps;
+ int zext;
+ char hostname[MAX_INFO_STRING];
+ char mod[MAX_INFO_STRING];
+ qbool player_timed_out;
+ int player_timout_frame;
} server_t;
typedef enum log_eventtype_s
@@ -546,6 +547,10 @@ typedef struct mvd_info_s
log_event_t *log_events_tail;
log_event_t *log_events_head;
+
+ unsigned int extension_flags_fte1;
+ unsigned int extension_flags_fte2;
+ unsigned int extension_flags_mvd;
} mvd_info_t;
// usercmd button bits
@@ -584,4 +589,11 @@ typedef struct temp_entity_list_s
temp_entity_list_t temp_entities;
+// Protocol extensions
+#define PROTOCOL_VERSION_FTE (('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions.
+#define PROTOCOL_VERSION_FTE2 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24)) //fte extensions.
+#define PROTOCOL_VERSION_MVD1 (('M'<<0) + ('V'<<8) + ('D'<<16) + ('1' << 24)) //mvdsv extensions.
+
+#define FTE_PEXT_FLOATCOORDS 0x00008000
+
#endif // __QW_PROTOCOL_H__
diff --git a/shared.c b/shared.c
index bc4c27c..8d61354 100644
--- a/shared.c
+++ b/shared.c
@@ -186,7 +186,8 @@ size_t strlcat(char *dst, const char *src, size_t siz)
return(dlen + (s - src)); /* count does not include NUL */
}
-#ifdef _MSC_VER
+// Implemented in later versions of Visual Studio (https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010)
+#if defined(_MSC_VER) && _MSC_VER < 1900
int snprintf(char *buffer, size_t count, char const *format, ...)
{
int ret;
@@ -288,7 +289,8 @@ qbool COM_ReadFile(const char *filename, byte **data, long *filelen)
return false;
}
- (*data) = Q_malloc(sizeof(byte) * len);
+ (*data) = Q_malloc(sizeof(byte) * (len + 1));
+ (*data)[len] = '\0';
num_read = fread((*data), 1, len, f);
diff --git a/template.dat b/template.dat
index a798be3..ae735bf 100644
--- a/template.dat
+++ b/template.dat
@@ -1,130 +1,216 @@
-
-////////////////////////////////////////////////////////////
-
-#FILE server %demoname%-server.log
-
-#EVENT MATCHEND 0
-hostname=%hostname%
-description=""
-address=""
-#EVENT_END
-
-#OUTPUT 0 server
-
-////////////////////////////////////////////////////////////
-
-#FILE map %demoname%-map.log
-
-#EVENT MATCHEND 1
-map=%map%
-#EVENT_END
-
-#OUTPUT 1 map
-
-////////////////////////////////////////////////////////////
-
-#FILE serversettings %demoname%-serversettings.log
-
-// STR_TO_DATE('%matchstartyear%-%matchstartmonth%-%matchstartdate% %matchstarthour%:%matchstartminute%', '%%Y-%%c-%%e %%k:%%i')
-
-#EVENT MATCHEND 2
-gamedir=%gamedir%
-serverinfo=%serverinfo%
-fraglimit=%fraglimit%
-timelimit=%timelimit%
-deathmatch=%deathmatchmode%
-maxfps=%maxfps%
-teamplay=%teamplay%
-z_ext=%zext%
-fpd=%fpd%
-maxclients=%maxclients%
-maxspectators=%maxspectators%
-watervis=%watervis%
-gametype=unknown
-version=%serverversion%
-mod=%mod%
-#EVENT_END
-
-#OUTPUT 2 serversettings
-
-////////////////////////////////////////////////////////////
-
-#FILE demo %demoname%-demo.log
-
-#EVENT MATCHEND 3
-mvdframe=%mvdframe%
-demotime=%demotime%
-matchstartdate=%matchstartfulldate%
-#EVENT_END
-
-#OUTPUT 3 demo
-
-////////////////////////////////////////////////////////////
-
-#FILE player %demoname%-%playernum%-player.log
-
-#EVENT MATCHEND_ALL 4
-client=%client%
-name=%name%
-name_raw=%nameraw%
-player_num=%playernum%
-player_id=%playerid%
-frags=%frags%
-spectator=%spectator%
-userinfo=%userinfo%
-top_color=%topcolor%
-bottom_color=%bottomcolor%
-teamname=%team%
-teamname_raw=%teamraw%
-deaths=%deaths%
-avg_packetloss=%avgpl%
-max_packetloss=%maxpl%
-min_packetloss=%minpl%
-avg_ping=%avgping%
-max_ping=%maxping%
-min_ping=%minping%
-ga_count=%gacount%
-ya_count=%yacount%
-ra_count=%racount%
-ssg_count=%ssgcount%
-ng_count=%ngcount%
-sng_count=%sngcount%
-gl_count=%glcount%
-rl_count=%rlcount%
-lg_count=%lgcount%
-ssg_drop=%ssgdrop%
-ng_drop=%ngdrop%
-sng_drop=%sngdrop%
-gl_drop=%gldrop%
-rl_drop=%rldrop%
-lg_drop=%lgdrop%
-sg_shots=%sgshots%
-ssg_shots=%ssgshots%
-ng_shots=%ngshots%
-sng_shots=%sngshots%
-gl_shots=%glshots%
-rl_shots=%rlshots%
-lg_shots=%lgshots%
-mh_count=%mhcount%
-quad_count=%quadcount%
-pent_count=%pentcount%
-ring_count=%ringcount%
-avg_speed=%avgspeed%
-max_speed=%maxspeed%
-distance_moved=%distancemoved%
-#EVENT_END
-
-#OUTPUT 4 player
-
-////////////////////////////////////////////////////////////
-
-#FILE events %demoname%-%playernum%-events.log
-
-#EVENT DEATH 5
-demotime=%demotime%;matchtime=%matchtime%;quake_loc_x=%posx%;quake_loc_y=%posy%;quake_loc_z=%posz%;player=%playernum%;type=DEATH
-#EVENT_END
-
-#OUTPUT 5 events
-
-////////////////////////////////////////////////////////////
-
+
+////////////////////////////////////////////////////////////
+
+#FILE server %demoname%-server.log
+
+#EVENT MATCHEND 0
+hostname=%hostname%
+description=""
+address=""
+#EVENT_END
+
+#OUTPUT 0 server
+
+////////////////////////////////////////////////////////////
+
+#FILE map %demoname%-map.log
+
+#EVENT MATCHEND 1
+map=%map%
+#EVENT_END
+
+#OUTPUT 1 map
+
+////////////////////////////////////////////////////////////
+
+#FILE serversettings %demoname%-serversettings.log
+
+// STR_TO_DATE('%matchstartyear%-%matchstartmonth%-%matchstartdate% %matchstarthour%:%matchstartminute%', '%%Y-%%c-%%e %%k:%%i')
+
+#EVENT MATCHEND 2
+gamedir=%gamedir%
+serverinfo=%serverinfo%
+fraglimit=%fraglimit%
+timelimit=%timelimit%
+deathmatch=%deathmatchmode%
+maxfps=%maxfps%
+teamplay=%teamplay%
+z_ext=%zext%
+fpd=%fpd%
+maxclients=%maxclients%
+maxspectators=%maxspectators%
+watervis=%watervis%
+gametype=unknown
+version=%serverversion%
+mod=%mod%
+#EVENT_END
+
+#OUTPUT 2 serversettings
+
+////////////////////////////////////////////////////////////
+
+#FILE demo %demoname%-demo.log
+
+#EVENT MATCHEND 3
+mvdframe=%mvdframe%
+demotime=%demotime%
+matchstartdate=%matchstartfulldate%
+#EVENT_END
+
+#OUTPUT 3 demo
+
+////////////////////////////////////////////////////////////
+
+#FILE player %demoname%-%playernum%-player.log
+
+#EVENT MATCHEND_ALL 4
+client=%client%
+name=%name%
+name_raw=%nameraw%
+player_num=%playernum%
+player_id=%playerid%
+frags=%frags%
+spectator=%spectator%
+userinfo=%userinfo%
+top_color=%topcolor%
+bottom_color=%bottomcolor%
+teamname=%team%
+teamname_raw=%teamraw%
+deaths=%deaths%
+avg_packetloss=%avgpl%
+max_packetloss=%maxpl%
+min_packetloss=%minpl%
+avg_ping=%avgping%
+max_ping=%maxping%
+min_ping=%minping%
+ga_count=%gacount%
+ya_count=%yacount%
+ra_count=%racount%
+ssg_count=%ssgcount%
+ng_count=%ngcount%
+sng_count=%sngcount%
+gl_count=%glcount%
+rl_count=%rlcount%
+lg_count=%lgcount%
+ssg_drop=%ssgdrop%
+ng_drop=%ngdrop%
+sng_drop=%sngdrop%
+gl_drop=%gldrop%
+rl_drop=%rldrop%
+lg_drop=%lgdrop%
+sg_shots=%sgshots%
+ssg_shots=%ssgshots%
+ng_shots=%ngshots%
+sng_shots=%sngshots%
+gl_shots=%glshots%
+rl_shots=%rlshots%
+lg_shots=%lgshots%
+mh_count=%mhcount%
+quad_count=%quadcount%
+pent_count=%pentcount%
+ring_count=%ringcount%
+avg_speed=%avgspeed%
+max_speed=%maxspeed%
+distance_moved=%distancemoved%
+#EVENT_END
+
+#OUTPUT 4 player
+
+////////////////////////////////////////////////////////////
+
+#FILE events %demoname%-%playernum%-events.log
+
+#EVENT DEATH 5
+demotime=%demotime%;matchtime=%matchtime%;quake_loc_x=%posx%;quake_loc_y=%posy%;quake_loc_z=%posz%;player=%playernum%;type=DEATH
+#EVENT_END
+
+#OUTPUT 5 events
+
+////////////////////////////////////////////////////////////
+
+#FILE json %demoname%.json
+
+#EVENT MATCHEND 6
+{
+ "hostname": "%hostname%",
+ "map_name": "%mapname%",
+ "map_file": "maps/%map%.bsp",
+ "gamedir": "%gamedir%",
+ "serverinfo": "%serverinfo%",
+ "fraglimit": "%fraglimit%",
+ "timelimit": "%timelimit%",
+ "deathmatch": "%deathmatchmode%",
+ "maxfps": "%maxfps%",
+ "teamplay": "%teamplay%",
+ "z_ext": "%zext%",
+ "fpd": "%fpd%",
+ "maxclients": "%maxclients%",
+ "maxspectators": "%maxspectators%",
+ "watervis": "%watervis%",
+ "gametype": "unknown",
+ "version": "%serverversion%",
+ "mod": "%mod%",
+ "players": [
+#EVENT_END
+
+#EVENT MATCHEND_ALL 7
+ {
+ "client": "%client%",
+ "name_sanatized": "%name%",
+ "name_raw": "%nameraw%",
+ "player_num": "%playernum%",
+ "player_id": "%playerid%",
+ "frags": "%frags%",
+ "spectator": "%spectator%",
+ "userinfo": "%userinfo%",
+ "top_color": "%topcolor%",
+ "bottom_color": "%bottomcolor%",
+ "team_sanatized": "%team%",
+ "team_raw": "%teamraw%",
+ "avg_packetloss": "%avgpl%",
+ "max_packetloss": "%maxpl%",
+ "min_packetloss": "%minpl%",
+ "avg_ping": "%avgping%",
+ "max_ping": "%maxping%",
+ "min_ping": "%minping%",
+ "sg_shots": "%sgshots%",
+ "avg_speed": "%avgspeed%",
+ "max_speed": "%maxspeed%",
+ "distance_moved": "%distancemoved%",
+ "stats": {
+ "Axe":{"Damage":0,"Drop":0,"Pickup":0},
+ "Deaths": "%deaths%",
+ "GreenArmor":{"Damage_Absorbed":0,"Pickup":%gacount%},
+ "GrenadeLauncher":{"Damage":0,"Drop":%gldrop%,"Pickup":%glcount%,"Shots":%glshots%},
+ "Kills":27,
+ "LightningGun":{"Damage":0,"Drop":%lgdrop%,"Pickup":%lgcount%,"Shots":%lgshots%},
+ "MegaHealth":{"Drop":0,"Pickup":%mhcount%},
+ "NailGun":{"Damage":0,"Drop":%ngdrop%,"Pickup":%ngcount%,"Shots":%ngshots%},
+ "Pentagram":{"Drop":0,"Pickup":%pentcount%},
+ "Quad":{"Drop":0,"Pickup":%quadcount%},
+ "RedArmor":{"Damage_Absorbed":0,"Pickup":%racount%},
+ "Ring":{"Drop":0,"Pickup":%ringcount%},
+ "RocketLauncher":{"Damage":0,"Drop":%rldrop%,"Pickup":%rlcount%,"Shots":%rlshots%},
+ "Shotgun":{"Damage":0,"Drop":0,"Pickup":0},
+ "Suicides":%suicides%,
+ "SuperNailGun":{"Damage":0,"Drop":%sngdrop%,"Pickup":%sngcount%,"Shots":%sngshots%},
+ "SuperShotgun":{"Damage":0,"Drop":%ssgdrop%,"Pickup":%ssgcount%,"Shots":%ssgshots%},
+ "Teamkills":%teamkills%,
+ "YellowArmor":{"Damage_Absorbed":0,"Pickup":%yacount%}
+ }
+ }
+#EVENT_END
+
+#EVENT MATCHEND_ALL_BETWEEN 8
+ ,
+#EVENT_END
+
+#EVENT MATCHEND_FINAL 9
+ ]
+}
+#EVENT_END
+
+#OUTPUT 6 json
+#OUTPUT 7 json
+#OUTPUT 8 json
+#OUTPUT 9 json