Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce API events & functions to work w/replays #358

Draft
wants to merge 1 commit into
base: 2.4
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 84 additions & 15 deletions include/bzfsAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ typedef enum
bz_ePermissionModificationEvent,
bz_eAllowServerShotFiredEvent,
bz_ePlayerDeathFinalizedEvent,
bz_eReplayRequestedEvent,
bz_eReplayLoadedEvent,
bz_eRecordingStartedEvent,
bz_eRecordingEndedEvent,
bz_eLastEvent //this is never used as an event, just show it's the last one
} bz_eEventType;

Expand Down Expand Up @@ -489,6 +493,23 @@ typedef struct bz_PlayerUpdateState

BZF_API bool bz_freePlayerRecord ( bz_BasePlayerRecord *playerRecord );

// Time utilities
typedef struct
{
int year;
int month;
int day;
int hour;
int minute;
int second;
int dayofweek;
bool daylightSavings;
} bz_Time;

BZF_API void bz_getLocaltime(bz_Time *ts);
BZF_API void bz_getUTCtime(bz_Time *ts);
BZF_API void bz_makeApiTime(time_t time, bz_Time* apiTime);

// event data types
class BZF_API bz_EventData
{
Expand Down Expand Up @@ -1465,6 +1486,65 @@ class BZF_API bz_PermissionModificationData_V1 : public bz_EventData
bool customPerm;
};

class BZF_API bz_RecordingStartedEventData_V1 : public bz_EventData
{
public:
bz_RecordingStartedEventData_V1() : bz_EventData(bz_eRecordingStartedEvent)
, playerID(-1)
{}

int playerID;
};

class BZF_API bz_RecordingEndedEventData_V1 : public bz_EventData
{
public:
bz_RecordingEndedEventData_V1() : bz_EventData(bz_eRecordingEndedEvent)
, playerID(-1)
{}

int playerID;
};

class BZF_API bz_ReplayRequestedEventData_V1 : public bz_EventData
{
public:
bz_ReplayRequestedEventData_V1() : bz_EventData(bz_eReplayRequestedEvent)
, playerID(-1)
, success(true)
, errorMsg("")
, filename("")
{}

int playerID;
bool success;
const char* errorMsg;
const char* filename;
};

class BZF_API bz_ReplayLoadedEventData_V1 : public bz_EventData
{
public:
bz_ReplayLoadedEventData_V1() : bz_EventData(bz_eReplayLoadedEvent)
, filename("")
, authorCallsign("")
, authorMotto("")
, serverVersion("")
, seconds(-1.0)
, start(NULL)
, end(NULL)
{}

const char* filename;
const char* authorCallsign;
const char* authorMotto;
const char* protocol;
const char* serverVersion;
float seconds;
bz_Time* start;
bz_Time* end;
};

// logging
BZF_API void bz_debugMessage ( int debugLevel, const char* message );
BZF_API void bz_debugMessagef( int debugLevel, const char* fmt, ... );
Expand Down Expand Up @@ -1742,21 +1822,6 @@ BZF_API uint32_t bz_getShotGUID (int fromPlayer, int shotID);
BZF_API bool bz_vectorFromPoints(const float p1[3], const float p2[3], float outVec[3]);
BZF_API bool bz_vectorFromRotations(const float tilt, const float rotation, float outVec[3]);

typedef struct
{
int year;
int month;
int day;
int hour;
int minute;
int second;
int dayofweek;
bool daylightSavings;
} bz_Time;

BZF_API void bz_getLocaltime(bz_Time *ts);
BZF_API void bz_getUTCtime(bz_Time *ts);

// BZDB API
BZF_API double bz_getBZDBDouble ( const char* variable );
BZF_API bz_ApiString bz_getBZDBString( const char* variable );
Expand Down Expand Up @@ -2145,6 +2210,10 @@ BZF_API bz_ApiString bz_filterPath ( const char* path );
BZF_API bool bz_saveRecBuf( const char * _filename, int seconds = 0);
BZF_API bool bz_startRecBuf( void );
BZF_API bool bz_stopRecBuf( void );
BZF_API bool bz_isReplayServer( void );
BZF_API bool bz_loadReplay( const char* _filename, int playerIndex = BZ_SERVERPLAYER );
BZF_API bool bz_replayExists( const char* _filename );
BZF_API bool bz_unloadReplay( int playerIndex = BZ_SERVERPLAYER );

// cheap Text Utils
BZF_API const char *bz_format(const char* fmt, ...);
Expand Down
155 changes: 123 additions & 32 deletions src/bzfs/RecordReplay.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "StateDatabase.h"
#include "DirectoryNames.h"
#include "NetHandler.h"
#include "WorldEventManager.h"
#include "md5.h"
#include "Score.h"
#include "version.h"
Expand Down Expand Up @@ -249,6 +250,11 @@ bool Record::start(int playerIndex)
saveStates();
sendMessage(ServerPlayer, playerIndex, "Recording started");

bz_RecordingStartedEventData_V1 eventData;
eventData.playerID = playerIndex;

worldEventManager.callEvents(bz_eRecordingStartedEvent, &eventData);

return true;
}

Expand All @@ -261,6 +267,11 @@ bool Record::stop(int playerIndex)
return false;
}

bz_RecordingEndedEventData_V1 eventData;
eventData.playerID = playerIndex;

worldEventManager.callEvents(bz_eRecordingEndedEvent, &eventData);

sendMessage(ServerPlayer, playerIndex, "Recording stopped");

recordReset();
Expand Down Expand Up @@ -748,32 +759,40 @@ bool Replay::loadFile(int playerIndex, const char *filename)
std::string name = RecordDir;
name += filename;

replayReset();
resetStates();
unloadFile(playerIndex);

bz_ReplayRequestedEventData_V1 requestedEventData;
requestedEventData.filename = filename;
requestedEventData.playerID = playerIndex;

ReplayFile = openFile(filename, "rb");

if (ReplayFile == NULL)
{
snprintf(buffer, MessageLen, "Could not open: %s", name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
return false;
requestedEventData.success = false;
}

if (!loadHeader(&header, ReplayFile))
else if (!loadHeader(&header, ReplayFile))
{
snprintf(buffer, MessageLen, "Could not open header: %s", name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
fclose(ReplayFile);
ReplayFile = NULL;
return false;
requestedEventData.success = false;
}

if (header.magic != ReplayMagic)
else if (header.magic != ReplayMagic)
{
snprintf(buffer, MessageLen, "Not a bzflag replay file: %s", name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
fclose(ReplayFile);
ReplayFile = NULL;
requestedEventData.success = false;
}

if (!requestedEventData.success)
{
sendMessage(ServerPlayer, playerIndex, requestedEventData.errorMsg = buffer);

worldEventManager.callEvents(bz_eReplayRequestedEvent, &requestedEventData);

return false;
}

Expand All @@ -790,51 +809,74 @@ bool Replay::loadFile(int playerIndex, const char *filename)
if (ReplayBuf.tail == NULL)
{
snprintf(buffer, MessageLen, "No valid data: %s", name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
replayReset();
return false;

requestedEventData.success = false;
}
else
{
ReplayPos = ReplayBuf.tail; // setup the initial position
ReplayFileTime = header.filetime;
ReplayStartTime = ReplayPos->timestamp;

ReplayPos = ReplayBuf.tail; // setup the initial position
ReplayFileTime = header.filetime;
ReplayStartTime = ReplayPos->timestamp;
if (!preloadVariables())
{
snprintf(buffer, MessageLen, "Could not preload variables: %s", name.c_str());
replayReset();

if (!preloadVariables())
{
snprintf(buffer, MessageLen, "Could not preload variables: %s",
name.c_str());
sendMessage(ServerPlayer, playerIndex, buffer);
replayReset();
return false;
requestedEventData.success = false;
}
}

if (strlen(requestedEventData.errorMsg = buffer) > 0)
sendMessage(ServerPlayer, playerIndex, requestedEventData.errorMsg);

worldEventManager.callEvents(bz_eReplayRequestedEvent, &requestedEventData);

if (!requestedEventData.success)
return false;

ReplayFilename = filename;

snprintf(buffer, MessageLen, "Loaded file: %s", name.c_str());
bz_ReplayLoadedEventData_V1 loadedEventData;
loadedEventData.filename = name.c_str();
loadedEventData.authorCallsign = header.callSign;
loadedEventData.authorMotto = header.motto;
loadedEventData.protocol = header.ServerVersion;
loadedEventData.serverVersion = header.appVersion;
loadedEventData.seconds = (float)header.filetime / 1000000.0f;

snprintf(buffer, MessageLen, "Loaded file: %s", loadedEventData.filename);
sendMessage(ServerPlayer, playerIndex, buffer);
snprintf(buffer, MessageLen, " author: %s (%.79s)",
header.callSign, header.motto);
snprintf(buffer, MessageLen, " author: %s (%.79s)", loadedEventData.authorCallsign, loadedEventData.authorMotto);
sendMessage(ServerPlayer, playerIndex, buffer);
snprintf(buffer, MessageLen, " protocol: %.8s", header.ServerVersion);
snprintf(buffer, MessageLen, " protocol: %.8s", loadedEventData.protocol);
sendMessage(ServerPlayer, playerIndex, buffer);
snprintf(buffer, MessageLen, " server: %.113s", header.appVersion);
snprintf(buffer, MessageLen, " server: %.113s", loadedEventData.serverVersion);
sendMessage(ServerPlayer, playerIndex, buffer);
snprintf(buffer, MessageLen, " seconds: %.1f",
(float)header.filetime/1000000.0f);
snprintf(buffer, MessageLen, " seconds: %.1f", loadedEventData.seconds);
sendMessage(ServerPlayer, playerIndex, buffer);

time_t startTime = (time_t)(ReplayPos->timestamp / 1000000);
bz_makeApiTime(startTime, loadedEventData.start);
snprintf(buffer, MessageLen, " start: %s", ctime(&startTime));
sendMessage(ServerPlayer, playerIndex, buffer);

time_t endTime =
(time_t)((header.filetime + ReplayPos->timestamp) / 1000000);
time_t endTime = (time_t)((header.filetime + ReplayPos->timestamp) / 1000000);
bz_makeApiTime(endTime, loadedEventData.end);
snprintf(buffer, MessageLen, " end: %s", ctime(&endTime));
sendMessage(ServerPlayer, playerIndex, buffer);

worldEventManager.callEvents(bz_eReplayLoadedEvent, &loadedEventData);

return true;
}

bool Replay::unloadFile(int playerIndex)
{
return replayReset() && resetStates();
}


static FILE *getRecordFile(const char *filename)
{
Expand Down Expand Up @@ -1053,6 +1095,55 @@ bool Replay::sendFileList(int playerIndex, const char* options)
return true;
}

bool Replay::exists(const char* filename)
{
#ifndef _MSC_VER

DIR *dir;
struct dirent *de;

if (!makeDirExist(RecordDir.c_str()))
return false;

dir = opendir(RecordDir.c_str());
if (dir == NULL)
return false;

while ((de = readdir(dir)) != NULL)
{
if (strcmp(de->d_name, filename) == 0)
return true;
}

closedir(dir);

return false;

#else // _MSC_VER

if (!makeDirExist(RecordDir.c_str()))
return false;

std::string pattern = RecordDir;
pattern += "*";
WIN32_FIND_DATA findData;
HANDLE h = FindFirstFile(pattern.c_str(), &findData);
if (h != INVALID_HANDLE_VALUE)
{
do
{
if (strcmp(findData.cFileName, filename))
return true;
}
while (FindNextFile(h, &findData));

FindClose(h);
}

return false

#endif // _MSC_VER
}

bool Replay::play(int playerIndex)
{
Expand Down
1 change: 1 addition & 0 deletions src/bzfs/RecordReplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ extern bool init (); // must be done before any players join
extern bool kill ();

extern bool sendFileList (int playerIndex, const char* options);
extern bool exists(const char* filename);
extern bool loadFile (int playerIndex, const char *filename);
extern bool unloadFile (int playerIndex);
extern bool play (int playerIndex);
Expand Down
Loading
Loading