Skip to content

Commit

Permalink
* added replay capability! included one replay file. documented in ac…
Browse files Browse the repository at this point in the history
…companying .txt file. this should help with debugging.
  • Loading branch information
ivucica committed Jul 21, 2009
1 parent a2efe22 commit b2b58ae
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 18 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Expand Up @@ -23,6 +23,7 @@ yatc_SOURCES = $(MAYBE_OPENGL) debugprint.cpp enginesdl.cpp gm_mainmenu.cpp \
bigint.cpp automap.cpp choicegrid.cpp stackpanel.cpp \
statusmsg.cpp clipboard.cpp \
\
net/connectionreplay.cpp \
net/connection.cpp net/networkmessage.cpp net/protocolgame80.cpp \
net/protocolgame81.cpp net/protocolgame811.cpp net/protocolgame82.cpp \
net/protocolgame821.cpp net/protocolgame822.cpp net/protocolgame83.cpp \
Expand Down Expand Up @@ -50,6 +51,7 @@ EXTRA_DIST = debugprint.h enginesdl.h gm_mainmenu.h objects.h spritegl.h \
bigint.h fassert.h stdinttypes.h popup.h automap.h \
choicegrid.h stackpanel.h statusmsg.h clipboard.h \
\
net/connectionreplay.h \
net/connection.h net/networkmessage.h net/protocolgame80.h \
net/protocolgame81.h net/protocolgame811.h net/protocolgame82.h \
net/protocolgame821.h net/protocolgame822.h net/protocolgame83.h \
Expand Down
15 changes: 13 additions & 2 deletions main.cpp
Expand Up @@ -71,6 +71,7 @@ unsigned int MAXFPS=50;
#include "clipboard.h"

#include "net/connection.h"
#include "net/connectionreplay.h"
#include "net/protocollogin.h"
#include "net/protocolgame.h"
#include "gamecontent/creature.h"
Expand Down Expand Up @@ -474,8 +475,18 @@ int main(int argc, char *argv[])

DEBUGPRINT(DEBUGPRINT_LEVEL_OBLIGATORY, DEBUGPRINT_NORMAL, "Constructing gamemode...\n");
resetDefaultCursor();
g_game = new GM_MainMenu();
//g_game = new GM_Debug(); // ivucica: this is for testing -- choice should be a cmd line option
if (argc == 1)
{
g_game = new GM_MainMenu();
//g_game = new GM_Debug(); // ivucica: this is for testing -- choice should be a cmd line option
} else
{
g_game = new GM_MainMenu();
ProtocolGame* protocol = ProtocolConfig::createGameProtocol(850,"","","",false);
g_connection = new ConnectionReplay(argv[1], protocol);

}


DEBUGPRINT(DEBUGPRINT_LEVEL_OBLIGATORY, DEBUGPRINT_NORMAL, "Initializing framerate manager...\n");
SDL_initFramerate(&g_fpsmgr);
Expand Down
28 changes: 20 additions & 8 deletions net/connection.cpp
Expand Up @@ -250,13 +250,11 @@ void ProtocolConfig::createLoginConnection(const std::string& accountname, const
}
}

ProtocolGame* ProtocolConfig::createGameConnection(const std::string& accountname, const std::string& password, const std::string& name, bool isGM)
ProtocolGame* ProtocolConfig::createGameProtocol(int version, const std::string&accountname, const std::string&password, const std::string&name, bool isGM)
{
ASSERT(g_connection == NULL);

ProtocolGame* protocol;
switch(getInstance().m_clientVersion){
case CLIENT_VERSION_780:
ProtocolGame* protocol;
switch(version){
case CLIENT_VERSION_780:
protocol = new ProtocolGame78(accountname, password, name, isGM);
break;
// todo (nfries88): more client version protocols.
Expand Down Expand Up @@ -300,6 +298,16 @@ ProtocolGame* ProtocolConfig::createGameConnection(const std::string& accountnam
return NULL;
break;
}
return protocol;
}

ProtocolGame* ProtocolConfig::createGameConnection(const std::string& accountname, const std::string& password, const std::string& name, bool isGM)
{
ASSERT(g_connection == NULL);

ProtocolGame* protocol = createGameProtocol(getInstance().m_clientVersion,accountname,password,name,isGM);
if (protocol == NULL)
return NULL;

EncXTEA* crypto = new EncXTEA;

Expand Down Expand Up @@ -457,13 +465,17 @@ void Connection::executeNetwork()
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef WIN32
if(m_socket == INVALID_SOCKET){
closeConnectionError(ERROR_CANNOT_CREATE_SOCKET);
return;
}
#else
if(m_socket <= 0){
m_socket = INVALID_SOCKET;
#endif
closeConnectionError(ERROR_CANNOT_CREATE_SOCKET);
closeConnectionError(ERROR_CANNOT_CREATE_SOCKET);
return;
}
#endif


//Set non-blocking socket
#ifdef WIN32
Expand Down
17 changes: 9 additions & 8 deletions net/connection.h
Expand Up @@ -148,9 +148,11 @@ class ProtocolConfig

static void createLoginConnection(const std::string& accountname, const std::string& password);
static ProtocolGame* createGameConnection(const std::string& accountname, const std::string& password, const std::string& name, bool isGM);
static ProtocolGame* createGameProtocol(int version, const std::string&accountname, const std::string&password, const std::string&name, bool isGM);

protected:


ClientOS_t m_os;
ClientVersion_t m_clientVersion;
uint16_t m_overrideVersion;
Expand Down Expand Up @@ -251,7 +253,7 @@ class Protocol
class Connection
{
public:
~Connection();
virtual ~Connection();

enum STATE
{
Expand Down Expand Up @@ -286,13 +288,13 @@ class Connection
};
static const char* getErrorDesc(int message);

void executeNetwork();
virtual void executeNetwork();

void closeConnection();
virtual void closeConnection();
STATE getState(){ return m_state; }
int getSocketError();

void sendMessage(NetworkMessage& msg);
virtual void sendMessage(NetworkMessage& msg);

bool getChecksumState() const { return m_checksumEnable; }
void setCryptoState(bool state){ m_cryptoEnable = state;}
Expand All @@ -315,13 +317,12 @@ class Connection
Connection(const std::string& host, uint16_t port, Encryption* crypto, Protocol* protocol);
friend class ProtocolConfig;

private:
//functions
void callCallback(int error);
unsigned long getPendingInput();
int internalRead(unsigned int n, bool all);
virtual unsigned long getPendingInput();
virtual int internalRead(unsigned int n, bool all);
void closeConnectionError(int error);
void checkSocketReadState();
virtual void checkSocketReadState();

//
Encryption* m_crypto;
Expand Down
237 changes: 237 additions & 0 deletions net/connectionreplay.cpp
@@ -0,0 +1,237 @@
//////////////////////////////////////////////////////////////////////
// Yet Another Tibia Client
//////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// 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, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//////////////////////////////////////////////////////////////////////

#include <SDL/SDL.h>
#include "connectionreplay.h"

// description of purpose included in connectionreplay.h

ConnectionReplay::ConnectionReplay(const std::string& file, Protocol* protocol) :
Connection(file,0,NULL,protocol) // we don't need crypto, we'll use m_host as storage for filename, port is of no use to us, etc
{
m_file = NULL; // at construction of connectionreplay, do nothing; open connection later, in execNetwork, at connection phase
m_nextticks = m_lastticks = 0;
}

ConnectionReplay::~ConnectionReplay()
{
}


void ConnectionReplay::executeNetwork()
{
printf("CR: executing net\n");
switch(m_state){
case STATE_INIT:
{
//Open file

m_ip = INADDR_NONE;
if (!(m_file=fopen(m_host.c_str(),"rb")))
{
closeConnectionError(ERROR_CANNOT_RESOLVE_HOST);
return;
}

//Delay since start of replay
//We have this as an increasing-from-zero var instead of just containing
//SDL_GetTicks()'s return value at start of the replay and then subtracting it
//because we want to be able to PAUSE the replay.
m_ticks = 0;
//Ticks during last executeNetwork()
m_lastticks = SDL_GetTicks();
//
fread(&m_nextticks,4,1,m_file);
printf("NEXTTICKS: %d\n", m_nextticks);

//Reset traffic counter
m_sentBytes = 0;
m_recvBytes = 0;

//And connect
//connection succeeds
m_state = STATE_CONNECTED;

m_inputMessage.reset();
break;
}
case STATE_CONNECTING:
{
// should never happen. still...
printf("ConnectionReplay::executeNetwork(): Warning: passed through STATE_CONNECTING.\n");
m_state = STATE_CONNECTED;
break;
}
case STATE_CONNECTED:
{
//Update ticks
m_ticks += SDL_GetTicks() - m_lastticks;
m_lastticks = SDL_GetTicks();
printf("Ticks: %d\n", m_ticks);

//Try to read messages
while(m_state == STATE_CONNECTED && getPendingInput() > 0){
switch(m_readState){
case READING_SIZE:
{
int ret = internalRead(2, true);
if(ret != 2){
checkSocketReadState();
return;
}
if(!m_inputMessage.getU16(m_msgSize)){
printf("Failed reading msg size\n");
closeConnectionError(ERROR_UNEXPECTED_RECV_ERROR);
return;
}
printf("MsgSize: %d\n", m_msgSize);
if(m_msgSize > NETWORK_MESSAGE_SIZE){
closeConnectionError(ERROR_TOO_BIG_MESSAGE);
return;
}
}
case READING_CHECKSUM: {
if(m_checksumEnable){
m_readState = READING_CHECKSUM;
int ret = internalRead(4, true);
if(ret != 4){
checkSocketReadState();
return;
}
uint32_t checksum;
if(!m_inputMessage.getU32(checksum)){
printf("Failed reading checksum\n");
closeConnectionError(ERROR_UNEXPECTED_RECV_ERROR);
return;
} else {
m_msgSize-=4;
m_readState = READING_MESSAGE;
}
} else
m_readState = READING_MESSAGE;
}
case READING_MESSAGE:
{
int ret = internalRead(m_msgSize, false);
if(ret <= 0){
checkSocketReadState();
return;
}
else if(ret != m_msgSize){
m_msgSize -= ret;
checkSocketReadState();

return;
}

//decrypt incoming message if needed
if(m_cryptoEnable && m_crypto){
if(!m_crypto->decrypt(m_inputMessage)){
closeConnectionError(ERROR_DECRYPT_FAIL);
return;
}
} else {
// this is 8.41 specific
// works for 8.40 too, however!
// data in unencrypted packets is now including unencrypted length,
// + encrypted length ... although packet is not encrypted
// let's just cut the cr/\p

// DONT DO THIS FOR REPLAY
/*uint16_t size = */ //m_inputMessage.getU16();
}
//raise onRecv event
if(!m_protocol->onRecv(m_inputMessage)){
closeConnectionError(ERROR_PROTOCOL_ONRECV);
return;
}
//resets input message state
m_readState = READING_SIZE;
m_inputMessage.reset();

fread(&m_nextticks, 4, 1, m_file);
//m_ticks = 0; // nextticks is a delta, let's hack
//m_lastticks = SDL_GetTicks();
break;
}
}
}
checkSocketReadState();
break;
}
case STATE_CLOSED:
case STATE_ERROR:
//nothing to do
break;
}
}

unsigned long ConnectionReplay::getPendingInput()
{
if (m_ticks < m_nextticks) // not ready yet for the next message
return 0;
switch(m_readState)
{
case READING_SIZE:
return 2;
break;
case READING_MESSAGE:
return m_msgSize;
break;
}
return 0;
}

void ConnectionReplay::closeConnection()
{
fclose(m_file);

m_state = STATE_CLOSED;
}

int ConnectionReplay::internalRead(unsigned int n, bool all)
{
fread(m_inputMessage.getReadBuffer() + m_inputMessage.getReadSize(), n, 1, m_file);
m_inputMessage.setReadSize(m_inputMessage.getReadSize() + n);

return n;
}

void ConnectionReplay::checkSocketReadState()
{
// FIXME should handle EOF
if(m_state != STATE_CONNECTED)
return;
/*
timeval tv = {0, 0}; //non-blocking select
fd_set read_set;
FD_ZERO(&read_set);
FD_SET(m_socket, &read_set);
int ret = select(m_socket + 1, &read_set, NULL, NULL, &tv);
if(ret == 1 && getPendingInput() == 0){
closeConnectionError(ERROR_CLOSED_SOCKET);
}
else if(ret == SOCKET_ERROR){
closeConnectionError(ERROR_SELECT_FAIL_CONNECTED);
}
*/
}

0 comments on commit b2b58ae

Please sign in to comment.