Upgrade d2dbs network processing to use fdwatch system and utilize other common PvPGN code
This commit is contained in:
parent
ac0cacfbd6
commit
16349d8163
22 changed files with 2670 additions and 1466 deletions
|
@ -4,7 +4,7 @@ set(COMMON_SOURCES
|
|||
bnettime.cpp bnettime.h bn_type.cpp bn_type.h bot_protocol.h conf.cpp
|
||||
conf.h d2char_checksum.cpp d2char_checksum.h d2char_file.h
|
||||
d2cs_bnetd_protocol.h d2cs_d2dbs_ladder.h d2cs_d2gs_character.h
|
||||
d2cs_d2gs_protocol.h d2cs_protocol.h d2game_protocol.h elist.h
|
||||
d2cs_d2gs_protocol.h d2cs_protocol.h d2dbs_d2gs_protocol.h d2game_protocol.h elist.h
|
||||
eventlog.cpp eventlog.h fdwatch.cpp fdwatch_epoll.cpp fdwatch_epoll.h
|
||||
fdwatch.h fdwatch_kqueue.cpp fdwatch_kqueue.h fdwatch_poll.cpp
|
||||
fdwatch_poll.h fdwatch_select.cpp fdwatch_select.h fdwbackend.cpp
|
||||
|
|
158
src/common/d2dbs_d2gs_protocol.h
Normal file
158
src/common/d2dbs_d2gs_protocol.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn)
|
||||
* Copyright (C) 2001 sousou (liupeng.cs@263.net)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_D2DBS_D2GS_PROTOCOL_H
|
||||
#define INCLUDED_D2DBS_D2GS_PROTOCOL_H
|
||||
|
||||
#include "common/bn_type.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bn_short size;
|
||||
bn_short type;
|
||||
bn_int seqno;
|
||||
} t_d2dbs_d2gs_header;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
} t_d2dbs_d2gs_generic;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bn_byte cclass;
|
||||
} t_d2gs_d2dbs_connect;
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2GS_D2DBS_SAVE_DATA_REQUEST 0x30
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_short datatype;
|
||||
bn_short datalen;
|
||||
/* AccountName */
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
/* data */
|
||||
} t_d2gs_d2dbs_save_data_request;
|
||||
#define D2GS_DATA_CHARSAVE 0x01
|
||||
#define D2GS_DATA_PORTRAIT 0x02
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2DBS_D2GS_SAVE_DATA_REPLY 0x30
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int result;
|
||||
bn_short datatype;
|
||||
/* CharName */
|
||||
} t_d2dbs_d2gs_save_data_reply;
|
||||
#define D2DBS_SAVE_DATA_SUCCESS 0
|
||||
#define D2DBS_SAVE_DATA_FAILED 1
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2GS_D2DBS_GET_DATA_REQUEST 0x31
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_short datatype;
|
||||
/* AccountName */
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
} t_d2gs_d2dbs_get_data_request;
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2DBS_D2GS_GET_DATA_REPLY 0x31
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int result;
|
||||
bn_int charcreatetime;
|
||||
bn_int allowladder;
|
||||
bn_short datatype;
|
||||
bn_short datalen;
|
||||
/* CharName */
|
||||
/* data */
|
||||
} t_d2dbs_d2gs_get_data_reply;
|
||||
|
||||
#define D2DBS_GET_DATA_SUCCESS 0
|
||||
#define D2DBS_GET_DATA_FAILED 1
|
||||
#define D2DBS_GET_DATA_CHARLOCKED 2
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2GS_D2DBS_UPDATE_LADDER 0x32
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int charlevel;
|
||||
bn_int charexplow;
|
||||
bn_int charexphigh;
|
||||
bn_short charclass;
|
||||
bn_short charstatus;
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
} t_d2gs_d2dbs_update_ladder;
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2GS_D2DBS_CHAR_LOCK 0x33
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int lockstatus;
|
||||
/* AccountName */
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
} t_d2gs_d2dbs_char_lock;
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2DBS_D2GS_ECHOREQUEST 0x34
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
} t_d2dbs_d2gs_echorequest;
|
||||
/******************************************************/
|
||||
|
||||
|
||||
/******************************************************/
|
||||
#define D2GS_D2DBS_ECHOREPLY 0x34
|
||||
typedef struct
|
||||
{
|
||||
t_d2dbs_d2gs_header h;
|
||||
} t_d2gs_d2dbs_echoreply;
|
||||
/******************************************************/
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -64,6 +64,7 @@ namespace pvpgn
|
|||
#define CLIENT_INITCONN_CLASS_D2CS 0x01
|
||||
#define CLIENT_INITCONN_CLASS_D2GS 0x64
|
||||
#define CLIENT_INITCONN_CLASS_D2CS_BNETD 0x65
|
||||
#define CLIENT_INITCONN_CLASS_D2GS_D2DBS 0x65
|
||||
#define CLIENT_INITCONN_CLASS_LOCALMACHINE 0x98 /* local computer connecting via 127.0.0.1 */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace pvpgn
|
|||
pclass != packet_class_d2cs &&
|
||||
pclass != packet_class_d2gs &&
|
||||
pclass != packet_class_d2cs_bnetd &&
|
||||
pclass != packet_class_d2dbs_d2gs &&
|
||||
pclass != packet_class_w3route &&
|
||||
pclass != packet_class_wolgameres)
|
||||
{
|
||||
|
@ -130,6 +131,8 @@ namespace pvpgn
|
|||
return packet_class_d2gs;
|
||||
case packet_class_d2cs_bnetd:
|
||||
return packet_class_d2cs_bnetd;
|
||||
case packet_class_d2dbs_d2gs:
|
||||
return packet_class_d2dbs_d2gs;
|
||||
case packet_class_w3route:
|
||||
return packet_class_w3route;
|
||||
case packet_class_wolgameres:
|
||||
|
@ -171,6 +174,8 @@ namespace pvpgn
|
|||
return "d2cs_bnetd";
|
||||
case packet_class_d2cs:
|
||||
return "d2cs";
|
||||
case packet_class_d2dbs_d2gs:
|
||||
return "d2dbs_d2gs";
|
||||
case packet_class_w3route:
|
||||
return "w3route";
|
||||
case packet_class_wolgameres:
|
||||
|
@ -205,6 +210,7 @@ namespace pvpgn
|
|||
pclass != packet_class_d2cs &&
|
||||
pclass != packet_class_d2gs &&
|
||||
pclass != packet_class_d2cs_bnetd &&
|
||||
pclass != packet_class_d2dbs_d2gs &&
|
||||
pclass != packet_class_w3route &&
|
||||
pclass != packet_class_wolgameres)
|
||||
{
|
||||
|
@ -287,6 +293,14 @@ namespace pvpgn
|
|||
}
|
||||
return bn_byte_get(packet->u.d2cs_client.h.type);
|
||||
|
||||
case packet_class_d2dbs_d2gs:
|
||||
if (packet_get_size(packet) < sizeof(t_d2dbs_d2gs_header))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "d2dbs_d2gs packet is shorter than header (len={})", packet_get_size(packet));
|
||||
return 0;
|
||||
}
|
||||
return bn_byte_get(packet->u.d2dbs_d2gs.h.type);
|
||||
|
||||
case packet_class_w3route:
|
||||
if (packet_get_size(packet) < sizeof(t_w3route_header))
|
||||
{
|
||||
|
@ -557,6 +571,9 @@ namespace pvpgn
|
|||
case packet_class_d2cs_bnetd:
|
||||
return "D2CS_BNETD";
|
||||
|
||||
case packet_class_d2dbs_d2gs:
|
||||
return "D2DBS_D2GS";
|
||||
|
||||
case packet_class_w3route:
|
||||
if (packet_get_size(packet) < sizeof(t_w3route_header))
|
||||
{
|
||||
|
@ -804,6 +821,9 @@ namespace pvpgn
|
|||
case packet_class_d2cs_bnetd:
|
||||
return "D2CS_BNETD";
|
||||
|
||||
case packet_class_d2dbs_d2gs:
|
||||
return "D2DBS_D2GS";
|
||||
|
||||
case packet_class_w3route:
|
||||
if (packet_get_size(packet) < sizeof(t_w3route_header))
|
||||
{
|
||||
|
@ -936,6 +956,15 @@ namespace pvpgn
|
|||
bn_byte_set(&packet->u.d2cs_client.h.type, type);
|
||||
return 0;
|
||||
|
||||
case packet_class_d2dbs_d2gs:
|
||||
if (packet_get_size(packet) < sizeof(t_d2dbs_d2gs_header))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "d2dbs_d2gs packet is shorter than header (len={})", packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
bn_short_set(&packet->u.d2dbs_d2gs.h.type, type);
|
||||
return 0;
|
||||
|
||||
case packet_class_w3route:
|
||||
if (packet_get_size(packet) < sizeof(t_w3route_header))
|
||||
{
|
||||
|
@ -1000,6 +1029,9 @@ namespace pvpgn
|
|||
case packet_class_d2cs:
|
||||
size = (unsigned int)bn_short_get(packet->u.d2cs_client.h.size);
|
||||
break;
|
||||
case packet_class_d2dbs_d2gs:
|
||||
size = (unsigned int)bn_short_get(packet->u.d2dbs_d2gs.h.size);
|
||||
break;
|
||||
case packet_class_w3route:
|
||||
size = (unsigned int)bn_short_get(packet->u.w3route.h.size);
|
||||
break;
|
||||
|
@ -1093,6 +1125,9 @@ namespace pvpgn
|
|||
case packet_class_d2cs_bnetd:
|
||||
bn_short_set(&packet->u.d2cs_bnetd.h.size, size);
|
||||
return 0;
|
||||
case packet_class_d2dbs_d2gs:
|
||||
bn_short_set(&packet->u.d2dbs_d2gs.h.size, size);
|
||||
return 0;
|
||||
case packet_class_w3route:
|
||||
if (size != 0 && size < sizeof(t_w3route_header))
|
||||
{
|
||||
|
@ -1140,6 +1175,8 @@ namespace pvpgn
|
|||
return sizeof(t_d2cs_d2gs_header);
|
||||
case packet_class_d2cs_bnetd:
|
||||
return sizeof(t_d2cs_bnetd_header);
|
||||
case packet_class_d2dbs_d2gs:
|
||||
return sizeof(t_d2dbs_d2gs_header);
|
||||
case packet_class_w3route:
|
||||
return sizeof(t_w3route_header);
|
||||
case packet_class_wolgameres:
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
# include "d2cs_protocol.h"
|
||||
# include "d2cs_d2gs_protocol.h"
|
||||
# include "d2cs_bnetd_protocol.h"
|
||||
# include "d2dbs_d2gs_protocol.h"
|
||||
# include "wol_gameres_protocol.h"
|
||||
#else
|
||||
# define JUST_NEED_TYPES
|
||||
|
@ -45,6 +46,7 @@
|
|||
# include "d2cs_protocol.h"
|
||||
# include "d2cs_d2gs_protocol.h"
|
||||
# include "d2cs_bnetd_protocol.h"
|
||||
# include "d2dbs_d2gs_protocol.h"
|
||||
# include "wol_gameres_protocol.h"
|
||||
# undef JUST_NEED_TYPES
|
||||
#endif
|
||||
|
@ -64,6 +66,7 @@ namespace pvpgn
|
|||
packet_class_d2gs,
|
||||
packet_class_d2cs,
|
||||
packet_class_d2cs_bnetd,
|
||||
packet_class_d2dbs_d2gs,
|
||||
packet_class_w3route,
|
||||
packet_class_wolgameres
|
||||
} t_packet_class;
|
||||
|
@ -286,6 +289,16 @@ namespace pvpgn
|
|||
t_client_d2cs_convertcharreq client_d2cs_convertcharreq;
|
||||
t_d2cs_client_convertcharreply d2cs_client_convertcharreply;
|
||||
|
||||
t_d2dbs_d2gs_generic d2dbs_d2gs;
|
||||
t_d2gs_d2dbs_save_data_request d2gs_d2dbs_save_data_request;
|
||||
t_d2dbs_d2gs_save_data_reply d2dbs_d2gs_save_data_reply;
|
||||
t_d2gs_d2dbs_get_data_request d2gs_d2dbs_get_data_request;
|
||||
t_d2dbs_d2gs_get_data_reply d2dbs_d2gs_get_data_reply;
|
||||
t_d2gs_d2dbs_update_ladder d2gs_d2dbs_update_ladder;
|
||||
t_d2gs_d2dbs_char_lock d2gs_d2dbs_char_lock;
|
||||
t_d2dbs_d2gs_echorequest d2dbs_d2gs_echorequest;
|
||||
t_d2gs_d2dbs_echoreply d2gs_d2dbs_echoreply;
|
||||
|
||||
t_client_friendslistreq client_friendslistreq;
|
||||
t_server_friendslistreply server_friendslistreply;
|
||||
t_client_friendinforeq client_friendinforeq;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(D2DBS_SOURCES
|
||||
charlock.cpp charlock.h cmdline.cpp cmdline.h d2ladder.cpp d2ladder.h
|
||||
dbsdupecheck.cpp dbsdupecheck.h dbserver.cpp dbserver.h dbspacket.cpp
|
||||
dbspacket.h handle_signal.cpp handle_signal.h main.cpp prefs.cpp
|
||||
charlock.cpp charlock.h cmdline.cpp cmdline.h connection.cpp connection.h d2ladder.cpp d2ladder.h
|
||||
dbsdupecheck.cpp dbsdupecheck.h dbserver.cpp dbserver.h handle_d2gs.cpp handle_d2gs.h handle_init.cpp
|
||||
handle_init.h handle_signal.cpp handle_signal.h main.cpp pgsid.cpp pgsid.h prefs.cpp
|
||||
prefs.h setup.h version.h ../win32/d2dbs_winmain.cpp ../win32/d2dbs_resource.h
|
||||
../win32/d2dbs_resource.rc)
|
||||
|
||||
|
@ -11,6 +11,12 @@ else(WITH_WIN32_GUI)
|
|||
add_executable(d2dbs ${D2DBS_SOURCES})
|
||||
endif(WITH_WIN32_GUI)
|
||||
|
||||
target_include_directories(d2dbs
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/>
|
||||
$<INSTALL_INTERFACE:include/>
|
||||
)
|
||||
|
||||
target_link_libraries(d2dbs PRIVATE common compat fmt::fmt win32 ${NETWORK_LIBRARIES})
|
||||
install(TARGETS d2dbs DESTINATION ${SBINDIR})
|
||||
if(WIN32 AND MSVC)
|
||||
|
|
454
src/d2dbs/connection.cpp
Normal file
454
src/d2dbs/connection.cpp
Normal file
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
* 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 "common/setup_before.h"
|
||||
#include "setup.h"
|
||||
#define CONNECTION_INTERNAL_ACCESS
|
||||
#include "connection.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
|
||||
#include "compat/psock.h"
|
||||
|
||||
#include "common/addr.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/list.h"
|
||||
#include "common/network.h"
|
||||
#include "common/xalloc.h"
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef HAVE_WS2TCPIP_H
|
||||
# include <Ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "charlock.h"
|
||||
#include "pgsid.h"
|
||||
#include "prefs.h"
|
||||
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
static std::list<t_d2dbs_connection*> conn_head = {};
|
||||
static std::list<t_d2dbs_connection*> conn_dead = {};
|
||||
|
||||
|
||||
t_d2dbs_connection* conn_create(int sock, unsigned int real_local_addr, unsigned short real_local_port, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port)
|
||||
{
|
||||
t_d2dbs_connection* conn = (t_d2dbs_connection*)xmalloc(sizeof(t_d2dbs_connection));
|
||||
if (!conn)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
std::memset(conn, 0, sizeof(t_d2dbs_connection));
|
||||
|
||||
conn->sd = sock;
|
||||
conn->ipaddr = addr;
|
||||
conn->fdw_idx = -1;
|
||||
conn->major = 0;
|
||||
conn->minor = 0;
|
||||
conn->type = 0;
|
||||
conn->stats = 0;
|
||||
conn->serverid = pgsid_get_id(addr);
|
||||
conn->verified = 0;
|
||||
{
|
||||
std::memset(conn->serverip, 0, sizeof(conn->serverip));
|
||||
struct in_addr in = {};
|
||||
in.s_addr = htonl(addr);
|
||||
char addrstr[INET_ADDRSTRLEN] = {};
|
||||
inet_ntop(AF_INET, &(in), addrstr, sizeof(addrstr));
|
||||
std::strncpy((char*)conn->serverip, addrstr, sizeof(conn->serverip) - 1);
|
||||
}
|
||||
conn->last_active = std::time(nullptr);
|
||||
conn->cclass = conn_class_init;
|
||||
conn->state = conn_state_initial;
|
||||
conn->queues.outqueue = nullptr;
|
||||
conn->queues.outsize = 0;
|
||||
conn->queues.outsizep = 0;
|
||||
conn->queues.inqueue = nullptr;
|
||||
conn->queues.insize = 0;
|
||||
|
||||
conn_head.push_back(conn);
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] created connection serverip={} serverid={}", conn->sd, conn->serverip, conn->serverid);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
// Caller is responsible for removing connection from desired list
|
||||
static void conn_destroy(t_d2dbs_connection* c)
|
||||
{
|
||||
if (c == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] unlock all characters on gs {}({})", c->sd, c->serverip, c->serverid);
|
||||
eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "unlock all characters on gs %s(%d)", c->serverip, c->serverid);
|
||||
eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "close connection to gs on socket %d", c->sd);
|
||||
cl_unlock_all_char_by_gsid(c->serverid);
|
||||
|
||||
// make sure the connection is closed
|
||||
if (c->sd != -1)
|
||||
{
|
||||
// -1 means that the socket was already closed by conn_close()
|
||||
fdwatch_del_fd(c->fdw_idx);
|
||||
psock_shutdown(c->sd, PSOCK_SHUT_RDWR);
|
||||
psock_close(c->sd);
|
||||
}
|
||||
|
||||
// clear out the packet queues
|
||||
if (c->queues.inqueue)
|
||||
{
|
||||
packet_del_ref(c->queues.inqueue);
|
||||
}
|
||||
|
||||
queue_clear(&c->queues.outqueue);
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] closed {} connection", c->sd, conn_class_get_str(conn_get_class(c)));
|
||||
|
||||
xfree(c);
|
||||
}
|
||||
|
||||
|
||||
const char* conn_class_get_str(t_conn_class cclass)
|
||||
{
|
||||
switch (cclass)
|
||||
{
|
||||
case conn_class_init:
|
||||
return "init";
|
||||
case conn_class_d2gs:
|
||||
return "d2gs";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
t_conn_class conn_get_class(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return conn_class_empty;
|
||||
}
|
||||
|
||||
return c->cclass;
|
||||
}
|
||||
|
||||
void conn_set_class(t_d2dbs_connection* c, t_conn_class cclass)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
c->cclass = cclass;
|
||||
}
|
||||
|
||||
|
||||
const char* conn_state_get_str(t_conn_state state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case conn_state_empty:
|
||||
return "empty";
|
||||
case conn_state_initial:
|
||||
return "initial";
|
||||
case conn_state_loggedin:
|
||||
return "loggedin";
|
||||
case conn_state_destroy:
|
||||
return "destroy";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
t_conn_state conn_get_state(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return conn_state_empty;
|
||||
}
|
||||
|
||||
return c->state;
|
||||
}
|
||||
|
||||
void conn_set_state(t_d2dbs_connection* c, t_conn_state state)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
// special case for destroying connections, add them to conn_dead list
|
||||
if (state == conn_state_destroy && c->state != conn_state_destroy)
|
||||
{
|
||||
conn_dead.push_back(c);
|
||||
}
|
||||
else if (state != conn_state_destroy && c->state == conn_state_destroy)
|
||||
{
|
||||
auto it = std::find(conn_dead.begin(), conn_dead.end(), c);
|
||||
if (it != conn_dead.end())
|
||||
{
|
||||
conn_dead.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] could not find connection in conn_dead", c->sd);
|
||||
}
|
||||
}
|
||||
|
||||
c->state = state;
|
||||
}
|
||||
|
||||
|
||||
void conn_clear_outqueue(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
queue_clear(&c->queues.outqueue);
|
||||
}
|
||||
|
||||
t_packet* conn_peek_outqueue(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (c->queues.outqueue)
|
||||
{
|
||||
return queue_peek_packet((t_queue const* const*)&c->queues.outqueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
t_packet* conn_pull_outqueue(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (c->queues.outsizep)
|
||||
{
|
||||
if (!(--c->queues.outsizep)) fdwatch_update_fd(c->fdw_idx, fdwatch_type_read);
|
||||
return queue_pull_packet((t_queue**)&c->queues.outqueue);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
t_packet* conn_get_in_queue(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return c->queues.inqueue;
|
||||
}
|
||||
|
||||
void conn_put_in_queue(t_d2dbs_connection* c, t_packet* packet)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
c->queues.inqueue = packet;
|
||||
}
|
||||
|
||||
|
||||
unsigned int conn_get_in_size(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return c->queues.insize;
|
||||
}
|
||||
|
||||
void conn_set_in_size(t_d2dbs_connection* c, unsigned int size)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
c->queues.insize = size;
|
||||
}
|
||||
|
||||
|
||||
unsigned int conn_get_out_size(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return c->queues.outsize;
|
||||
}
|
||||
|
||||
|
||||
void conn_set_out_size(t_d2dbs_connection* c, unsigned int size)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
c->queues.outsize = size;
|
||||
}
|
||||
|
||||
|
||||
int conn_push_outqueue(t_d2dbs_connection* c, t_packet* packet)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!packet)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL packet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
queue_push_packet(&c->queues.outqueue, packet);
|
||||
if (!c->queues.outsizep++)
|
||||
{
|
||||
fdwatch_update_fd(c->fdw_idx, fdwatch_type_read | fdwatch_type_write);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int conn_add_fdwatch(t_d2dbs_connection* c, fdwatch_handler handle)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->fdw_idx = fdwatch_add_fd(c->sd, fdwatch_type_read, handle, c);
|
||||
return c->fdw_idx;
|
||||
}
|
||||
|
||||
|
||||
void conn_close_read(t_d2dbs_connection* c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return;
|
||||
}
|
||||
|
||||
conn_set_state(c, conn_state_destroy);
|
||||
|
||||
/* only if we still got output packets remove the read availability
|
||||
* from fdwatch, we are NOT allowed to remove all availability or
|
||||
* remove it completely from fdwatch while handling read, also
|
||||
* if the connection has no output packets is ok to leave it
|
||||
* in read availability check cause it will be closed immediately
|
||||
* in connlist_reap() anyway
|
||||
*/
|
||||
if (conn_peek_outqueue(c))
|
||||
{
|
||||
fdwatch_update_fd(c->fdw_idx, fdwatch_type_write);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const std::list<t_d2dbs_connection*>& connlist()
|
||||
{
|
||||
return conn_head;
|
||||
}
|
||||
|
||||
void connlist_reap()
|
||||
{
|
||||
for (auto it = conn_dead.begin(); it != conn_dead.end(); )
|
||||
{
|
||||
t_d2dbs_connection* c = *it;
|
||||
if (!conn_peek_outqueue(c))
|
||||
{
|
||||
conn_destroy(c); // also removes from fdwatch
|
||||
|
||||
conn_head.remove(c);
|
||||
it = conn_dead.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void connlist_destroy()
|
||||
{
|
||||
for (auto it = conn_head.begin(); it != conn_head.end(); )
|
||||
{
|
||||
t_d2dbs_connection* c = *it;
|
||||
if (!conn_peek_outqueue(c))
|
||||
{
|
||||
conn_destroy(c); // also removes from fdwatch
|
||||
|
||||
conn_dead.remove(c);
|
||||
it = conn_head.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
114
src/d2dbs/connection.h
Normal file
114
src/d2dbs/connection.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_CONNECTION_H
|
||||
#define INCLUDED_CONNECTION_H
|
||||
|
||||
#include <ctime>
|
||||
#include <list>
|
||||
|
||||
#include "common/queue.h"
|
||||
#include "common/hashtable.h"
|
||||
#include "common/packet.h"
|
||||
#include "common/fdwatch.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
enum t_conn_class
|
||||
{
|
||||
conn_class_empty,
|
||||
conn_class_init,
|
||||
conn_class_d2gs
|
||||
};
|
||||
|
||||
enum t_conn_state
|
||||
{
|
||||
conn_state_empty,
|
||||
conn_state_initial,
|
||||
conn_state_loggedin,
|
||||
conn_state_destroy
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sd; // tcp_sock
|
||||
unsigned int ipaddr; // tcp_addr
|
||||
int fdw_idx;
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
unsigned char type;
|
||||
unsigned char stats;
|
||||
unsigned int serverid;
|
||||
unsigned int verified;
|
||||
unsigned char serverip[16];
|
||||
std::time_t last_active;
|
||||
#ifdef CONNECTION_INTERNAL_ACCESS
|
||||
t_conn_class cclass;
|
||||
t_conn_state state;
|
||||
struct
|
||||
{
|
||||
t_queue* outqueue; /* packets waiting to be sent */
|
||||
unsigned int outsize; /* amount sent from the current output packet */
|
||||
unsigned int outsizep;
|
||||
t_packet* inqueue; /* packet waiting to be processed */
|
||||
unsigned int insize; /* amount received into the current input packet */
|
||||
} queues; /* network queues and related data */
|
||||
#endif
|
||||
} t_d2dbs_connection;
|
||||
|
||||
|
||||
t_d2dbs_connection* conn_create(int sock, unsigned int real_local_addr, unsigned short real_local_port, unsigned int local_addr, unsigned short local_port, unsigned int addr, unsigned short port);
|
||||
|
||||
const char* conn_class_get_str(t_conn_class cclass);
|
||||
t_conn_class conn_get_class(t_d2dbs_connection* c);
|
||||
void conn_set_class(t_d2dbs_connection* c, t_conn_class cclass);
|
||||
|
||||
const char* conn_state_get_str(t_conn_state state);
|
||||
t_conn_state conn_get_state(t_d2dbs_connection* c);
|
||||
void conn_set_state(t_d2dbs_connection* c, t_conn_state state);
|
||||
|
||||
void conn_clear_outqueue(t_d2dbs_connection* c);
|
||||
t_packet* conn_peek_outqueue(t_d2dbs_connection* c);
|
||||
t_packet* conn_pull_outqueue(t_d2dbs_connection* c);
|
||||
|
||||
t_packet* conn_get_in_queue(t_d2dbs_connection* c);
|
||||
void conn_put_in_queue(t_d2dbs_connection* c, t_packet* packet);
|
||||
|
||||
unsigned int conn_get_in_size(t_d2dbs_connection* c);
|
||||
void conn_set_in_size(t_d2dbs_connection* c, unsigned int size);
|
||||
|
||||
unsigned int conn_get_out_size(t_d2dbs_connection* c);
|
||||
void conn_set_out_size(t_d2dbs_connection* c, unsigned int size);
|
||||
|
||||
int conn_push_outqueue(t_d2dbs_connection* c, t_packet* packet);
|
||||
|
||||
int conn_add_fdwatch(t_d2dbs_connection* c, fdwatch_handler handle);
|
||||
|
||||
void conn_close_read(t_d2dbs_connection* c);
|
||||
|
||||
|
||||
const std::list<t_d2dbs_connection*>& connlist();
|
||||
void connlist_reap();
|
||||
void connlist_destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -18,7 +18,10 @@
|
|||
#ifndef INCLUDED_DBSERVER_H
|
||||
#define INCLUDED_DBSERVER_H
|
||||
|
||||
#include "common/list.h"
|
||||
#define STATUS_D2LADDER_FAILURE 20
|
||||
#define STATUS_CHARLOCK_FAILURE 30
|
||||
#define STATUS_FDWATCH_FAILURE 90
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
@ -26,33 +29,25 @@ namespace pvpgn
|
|||
namespace d2dbs
|
||||
{
|
||||
|
||||
typedef struct {
|
||||
int sd;
|
||||
unsigned int ipaddr;
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
unsigned char type;
|
||||
unsigned char stats;
|
||||
unsigned int serverid;
|
||||
unsigned int verified;
|
||||
unsigned char serverip[16];
|
||||
int last_active;
|
||||
int nCharsInReadBuffer;
|
||||
int nCharsInWriteBuffer;
|
||||
char ReadBuf[kBufferSize];
|
||||
char WriteBuf[kBufferSize];
|
||||
} t_d2dbs_connection;
|
||||
#ifdef SERVER_INTERNAL_ACCESS
|
||||
|
||||
typedef struct raw_preset_d2gsid {
|
||||
unsigned int ipaddr;
|
||||
unsigned int d2gsid;
|
||||
struct raw_preset_d2gsid *next;
|
||||
} t_preset_d2gsid;
|
||||
enum t_laddr_type
|
||||
{
|
||||
laddr_type_d2gs, // d2gs (port 4000)
|
||||
};
|
||||
|
||||
int dbs_server_main(void);
|
||||
int dbs_server_shutdown_connection(t_d2dbs_connection* conn);
|
||||
// listen address structure
|
||||
struct t_laddr_info
|
||||
{
|
||||
int ssocket; // TCP listen socket
|
||||
t_laddr_type type;
|
||||
};
|
||||
|
||||
extern t_list * dbs_server_connection_list;
|
||||
#endif
|
||||
|
||||
int pre_server_startup();
|
||||
bool server_process();
|
||||
void post_server_shutdown(int status);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,917 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001 sousou (liupeng.cs@263.net)
|
||||
*
|
||||
* 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 "common/setup_before.h"
|
||||
#include "setup.h"
|
||||
#include "dbspacket.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "compat/strsep.h"
|
||||
#include "compat/mkdir.h"
|
||||
#include "compat/rename.h"
|
||||
#include "compat/access.h"
|
||||
#include "compat/statmacros.h"
|
||||
#include "compat/psock.h"
|
||||
#include "common/xstring.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/d2cs_d2gs_character.h"
|
||||
#include "common/d2char_checksum.h"
|
||||
#include "common/xalloc.h"
|
||||
#include "common/addr.h"
|
||||
#include "prefs.h"
|
||||
#include "charlock.h"
|
||||
#include "d2ladder.h"
|
||||
#include "common/setup_after.h"
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen);
|
||||
static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen);
|
||||
static unsigned int dbs_packet_getdata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, long bufsize);
|
||||
static unsigned int dbs_packet_getdata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int bufsize);
|
||||
static unsigned int dbs_packet_echoreply(t_d2dbs_connection* conn);
|
||||
static int dbs_packet_getdata(t_d2dbs_connection* conn);
|
||||
static int dbs_packet_savedata(t_d2dbs_connection* conn);
|
||||
static int dbs_packet_charlock(t_d2dbs_connection* conn);
|
||||
static int dbs_packet_updateladder(t_d2dbs_connection* conn);
|
||||
static int dbs_verify_ipaddr(char const * addrlist, t_d2dbs_connection * c);
|
||||
|
||||
static int dbs_packet_fix_charinfo(t_d2dbs_connection * conn, char * AccountName, char * CharName, char * charsave);
|
||||
static void dbs_packet_set_charinfo_level(char * CharName, char * charinfo);
|
||||
|
||||
static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen)
|
||||
{
|
||||
std::FILE * fd;
|
||||
int checksum_header;
|
||||
int checksum_calc;
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
//check if checksum is ok
|
||||
checksum_header = bn_int_get((bn_basic*)&data[D2CHARSAVE_CHECKSUM_OFFSET]);
|
||||
checksum_calc = d2charsave_checksum((unsigned char *)data, datalen, D2CHARSAVE_CHECKSUM_OFFSET);
|
||||
|
||||
if (checksum_header != checksum_calc)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "received ({}) and calculated({}) checksum do not match - discarding charsave", checksum_header, checksum_calc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::string filename = fmt::format("{}/.{}.tmp", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
fd = std::fopen(filename.c_str(), "wb");
|
||||
if (!fd) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t curlen = 0;
|
||||
std::size_t leftlen = datalen;
|
||||
while (curlen<datalen)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
curlen += readlen;
|
||||
leftlen -= readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
|
||||
std::string bakfile = fmt::format("{}/{}", prefs_get_charsave_bak_dir(), CharName);
|
||||
std::string savefile = fmt::format("{}/{}", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
if (p_rename(savefile.c_str(), bakfile.c_str()) == -1) {
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile);
|
||||
}
|
||||
if (p_rename(filename.c_str(), savefile.c_str()) == -1) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile);
|
||||
return 0;
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "saved charsave {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid);
|
||||
return datalen;
|
||||
}
|
||||
|
||||
static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int datalen)
|
||||
{
|
||||
std::FILE * fd;
|
||||
struct stat statbuf;
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filepath = fmt::format("{}/{}", prefs_get_charinfo_bak_dir(), AccountName);
|
||||
if (stat(filepath.c_str(), &statbuf) == -1)
|
||||
{
|
||||
if (p_mkdir(filepath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "created charinfo directory: {}", filepath);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "failed to create charinfo directory \"{}\" (errno: {})", filepath, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string filename = fmt::format("{}/{}/.{}.tmp", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
fd = std::fopen(filename.c_str(), "wb");
|
||||
if (!fd) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t curlen = 0;
|
||||
std::size_t leftlen = datalen;
|
||||
while (curlen < datalen)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
curlen += readlen;
|
||||
leftlen -= readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
|
||||
std::string bakfile = fmt::format("{}/{}/{}", prefs_get_charinfo_bak_dir(), AccountName, CharName);
|
||||
std::string savefile = fmt::format("{}/{}/{}", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
if (p_rename(savefile.c_str(), bakfile.c_str()) == -1) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile);
|
||||
}
|
||||
if (p_rename(filename.c_str(), savefile.c_str()) == -1) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile);
|
||||
return 0;
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "saved charinfo {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid);
|
||||
return datalen;
|
||||
}
|
||||
|
||||
static unsigned int dbs_packet_getdata_charsave(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, long bufsize)
|
||||
{
|
||||
std::FILE * fd;
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filename = fmt::format("{}/{}", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
std::string filename_d2closed = fmt::format("{}/{}.d2s", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
if ((access(filename.c_str(), F_OK) < 0) && (access(filename_d2closed.c_str(), F_OK) == 0))
|
||||
{
|
||||
if (std::rename(filename_d2closed.c_str(), filename.c_str()) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to rename file \"{}\" to \"{}\"", filename_d2closed, filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fd = std::fopen(filename.c_str(), "rb");
|
||||
if (!fd) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return 0;
|
||||
}
|
||||
std::fseek(fd, 0, SEEK_END);
|
||||
long filesize = std::ftell(fd);
|
||||
if (filesize == -1L)
|
||||
{
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "ftell() failed");
|
||||
return 0;
|
||||
}
|
||||
std::rewind(fd);
|
||||
|
||||
if (bufsize < filesize) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "not enough buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long curlen = 0;
|
||||
std::size_t leftlen = filesize;
|
||||
while (curlen < filesize)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fread(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "read() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
leftlen -= readlen;
|
||||
curlen += readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "loaded charsave {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid);
|
||||
return filesize;
|
||||
}
|
||||
|
||||
static unsigned int dbs_packet_getdata_charinfo(t_d2dbs_connection* conn, char * AccountName, char * CharName, char * data, unsigned int bufsize)
|
||||
{
|
||||
std::FILE * fd;
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filename = fmt::format("{}/{}/{}", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
fd = std::fopen(filename.c_str(), "rb");
|
||||
if (!fd) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return 0;
|
||||
}
|
||||
std::fseek(fd, 0, SEEK_END);
|
||||
long filesize = std::ftell(fd);
|
||||
std::rewind(fd);
|
||||
if (filesize == -1) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "lseek() failed");
|
||||
return 0;
|
||||
}
|
||||
if ((signed)bufsize < filesize) {
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "not enough buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t curlen = 0;
|
||||
std::size_t leftlen = filesize;
|
||||
while (curlen < filesize)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fread(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0)
|
||||
{
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "read() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
leftlen -= readlen;
|
||||
curlen += readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "loaded charinfo {}(*{}) for gs {}({})", CharName, AccountName, conn->serverip, conn->serverid);
|
||||
return filesize;
|
||||
}
|
||||
|
||||
static int dbs_packet_savedata(t_d2dbs_connection * conn)
|
||||
{
|
||||
unsigned short datatype;
|
||||
unsigned short datalen;
|
||||
unsigned int result;
|
||||
char AccountName[MAX_USERNAME_LEN];
|
||||
char CharName[MAX_CHARNAME_LEN];
|
||||
char RealmName[MAX_REALMNAME_LEN];
|
||||
t_d2gs_d2dbs_save_data_request * savecom;
|
||||
char* readpos = conn->ReadBuf;
|
||||
|
||||
savecom = (t_d2gs_d2dbs_save_data_request *)readpos;
|
||||
datatype = bn_short_get(savecom->datatype);
|
||||
datalen = bn_short_get(savecom->datalen);
|
||||
|
||||
readpos += sizeof(*savecom);
|
||||
std::strncpy(AccountName, readpos, MAX_USERNAME_LEN);
|
||||
if (AccountName[MAX_USERNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max acccount name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(AccountName) + 1;
|
||||
std::strncpy(CharName, readpos, MAX_CHARNAME_LEN);
|
||||
if (CharName[MAX_CHARNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(CharName) + 1;
|
||||
std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN);
|
||||
if (RealmName[MAX_REALMNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(RealmName) + 1;
|
||||
|
||||
if (readpos + datalen != conn->ReadBuf + bn_short_get(savecom->h.size)) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "request packet size error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (datatype == D2GS_DATA_CHARSAVE) {
|
||||
if (dbs_packet_savedata_charsave(conn, AccountName, CharName, readpos, datalen) > 0 &&
|
||||
dbs_packet_fix_charinfo(conn, AccountName, CharName, readpos)) {
|
||||
result = D2DBS_SAVE_DATA_SUCCESS;
|
||||
}
|
||||
else {
|
||||
datalen = 0;
|
||||
result = D2DBS_SAVE_DATA_FAILED;
|
||||
}
|
||||
}
|
||||
else if (datatype == D2GS_DATA_PORTRAIT) {
|
||||
/* if level is > 255 , sets level to 255 */
|
||||
dbs_packet_set_charinfo_level(CharName, readpos);
|
||||
if (dbs_packet_savedata_charinfo(conn, AccountName, CharName, readpos, datalen) > 0) {
|
||||
result = D2DBS_SAVE_DATA_SUCCESS;
|
||||
}
|
||||
else {
|
||||
datalen = 0;
|
||||
result = D2DBS_SAVE_DATA_FAILED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
t_d2dbs_d2gs_save_data_reply* saveret;
|
||||
std::size_t writelen = sizeof(*saveret) + std::strlen(CharName) + 1;
|
||||
std::size_t buffer_available_len = sizeof(conn->WriteBuf) - conn->nCharsInWriteBuffer;
|
||||
|
||||
if (buffer_available_len < writelen)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] not enough space in write buffer (available space: {}, needed: {})", conn->sd, buffer_available_len, writelen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char* writepos = reinterpret_cast<unsigned char*>(conn->WriteBuf + conn->nCharsInWriteBuffer);
|
||||
saveret = reinterpret_cast<t_d2dbs_d2gs_save_data_reply*>(writepos);
|
||||
bn_short_set(&saveret->h.type, D2DBS_D2GS_SAVE_DATA_REPLY);
|
||||
bn_short_set(&saveret->h.size, writelen);
|
||||
bn_int_set(&saveret->h.seqno, bn_int_get(savecom->h.seqno));
|
||||
bn_short_set(&saveret->datatype, bn_short_get(savecom->datatype));
|
||||
bn_int_set(&saveret->result, result);
|
||||
writepos += sizeof(*saveret);
|
||||
std::memcpy(writepos, CharName, std::strlen(CharName) + 1);
|
||||
|
||||
conn->nCharsInWriteBuffer += writelen;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int dbs_packet_echoreply(t_d2dbs_connection * conn)
|
||||
{
|
||||
conn->last_active = std::time(NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbs_packet_getdata(t_d2dbs_connection * conn)
|
||||
{
|
||||
unsigned short datatype;
|
||||
unsigned short datalen;
|
||||
unsigned int result;
|
||||
char AccountName[MAX_USERNAME_LEN];
|
||||
char CharName[MAX_CHARNAME_LEN];
|
||||
char RealmName[MAX_REALMNAME_LEN];
|
||||
t_d2gs_d2dbs_get_data_request * getcom;
|
||||
t_d2dbs_d2gs_get_data_reply * getret;
|
||||
char * readpos;
|
||||
char * writepos;
|
||||
char databuf[kBufferSize];
|
||||
t_d2charinfo_file charinfo;
|
||||
unsigned short charinfolen;
|
||||
unsigned int gsid;
|
||||
|
||||
readpos = conn->ReadBuf;
|
||||
getcom = (t_d2gs_d2dbs_get_data_request *)readpos;
|
||||
datatype = bn_short_get(getcom->datatype);
|
||||
|
||||
readpos += sizeof(*getcom);
|
||||
std::strncpy(AccountName, readpos, MAX_USERNAME_LEN);
|
||||
if (AccountName[MAX_USERNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max account name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(AccountName) + 1;
|
||||
std::strncpy(CharName, readpos, MAX_CHARNAME_LEN);
|
||||
if (CharName[MAX_CHARNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(CharName) + 1;
|
||||
std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN);
|
||||
if (RealmName[MAX_REALMNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(RealmName) + 1;
|
||||
|
||||
if (readpos != conn->ReadBuf + bn_short_get(getcom->h.size)) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "request packet size error");
|
||||
return -1;
|
||||
}
|
||||
writepos = conn->WriteBuf + conn->nCharsInWriteBuffer;
|
||||
getret = (t_d2dbs_d2gs_get_data_reply *)writepos;
|
||||
datalen = 0;
|
||||
if (datatype == D2GS_DATA_CHARSAVE) {
|
||||
if (cl_query_charlock_status((unsigned char*)CharName, (unsigned char*)RealmName, &gsid) != 0) {
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "char {}(*{})@{} is already locked on gs {}", CharName, AccountName, RealmName, gsid);
|
||||
result = D2DBS_GET_DATA_CHARLOCKED;
|
||||
}
|
||||
else if (cl_lock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to lock char {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
result = D2DBS_GET_DATA_CHARLOCKED;
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "lock char {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
datalen = dbs_packet_getdata_charsave(conn, AccountName, CharName, databuf, kBufferSize);
|
||||
if (datalen > 0) {
|
||||
result = D2DBS_GET_DATA_SUCCESS;
|
||||
charinfolen = dbs_packet_getdata_charinfo(conn, AccountName, CharName, (char *)&charinfo, sizeof(charinfo));
|
||||
if (charinfolen > 0) {
|
||||
result = D2DBS_GET_DATA_SUCCESS;
|
||||
}
|
||||
else {
|
||||
result = D2DBS_GET_DATA_FAILED;
|
||||
if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, gsid) != 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock char {}(*{})@{} for gs {}({})", CharName, \
|
||||
AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", CharName, \
|
||||
AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
datalen = 0;
|
||||
result = D2DBS_GET_DATA_FAILED;
|
||||
if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, gsid) != 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "faled to unlock char {}(*{})@{} for gs {}({})", CharName, \
|
||||
AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", CharName, \
|
||||
AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (result == D2DBS_GET_DATA_SUCCESS) {
|
||||
bn_int_set(&getret->charcreatetime, bn_int_get(charinfo.header.create_time));
|
||||
/* FIXME: this should be rewritten to support string formatted std::time */
|
||||
if (bn_int_get(charinfo.header.create_time) >= prefs_get_ladderinit_time()) {
|
||||
bn_int_set(&getret->allowladder, 1);
|
||||
}
|
||||
else {
|
||||
bn_int_set(&getret->allowladder, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bn_int_set(&getret->charcreatetime, 0);
|
||||
bn_int_set(&getret->allowladder, 0);
|
||||
}
|
||||
}
|
||||
else if (datatype == D2GS_DATA_PORTRAIT) {
|
||||
datalen = dbs_packet_getdata_charinfo(conn, AccountName, CharName, databuf, kBufferSize);
|
||||
if (datalen > 0) result = D2DBS_GET_DATA_SUCCESS;
|
||||
else {
|
||||
datalen = 0;
|
||||
result = D2DBS_GET_DATA_FAILED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::size_t buffer_available_len = sizeof(conn->WriteBuf) - conn->nCharsInWriteBuffer;
|
||||
std::size_t writelen = sizeof(*getret) + std::strlen(CharName) + 1 + datalen;
|
||||
if (buffer_available_len < writelen)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] not enough space in write buffer (available space: {}, needed: {})", conn->sd, buffer_available_len, writelen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bn_short_set(&getret->h.type, D2DBS_D2GS_GET_DATA_REPLY);
|
||||
bn_short_set(&getret->h.size, writelen);
|
||||
bn_int_set(&getret->h.seqno, bn_int_get(getcom->h.seqno));
|
||||
bn_short_set(&getret->datatype, bn_short_get(getcom->datatype));
|
||||
bn_int_set(&getret->result, result);
|
||||
bn_short_set(&getret->datalen, datalen);
|
||||
writepos += sizeof(*getret);
|
||||
|
||||
std::memcpy(writepos, CharName, std::strlen(CharName) + 1);
|
||||
writepos += std::strlen(CharName) + 1;
|
||||
|
||||
if (datalen)
|
||||
{
|
||||
std::memcpy(writepos, databuf, datalen);
|
||||
}
|
||||
|
||||
conn->nCharsInWriteBuffer += writelen;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbs_packet_updateladder(t_d2dbs_connection * conn)
|
||||
{
|
||||
char CharName[MAX_CHARNAME_LEN];
|
||||
char RealmName[MAX_REALMNAME_LEN];
|
||||
t_d2gs_d2dbs_update_ladder * updateladder;
|
||||
char * readpos;
|
||||
t_d2ladder_info charladderinfo;
|
||||
|
||||
readpos = conn->ReadBuf;
|
||||
updateladder = (t_d2gs_d2dbs_update_ladder *)readpos;
|
||||
|
||||
readpos += sizeof(*updateladder);
|
||||
std::strncpy(CharName, readpos, MAX_CHARNAME_LEN);
|
||||
if (CharName[MAX_CHARNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(CharName) + 1;
|
||||
std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN);
|
||||
if (RealmName[MAX_REALMNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(RealmName) + 1;
|
||||
if (readpos != conn->ReadBuf + bn_short_get(updateladder->h.size)) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "request packet size error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::strcpy(charladderinfo.charname, CharName);
|
||||
charladderinfo.experience = bn_int_get(updateladder->charexplow);
|
||||
charladderinfo.level = bn_int_get(updateladder->charlevel);
|
||||
charladderinfo.status = bn_short_get(updateladder->charstatus);
|
||||
charladderinfo.chclass = bn_short_get(updateladder->charclass);
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "update ladder for {}@{} for gs {}({})", CharName, RealmName, conn->serverip, conn->serverid);
|
||||
d2ladder_update(&charladderinfo);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbs_packet_charlock(t_d2dbs_connection * conn)
|
||||
{
|
||||
char CharName[MAX_CHARNAME_LEN];
|
||||
char AccountName[MAX_USERNAME_LEN];
|
||||
char RealmName[MAX_REALMNAME_LEN];
|
||||
t_d2gs_d2dbs_char_lock * charlock;
|
||||
char * readpos;
|
||||
|
||||
readpos = conn->ReadBuf;
|
||||
charlock = (t_d2gs_d2dbs_char_lock*)readpos;
|
||||
|
||||
readpos += sizeof(*charlock);
|
||||
std::strncpy(AccountName, readpos, MAX_USERNAME_LEN);
|
||||
if (AccountName[MAX_USERNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max account name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(AccountName) + 1;
|
||||
std::strncpy(CharName, readpos, MAX_CHARNAME_LEN);
|
||||
if (CharName[MAX_CHARNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max char name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(CharName) + 1;
|
||||
std::strncpy(RealmName, readpos, MAX_REALMNAME_LEN);
|
||||
if (RealmName[MAX_REALMNAME_LEN - 1] != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "max realm name length exceeded");
|
||||
return -1;
|
||||
}
|
||||
readpos += std::strlen(RealmName) + 1;
|
||||
|
||||
if (readpos != conn->ReadBuf + bn_short_get(charlock->h.size)) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "request packet size error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bn_int_get(charlock->lockstatus)) {
|
||||
if (cl_lock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to lock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "lock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (cl_unlock_char((unsigned char*)CharName, (unsigned char*)RealmName, conn->serverid) != 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock character {}(*{})@{} for gs {}({})", CharName, AccountName, RealmName, conn->serverip, conn->serverid);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
return value:
|
||||
1 : process one or more packet
|
||||
0 : not get a whole packet,do nothing
|
||||
-1 : error
|
||||
*/
|
||||
extern int dbs_packet_handle(t_d2dbs_connection* conn)
|
||||
{
|
||||
unsigned short readlen, writelen;
|
||||
t_d2dbs_d2gs_header * readhead;
|
||||
int retval;
|
||||
|
||||
if (conn->stats == 0) {
|
||||
if (conn->nCharsInReadBuffer < (signed)sizeof(t_d2gs_d2dbs_connect)) {
|
||||
return 0;
|
||||
}
|
||||
conn->stats = 1;
|
||||
conn->type = conn->ReadBuf[0];
|
||||
|
||||
if (conn->type == CONNECT_CLASS_D2GS_TO_D2DBS) {
|
||||
if (dbs_verify_ipaddr(d2dbs_prefs_get_d2gs_list(), conn) < 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "d2gs connection from unknown ip address");
|
||||
return -1;
|
||||
}
|
||||
readlen = 1;
|
||||
writelen = 0;
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "set connection type for gs {}({}) on socket {}", conn->serverip, conn->serverid, conn->sd);
|
||||
eventlog_step(prefs_get_logfile_gs(), eventlog_level_info, __FUNCTION__, "set connection type for gs %s(%d) on socket %d", conn->serverip, conn->serverid, conn->sd);
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown connection type");
|
||||
return -1;
|
||||
}
|
||||
conn->nCharsInReadBuffer -= readlen;
|
||||
std::memmove(conn->ReadBuf, conn->ReadBuf + readlen, conn->nCharsInReadBuffer);
|
||||
}
|
||||
else if (conn->stats == 1) {
|
||||
if (conn->type == CONNECT_CLASS_D2GS_TO_D2DBS) {
|
||||
while (conn->nCharsInReadBuffer >= (signed)sizeof(*readhead)) {
|
||||
readhead = (t_d2dbs_d2gs_header *)conn->ReadBuf;
|
||||
readlen = bn_short_get(readhead->size);
|
||||
if (conn->nCharsInReadBuffer < readlen) break;
|
||||
switch (bn_short_get(readhead->type)) {
|
||||
case D2GS_D2DBS_SAVE_DATA_REQUEST:
|
||||
retval = dbs_packet_savedata(conn);
|
||||
break;
|
||||
case D2GS_D2DBS_GET_DATA_REQUEST:
|
||||
retval = dbs_packet_getdata(conn);
|
||||
break;
|
||||
case D2GS_D2DBS_UPDATE_LADDER:
|
||||
retval = dbs_packet_updateladder(conn);
|
||||
break;
|
||||
case D2GS_D2DBS_CHAR_LOCK:
|
||||
retval = dbs_packet_charlock(conn);
|
||||
break;
|
||||
case D2GS_D2DBS_ECHOREPLY:
|
||||
retval = dbs_packet_echoreply(conn);
|
||||
break;
|
||||
default:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown request type {}", \
|
||||
bn_short_get(readhead->type));
|
||||
retval = -1;
|
||||
}
|
||||
if (retval != 1) return retval;
|
||||
conn->nCharsInReadBuffer -= readlen;
|
||||
std::memmove(conn->ReadBuf, conn->ReadBuf + readlen, conn->nCharsInReadBuffer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown connection type {}", conn->type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown connection stats");
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: we should save client ipaddr into c->ipaddr after accept */
|
||||
static int dbs_verify_ipaddr(char const * addrlist, t_d2dbs_connection * c)
|
||||
{
|
||||
char * adlist;
|
||||
char * s, *temp;
|
||||
t_elem * elem;
|
||||
t_d2dbs_connection * tempc;
|
||||
unsigned int valid;
|
||||
unsigned int resolveipaddr;
|
||||
|
||||
adlist = xstrdup(addrlist);
|
||||
temp = adlist;
|
||||
valid = 0;
|
||||
while ((s = strsep(&temp, ","))) {
|
||||
host_lookup(s, &resolveipaddr);
|
||||
if (resolveipaddr == 0) continue;
|
||||
|
||||
if (c->ipaddr == resolveipaddr) {
|
||||
valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xfree(adlist);
|
||||
if (valid) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "ip address {} is valid", addr_num_to_ip_str(c->ipaddr));
|
||||
LIST_TRAVERSE(dbs_server_connection_list, elem)
|
||||
{
|
||||
if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue;
|
||||
if (tempc != c && tempc->ipaddr == c->ipaddr) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "destroying previous connection {}", tempc->serverid);
|
||||
dbs_server_shutdown_connection(tempc);
|
||||
list_remove_elem(dbs_server_connection_list, &elem);
|
||||
}
|
||||
}
|
||||
c->verified = 1;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "ip address {} is invalid", addr_num_to_ip_str(c->ipaddr));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dbs_check_timeout(void)
|
||||
{
|
||||
t_elem *elem;
|
||||
t_d2dbs_connection *tempc;
|
||||
std::time_t now;
|
||||
int timeout;
|
||||
|
||||
now = std::time(NULL);
|
||||
timeout = d2dbs_prefs_get_idletime();
|
||||
LIST_TRAVERSE(dbs_server_connection_list, elem)
|
||||
{
|
||||
if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue;
|
||||
if (now - tempc->last_active > timeout) {
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "connection {} timed out", tempc->serverid);
|
||||
dbs_server_shutdown_connection(tempc);
|
||||
list_remove_elem(dbs_server_connection_list, &elem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbs_keepalive(void)
|
||||
{
|
||||
t_elem *elem;
|
||||
t_d2dbs_connection *tempc;
|
||||
t_d2dbs_d2gs_echorequest *echoreq;
|
||||
unsigned short writelen;
|
||||
unsigned char *writepos;
|
||||
std::time_t now;
|
||||
|
||||
writelen = sizeof(t_d2dbs_d2gs_echorequest);
|
||||
now = std::time(NULL);
|
||||
LIST_TRAVERSE(dbs_server_connection_list, elem)
|
||||
{
|
||||
if (!(tempc = (t_d2dbs_connection*)elem_get_data(elem))) continue;
|
||||
if (writelen > kBufferSize - tempc->nCharsInWriteBuffer) continue;
|
||||
writepos = (unsigned char*)(tempc->WriteBuf + tempc->nCharsInWriteBuffer);
|
||||
echoreq = (t_d2dbs_d2gs_echorequest*)writepos;
|
||||
bn_short_set(&echoreq->h.type, D2DBS_D2GS_ECHOREQUEST);
|
||||
bn_short_set(&echoreq->h.size, writelen);
|
||||
/* FIXME: sequence number not set */
|
||||
bn_int_set(&echoreq->h.seqno, 0);
|
||||
tempc->nCharsInWriteBuffer += writelen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************/
|
||||
#define CHARINFO_SIZE 0xC0
|
||||
#define CHARINFO_PORTRAIT_LEVEL_OFFSET 0x89
|
||||
#define CHARINFO_PORTRAIT_STATUS_OFFSET 0x8A
|
||||
#define CHARINFO_SUMMARY_LEVEL_OFFSET 0xB8
|
||||
#define CHARINFO_SUMMARY_STATUS_OFFSET 0xB4
|
||||
#define CHARINFO_PORTRAIT_GFX_OFFSET 0x72
|
||||
#define CHARINFO_PORTRAIT_COLOR_OFFSET 0x7E
|
||||
|
||||
#define CHARSAVE_LEVEL_OFFSET 0x2B
|
||||
#define CHARSAVE_STATUS_OFFSET 0x24
|
||||
#define CHARSAVE_GFX_OFFSET 0x88
|
||||
#define CHARSAVE_COLOR_OFFSET 0x98
|
||||
|
||||
#define charstatus_to_portstatus(status) ((((status & 0xFF00) << 1) | (status & 0x00FF)) | 0x8080)
|
||||
#define portstatus_to_charstatus(status) (((status & 0x7F00) >> 1) | (status & 0x007F))
|
||||
|
||||
static void dbs_packet_set_charinfo_level(char * CharName, char * charinfo)
|
||||
{
|
||||
if (prefs_get_difficulty_hack()) { /* difficulty hack enabled */
|
||||
unsigned int level = bn_int_get((bn_basic*)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET]);
|
||||
unsigned int plevel = bn_byte_get((bn_basic*)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET]);
|
||||
|
||||
/* levels 257 thru 355 */
|
||||
if (level != plevel) {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "level mis-match for {} ( {} != {} ) setting to 255", CharName, level, plevel);
|
||||
bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET], 255);
|
||||
bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET], 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dbs_packet_fix_charinfo(t_d2dbs_connection * conn, char * AccountName, char * CharName, char * charsave)
|
||||
{
|
||||
if (prefs_get_difficulty_hack()) {
|
||||
unsigned char charinfo[CHARINFO_SIZE];
|
||||
unsigned int level = bn_byte_get((bn_basic*)&charsave[CHARSAVE_LEVEL_OFFSET]);
|
||||
unsigned short status = bn_short_get((bn_basic*)&charsave[CHARSAVE_STATUS_OFFSET]);
|
||||
unsigned short pstatus = charstatus_to_portstatus(status);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* charinfo is only updated from level 1 to 99 (d2gs issue)
|
||||
* from 100 to 256 d2gs does not send it
|
||||
* when value rolls over (level 256 = 0)
|
||||
* and charactar reaches level 257 (rolled over to level 1)
|
||||
* d2gs starts sending it agian until level 356 (rolled over to 100)
|
||||
* is reached agian. etc. etc. etc.
|
||||
*/
|
||||
if (level == 0) /* level 256, 512, 768, etc */
|
||||
level = 255;
|
||||
|
||||
if (level < 100)
|
||||
return 1; /* d2gs will send charinfo - level will be set to 255 at that std::time if needed */
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "level {} > 99 for {}", level, CharName);
|
||||
|
||||
if (!(dbs_packet_getdata_charinfo(conn, AccountName, CharName, (char*)charinfo, CHARINFO_SIZE))) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unable to get charinfo for {}", CharName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if level in charinfo file is already set to 255,
|
||||
* then is must have been set when d2gs sent the charinfo
|
||||
* and got a level mis-match (levels 257 - 355)
|
||||
* or level is actually 255. In eather case we set to 255
|
||||
* this should work for any level mod
|
||||
*/
|
||||
if (bn_byte_get(&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET]) == 255)
|
||||
level = 255;
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "updating charinfo for {} -> level = {} , status = 0x{:04X} , pstatus = 0x{:04X}", CharName, level, status, pstatus);
|
||||
bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET], level);
|
||||
bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET], level);
|
||||
bn_short_set((bn_short *)&charinfo[CHARINFO_PORTRAIT_STATUS_OFFSET], pstatus);
|
||||
bn_int_set((bn_int *)&charinfo[CHARINFO_SUMMARY_STATUS_OFFSET], status);
|
||||
|
||||
for (i = 0; i < 11; i++) {
|
||||
bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_GFX_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i]));
|
||||
bn_byte_set((bn_byte *)&charinfo[CHARINFO_PORTRAIT_COLOR_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i]));
|
||||
}
|
||||
|
||||
if (!(dbs_packet_savedata_charinfo(conn, AccountName, CharName, (char*)charinfo, CHARINFO_SIZE))) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unable to save charinfo for {}", CharName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; /* charinfo updated */
|
||||
}
|
||||
|
||||
return 1; /* difficulty hack not enabled */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001 faster (lqx@cic.tsinghua.edu.cn)
|
||||
* Copyright (C) 2001 sousou (liupeng.cs@263.net)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_DBSPACKET_H
|
||||
#define INCLUDED_DBSPACKET_H
|
||||
|
||||
#include "common/bn_type.h"
|
||||
#include "dbserver.h"
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
typedef struct {
|
||||
bn_short size;
|
||||
bn_short type;
|
||||
bn_int seqno;
|
||||
} t_d2dbs_d2gs_header;
|
||||
|
||||
typedef struct {
|
||||
bn_byte cclass;
|
||||
} t_d2gs_d2dbs_connect;
|
||||
#define CONNECT_CLASS_D2GS_TO_D2DBS 0x65
|
||||
|
||||
#define D2GS_D2DBS_SAVE_DATA_REQUEST 0x30
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_short datatype;
|
||||
bn_short datalen;
|
||||
/* AccountName */
|
||||
/* CharName */
|
||||
/* data */
|
||||
} t_d2gs_d2dbs_save_data_request;
|
||||
#define D2GS_DATA_CHARSAVE 0x01
|
||||
#define D2GS_DATA_PORTRAIT 0x02
|
||||
|
||||
#define D2DBS_D2GS_SAVE_DATA_REPLY 0x30
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int result;
|
||||
bn_short datatype;
|
||||
/* CharName */
|
||||
} t_d2dbs_d2gs_save_data_reply;
|
||||
#define D2DBS_SAVE_DATA_SUCCESS 0
|
||||
#define D2DBS_SAVE_DATA_FAILED 1
|
||||
|
||||
#define D2GS_D2DBS_GET_DATA_REQUEST 0x31
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_short datatype;
|
||||
/* AccountName */
|
||||
/* CharName */
|
||||
} t_d2gs_d2dbs_get_data_request;
|
||||
|
||||
#define D2DBS_D2GS_GET_DATA_REPLY 0x31
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int result;
|
||||
bn_int charcreatetime;
|
||||
bn_int allowladder;
|
||||
bn_short datatype;
|
||||
bn_short datalen;
|
||||
/* CharName */
|
||||
/* data */
|
||||
} t_d2dbs_d2gs_get_data_reply;
|
||||
|
||||
#define D2DBS_GET_DATA_SUCCESS 0
|
||||
#define D2DBS_GET_DATA_FAILED 1
|
||||
#define D2DBS_GET_DATA_CHARLOCKED 2
|
||||
|
||||
#define D2GS_D2DBS_UPDATE_LADDER 0x32
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int charlevel;
|
||||
bn_int charexplow;
|
||||
bn_int charexphigh;
|
||||
bn_short charclass;
|
||||
bn_short charstatus;
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
} t_d2gs_d2dbs_update_ladder;
|
||||
|
||||
#define D2GS_D2DBS_CHAR_LOCK 0x33
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
bn_int lockstatus;
|
||||
/* CharName */
|
||||
/* RealmName */
|
||||
} t_d2gs_d2dbs_char_lock;
|
||||
|
||||
#define D2DBS_D2GS_ECHOREQUEST 0x34
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
} t_d2dbs_d2gs_echorequest;
|
||||
|
||||
#define D2GS_D2DBS_ECHOREPLY 0x34
|
||||
typedef struct {
|
||||
t_d2dbs_d2gs_header h;
|
||||
} t_d2gs_d2dbs_echoreply;
|
||||
|
||||
|
||||
extern int dbs_packet_handle(t_d2dbs_connection * conn);
|
||||
extern int dbs_keepalive(void);
|
||||
extern int dbs_check_timeout(void);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
915
src/d2dbs/handle_d2gs.cpp
Normal file
915
src/d2dbs/handle_d2gs.cpp
Normal file
|
@ -0,0 +1,915 @@
|
|||
/*
|
||||
* 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 "common/setup_before.h"
|
||||
#include "setup.h"
|
||||
#include "handle_d2gs.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <nonstd/optional.hpp>
|
||||
|
||||
#include "common/addr.h"
|
||||
#include "common/bn_type.h"
|
||||
#include "common/d2char_checksum.h"
|
||||
#include "common/d2cs_d2gs_character.h"
|
||||
#include "common/d2dbs_d2gs_protocol.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/packet.h"
|
||||
#include "common/xalloc.h"
|
||||
#include "common/xstring.h"
|
||||
|
||||
#include "compat/mkdir.h"
|
||||
#include "compat/rename.h"
|
||||
|
||||
#include "charlock.h"
|
||||
#include "connection.h"
|
||||
#include "d2ladder.h"
|
||||
#include "prefs.h"
|
||||
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* c, const char* account_name, const char* char_name, const char* data, unsigned int datalen);
|
||||
static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* c, const char* account_name, const char* char_name, const char* data, unsigned int datalen);
|
||||
static nonstd::optional<std::vector<std::uint8_t>> dbs_packet_getdata_charsave(t_d2dbs_connection* c, const char* account_name, const char* char_name);
|
||||
static nonstd::optional<std::vector<std::uint8_t>> dbs_packet_getdata_charinfo(t_d2dbs_connection* c, const char* account_name, const char* char_name);
|
||||
|
||||
static int dbs_packet_savedata(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
static int dbs_packet_getdata(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
static int dbs_packet_updateladder(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
static int dbs_packet_charlock(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
static int dbs_packet_echoreply(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
|
||||
static int dbs_packet_fix_charinfo(t_d2dbs_connection* c, const char* AccountName, const char* CharName, const char* charsave);
|
||||
static void dbs_packet_set_charinfo_level(const char* char_name, char* charinfo);
|
||||
|
||||
|
||||
std::unordered_map<decltype(packet_get_type(std::declval<t_packet*>())), std::function<int(t_d2dbs_connection*, const t_packet* const packet)>> d2gs_packet_table = {
|
||||
{ D2GS_D2DBS_SAVE_DATA_REQUEST, dbs_packet_savedata },
|
||||
{ D2GS_D2DBS_GET_DATA_REQUEST, dbs_packet_getdata },
|
||||
{ D2GS_D2DBS_UPDATE_LADDER, dbs_packet_updateladder },
|
||||
{ D2GS_D2DBS_CHAR_LOCK, dbs_packet_charlock },
|
||||
{ D2DBS_D2GS_ECHOREQUEST, dbs_packet_echoreply }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* return value:
|
||||
* 1 : process one or more packet
|
||||
* 0 : not get a whole packet,do nothing
|
||||
* -1 : error
|
||||
*/
|
||||
int handle_d2gs_packet(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!packet)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (packet_get_class(packet) != packet_class_d2dbs_d2gs)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", c->sd, static_cast<std::underlying_type<t_packet_class>::type>(packet_get_class(packet)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (conn_get_state(c))
|
||||
{
|
||||
case conn_state_loggedin:
|
||||
switch (d2gs_packet_table.at(packet_get_type(packet))(c, packet))
|
||||
{
|
||||
case 1:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown (logged in) d2gs packet type 0x{:04x}, len {}", c->sd, packet_get_type(packet), packet_get_size(packet));
|
||||
break;
|
||||
case -1:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] (logged in) got error handling packet type 0x{:04x}, len {}", c->sd, packet_get_type(packet), packet_get_size(packet));
|
||||
break;
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] invalid login state {}", c->sd, static_cast<std::underlying_type<t_conn_state>::type>(conn_get_state(c)));
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int dbs_packet_savedata_charsave(t_d2dbs_connection* c, const char* account_name, const char* char_name, const char* data, unsigned int datalen)
|
||||
{
|
||||
char* AccountName = xstrdup(account_name);
|
||||
if (!AccountName)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* CharName = xstrdup(char_name);
|
||||
if (!CharName)
|
||||
{
|
||||
xfree(AccountName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::FILE* fd;
|
||||
int checksum_header;
|
||||
int checksum_calc;
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
//check if checksum is ok
|
||||
checksum_header = bn_int_get((bn_basic*)&data[D2CHARSAVE_CHECKSUM_OFFSET]);
|
||||
checksum_calc = d2charsave_checksum((unsigned char*)data, datalen, D2CHARSAVE_CHECKSUM_OFFSET);
|
||||
|
||||
if (checksum_header != checksum_calc)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "received ({}) and calculated({}) checksum do not match - discarding charsave", checksum_header, checksum_calc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::string filename = fmt::format("{}/.{}.tmp", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
fd = std::fopen(filename.c_str(), "wb");
|
||||
if (!fd)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t curlen = 0;
|
||||
std::size_t leftlen = datalen;
|
||||
while (curlen < datalen)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
curlen += readlen;
|
||||
leftlen -= readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
|
||||
std::string bakfile = fmt::format("{}/{}", prefs_get_charsave_bak_dir(), CharName);
|
||||
std::string savefile = fmt::format("{}/{}", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
if (p_rename(savefile.c_str(), bakfile.c_str()) == -1)
|
||||
{
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile);
|
||||
}
|
||||
if (p_rename(filename.c_str(), savefile.c_str()) == -1)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile);
|
||||
return 0;
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "saved charsave {}(*{}) for gs {}({})", CharName, AccountName, c->serverip, c->serverid);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return datalen;
|
||||
}
|
||||
|
||||
static unsigned int dbs_packet_savedata_charinfo(t_d2dbs_connection* c, const char* account_name, const char* char_name, const char* data, unsigned int datalen)
|
||||
{
|
||||
char* AccountName = xstrdup(account_name);
|
||||
if (!AccountName)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* CharName = xstrdup(char_name);
|
||||
if (!CharName)
|
||||
{
|
||||
xfree(AccountName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filepath = fmt::format("{}/{}", prefs_get_charinfo_bak_dir(), AccountName);
|
||||
{
|
||||
struct stat statbuf;
|
||||
if (stat(filepath.c_str(), &statbuf) == -1)
|
||||
{
|
||||
if (p_mkdir(filepath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "created charinfo directory: {}", filepath);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "failed to create charinfo directory \"{}\" (errno: {})", filepath, errno);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string filename = fmt::format("{}/{}/.{}.tmp", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
std::FILE* fd = std::fopen(filename.c_str(), "wb");
|
||||
if (!fd)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t curlen = 0;
|
||||
std::size_t leftlen = datalen;
|
||||
while (curlen < datalen)
|
||||
{
|
||||
std::size_t writelen = leftlen > 2000 ? 2000 : leftlen;
|
||||
|
||||
std::size_t readlen = std::fwrite(data + curlen, 1, writelen, fd);
|
||||
if (readlen <= 0)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "write() failed error : {}", std::strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
curlen += readlen;
|
||||
leftlen -= readlen;
|
||||
}
|
||||
std::fclose(fd);
|
||||
|
||||
std::string bakfile = fmt::format("{}/{}/{}", prefs_get_charinfo_bak_dir(), AccountName, CharName);
|
||||
std::string savefile = fmt::format("{}/{}/{}", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
if (p_rename(savefile.c_str(), bakfile.c_str()) == -1)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "error std::rename {} to {}", savefile, bakfile);
|
||||
}
|
||||
if (p_rename(filename.c_str(), savefile.c_str()) == -1)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "error std::rename {} to {}", filename, savefile);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return 0;
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "saved charinfo {}(*{}) for gs {}({})", CharName, AccountName, c->serverip, c->serverid);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return datalen;
|
||||
}
|
||||
|
||||
static nonstd::optional<std::vector<std::uint8_t>> dbs_packet_getdata_charsave(t_d2dbs_connection* c, const char* account_name, const char* char_name)
|
||||
{
|
||||
char* AccountName = xstrdup(account_name);
|
||||
if (!AccountName)
|
||||
{
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
char* CharName = xstrdup(char_name);
|
||||
if (!CharName)
|
||||
{
|
||||
xfree(AccountName);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filename = fmt::format("{}/{}", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
std::string filename_d2closed = fmt::format("{}/{}.d2s", d2dbs_prefs_get_charsave_dir(), CharName);
|
||||
if ((access(filename.c_str(), F_OK) < 0) && (access(filename_d2closed.c_str(), F_OK) == 0))
|
||||
{
|
||||
if (std::rename(filename_d2closed.c_str(), filename.c_str()) != 0)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to rename file \"{}\" to \"{}\"", filename_d2closed, filename);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::FILE* fd = std::fopen(filename.c_str(), "rb");
|
||||
if (!fd)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
if (std::fseek(fd, 0, SEEK_END) != 0)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "std::fseek() failed");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
long filesize = std::ftell(fd);
|
||||
if (filesize == -1L)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "ftell() failed");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
std::rewind(fd);
|
||||
|
||||
std::vector<std::uint8_t> charsave(filesize);
|
||||
std::size_t readlen = std::fread(charsave.data(), sizeof(decltype(charsave)::value_type), charsave.size(), fd);
|
||||
if (readlen < filesize)
|
||||
{
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to read charinfo {}(*{}): read {} bytes, expected {} bytes", CharName, AccountName, readlen, filesize);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "loaded charsave {}(*{}) for gs {}({})", CharName, AccountName, c->serverip, c->serverid);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
|
||||
return charsave;
|
||||
}
|
||||
|
||||
static nonstd::optional<std::vector<std::uint8_t>> dbs_packet_getdata_charinfo(t_d2dbs_connection* c, const char* account_name, const char* char_name)
|
||||
{
|
||||
char* AccountName = xstrdup(account_name);
|
||||
if (!AccountName)
|
||||
{
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
char* CharName = xstrdup(char_name);
|
||||
if (!CharName)
|
||||
{
|
||||
xfree(AccountName);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
strtolower(AccountName);
|
||||
strtolower(CharName);
|
||||
|
||||
std::string filename = fmt::format("{}/{}/{}", d2dbs_prefs_get_charinfo_dir(), AccountName, CharName);
|
||||
std::FILE* fd = std::fopen(filename.c_str(), "rb");
|
||||
if (!fd)
|
||||
{
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "open() failed : {}", filename);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
if (std::fseek(fd, 0, SEEK_END) != 0)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "std::fseek() failed");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
long filesize = std::ftell(fd);
|
||||
if (filesize == -1)
|
||||
{
|
||||
std::fclose(fd);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "std::ftell() failed");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
std::rewind(fd);
|
||||
|
||||
std::vector<std::uint8_t> charinfo(filesize);
|
||||
std::size_t readlen = std::fread(charinfo.data(), sizeof(decltype(charinfo)::value_type), charinfo.size(), fd);
|
||||
if (readlen < filesize)
|
||||
{
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to read charinfo {}(*{}): read {} bytes, expected {} bytes", CharName, AccountName, readlen, filesize);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
std::fclose(fd);
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "loaded charinfo {}(*{}) for gs {}({})", CharName, AccountName, c->serverip, c->serverid);
|
||||
xfree(AccountName);
|
||||
xfree(CharName);
|
||||
|
||||
return charinfo;
|
||||
}
|
||||
|
||||
static int dbs_packet_savedata(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (packet_get_size(packet) < sizeof(t_d2gs_d2dbs_save_data_request))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad savedata packet (expected {} bytes, got {})", c->sd, sizeof(t_d2gs_d2dbs_save_data_request), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto datatype = bn_short_get(packet->u.d2gs_d2dbs_save_data_request.datatype);
|
||||
const auto datalen = bn_short_get(packet->u.d2gs_d2dbs_save_data_request.datalen);
|
||||
|
||||
const char* const account_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_save_data_request), MAX_USERNAME_LEN);
|
||||
if (!account_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SAVEDATAREQUEST (missing or too long account name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const char_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_save_data_request) + std::strlen(account_name) + 1, MAX_CHARNAME_LEN);
|
||||
if (!char_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SAVEDATAREQUEST (missing or too long char name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const realm_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_save_data_request) + std::strlen(account_name) + 1 + std::strlen(char_name) + 1, MAX_REALMNAME_LEN);
|
||||
if (!realm_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SAVEDATAREQUEST (missing or too long realm name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const void* const data = packet_get_data_const(packet, sizeof(t_d2gs_d2dbs_save_data_request) + std::strlen(account_name) + 1 + std::strlen(char_name) + 1 + std::strlen(realm_name) + 1, datalen);
|
||||
if (!data)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad SAVEDATAREQUEST (missing or too long data)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = D2DBS_SAVE_DATA_FAILED;
|
||||
if (datatype == D2GS_DATA_CHARSAVE)
|
||||
{
|
||||
if (dbs_packet_savedata_charsave(c, account_name, char_name, static_cast<const char*>(data), datalen) > 0 &&
|
||||
dbs_packet_fix_charinfo(c, account_name, char_name, static_cast<const char*>(data)))
|
||||
{
|
||||
result = D2DBS_SAVE_DATA_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = D2DBS_SAVE_DATA_FAILED;
|
||||
}
|
||||
}
|
||||
else if (datatype == D2GS_DATA_PORTRAIT)
|
||||
{
|
||||
char* modified_data = static_cast<char*>(xmalloc(datalen));
|
||||
if (!modified_data)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memcpy(modified_data, data, datalen);
|
||||
|
||||
/* if level is > 255 , sets level to 255 */
|
||||
dbs_packet_set_charinfo_level(char_name, modified_data);
|
||||
if (dbs_packet_savedata_charinfo(c, account_name, char_name, modified_data, datalen) > 0)
|
||||
{
|
||||
result = D2DBS_SAVE_DATA_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = D2DBS_SAVE_DATA_FAILED;
|
||||
}
|
||||
|
||||
xfree(modified_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
t_packet* rpacket = packet_create(packet_class_d2dbs_d2gs);
|
||||
if (!rpacket)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_set_size(rpacket, sizeof(t_d2dbs_d2gs_save_data_reply));
|
||||
packet_set_type(rpacket, D2DBS_D2GS_SAVE_DATA_REPLY);
|
||||
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_save_data_reply.h.seqno, bn_int_get(packet->u.d2dbs_d2gs_save_data_reply.h.seqno));
|
||||
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_save_data_reply.result, result);
|
||||
bn_short_set(&rpacket->u.d2dbs_d2gs_save_data_reply.datatype, datatype);
|
||||
packet_append_string(rpacket, char_name);
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbs_packet_echoreply(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
c->last_active = std::time(nullptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbs_packet_getdata(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (packet_get_size(packet) < sizeof(t_d2gs_d2dbs_get_data_request))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST packet (expected {} bytes, got {})", c->sd, sizeof(t_d2gs_d2dbs_get_data_request), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto datatype = bn_short_get(packet->u.d2gs_d2dbs_save_data_request.datatype);
|
||||
|
||||
const char* const account_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_get_data_request), MAX_USERNAME_LEN);
|
||||
if (!account_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long account name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const char_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_get_data_request) + std::strlen(account_name) + 1, MAX_CHARNAME_LEN);
|
||||
if (!char_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long char name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const realm_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_get_data_request) + std::strlen(account_name) + 1 + std::strlen(char_name) + 1, MAX_REALMNAME_LEN);
|
||||
if (!realm_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long realm name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
nonstd::optional<std::vector<std::uint8_t>> charinfo = nonstd::nullopt;
|
||||
nonstd::optional<std::vector<std::uint8_t>> charsave = nonstd::nullopt;
|
||||
unsigned int result;
|
||||
if (datatype == D2GS_DATA_CHARSAVE)
|
||||
{
|
||||
unsigned int gsid = 0;
|
||||
if (cl_query_charlock_status((unsigned char*)char_name, (unsigned char*)realm_name, &gsid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "char {}(*{})@{} is already locked on gs {}", char_name, account_name, realm_name, gsid);
|
||||
result = D2DBS_GET_DATA_CHARLOCKED;
|
||||
}
|
||||
else if (cl_lock_char((unsigned char*)char_name, (unsigned char*)realm_name, c->serverid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to lock char {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
result = D2DBS_GET_DATA_CHARLOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "lock char {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
|
||||
charsave = dbs_packet_getdata_charsave(c, account_name, char_name);
|
||||
if (charsave.has_value())
|
||||
{
|
||||
charinfo = dbs_packet_getdata_charinfo(c, account_name, char_name);
|
||||
if (charinfo.has_value())
|
||||
{
|
||||
result = D2DBS_GET_DATA_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = D2DBS_GET_DATA_FAILED;
|
||||
|
||||
if (cl_unlock_char((unsigned char*)char_name, (unsigned char*)realm_name, gsid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock char {}(*{})@{} for gs {}({})", char_name, \
|
||||
account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", char_name, \
|
||||
account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = D2DBS_GET_DATA_FAILED;
|
||||
|
||||
if (cl_unlock_char((unsigned char*)char_name, (unsigned char*)realm_name, gsid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "faled to unlock char {}(*{})@{} for gs {}({})", char_name, \
|
||||
account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock char {}(*{})@{} for gs {}({})", char_name, \
|
||||
account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (datatype == D2GS_DATA_PORTRAIT)
|
||||
{
|
||||
charinfo = dbs_packet_getdata_charinfo(c, account_name, char_name);
|
||||
|
||||
result = charinfo.has_value() ? D2DBS_GET_DATA_SUCCESS : D2DBS_GET_DATA_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unknown data type {}", datatype);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
t_packet* rpacket = packet_create(packet_class_d2dbs_d2gs);
|
||||
if (!rpacket)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_set_size(rpacket, sizeof(t_d2dbs_d2gs_get_data_reply));
|
||||
packet_set_type(rpacket, D2DBS_D2GS_GET_DATA_REPLY);
|
||||
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.h.seqno, bn_int_get(packet->u.d2dbs_d2gs_get_data_reply.h.seqno));
|
||||
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.result, result);
|
||||
if (datatype == D2GS_DATA_CHARSAVE && result == D2DBS_GET_DATA_SUCCESS && charinfo.has_value())
|
||||
{
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.charcreatetime, bn_int_get(reinterpret_cast<t_d2charinfo_file*>(charinfo.value().data())->header.create_time));
|
||||
|
||||
// FIXME: this should be rewritten to support string formatted std::time
|
||||
if (bn_int_get(reinterpret_cast<t_d2charinfo_file*>(charinfo.value().data())->header.create_time) >= prefs_get_ladderinit_time())
|
||||
{
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.allowladder, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.allowladder, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.charcreatetime, 0);
|
||||
bn_int_set(&rpacket->u.d2dbs_d2gs_get_data_reply.allowladder, 0);
|
||||
}
|
||||
bn_short_set(&rpacket->u.d2dbs_d2gs_get_data_reply.datatype, datatype);
|
||||
packet_append_string(rpacket, char_name);
|
||||
if (result == D2DBS_GET_DATA_SUCCESS)
|
||||
{
|
||||
if (datatype == D2GS_DATA_CHARSAVE)
|
||||
{
|
||||
bn_short_set(&rpacket->u.d2dbs_d2gs_get_data_reply.datalen, charsave.value().size());
|
||||
packet_append_data(rpacket, charsave.value().data(), charsave.value().size());
|
||||
}
|
||||
else
|
||||
{
|
||||
bn_short_set(&rpacket->u.d2dbs_d2gs_get_data_reply.datalen, charinfo.value().size());
|
||||
packet_append_data(rpacket, charinfo.value().data(), charinfo.value().size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bn_short_set(&rpacket->u.d2dbs_d2gs_get_data_reply.datalen, 0);
|
||||
}
|
||||
|
||||
|
||||
conn_push_outqueue(c, rpacket);
|
||||
|
||||
packet_del_ref(rpacket);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbs_packet_updateladder(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (packet_get_size(packet) < sizeof(t_d2gs_d2dbs_update_ladder))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UPDATELADDER packet (expected {} bytes, got {})", c->sd, sizeof(t_d2gs_d2dbs_update_ladder), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const char_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_update_ladder), MAX_CHARNAME_LEN);
|
||||
if (!char_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UPDATELADDER (missing or too long char name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const realm_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_update_ladder) + std::strlen(char_name) + 1, MAX_REALMNAME_LEN);
|
||||
if (!realm_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad UPDATELADDER (missing or too long realm name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
t_d2ladder_info charladderinfo = {};
|
||||
std::strcpy(charladderinfo.charname, char_name);
|
||||
charladderinfo.experience = bn_int_get(packet->u.d2gs_d2dbs_update_ladder.charexplow);
|
||||
charladderinfo.level = bn_int_get(packet->u.d2gs_d2dbs_update_ladder.charlevel);
|
||||
charladderinfo.status = bn_short_get(packet->u.d2gs_d2dbs_update_ladder.charstatus);
|
||||
charladderinfo.chclass = bn_short_get(packet->u.d2gs_d2dbs_update_ladder.charclass);
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "update ladder for {}@{} for gs {}({})", char_name, realm_name, c->serverip, c->serverid);
|
||||
d2ladder_update(&charladderinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dbs_packet_charlock(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (packet_get_size(packet) < sizeof(t_d2gs_d2dbs_char_lock))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad CHARLOCK packet (expected {} bytes, got {})", c->sd, sizeof(t_d2gs_d2dbs_char_lock), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const account_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_char_lock), MAX_USERNAME_LEN);
|
||||
if (!account_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long account name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const char_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_char_lock) + std::strlen(account_name) + 1, MAX_CHARNAME_LEN);
|
||||
if (!char_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long char name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* const realm_name = packet_get_str_const(packet, sizeof(t_d2gs_d2dbs_char_lock) + std::strlen(account_name) + 1 + std::strlen(char_name) + 1, MAX_REALMNAME_LEN);
|
||||
if (!realm_name)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad GETDATAREQUEST (missing or too long realm name)", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bn_int_get(packet->u.d2gs_d2dbs_char_lock.lockstatus))
|
||||
{
|
||||
if (cl_lock_char((unsigned char*)char_name, (unsigned char*)realm_name, c->serverid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to lock character {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "lock character {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cl_unlock_char((unsigned char*)char_name, (unsigned char*)realm_name, c->serverid) != 0)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to unlock character {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "unlock character {}(*{})@{} for gs {}({})", char_name, account_name, realm_name, c->serverip, c->serverid);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************/
|
||||
#define CHARINFO_SIZE 0xC0
|
||||
#define CHARINFO_PORTRAIT_LEVEL_OFFSET 0x89
|
||||
#define CHARINFO_PORTRAIT_STATUS_OFFSET 0x8A
|
||||
#define CHARINFO_SUMMARY_LEVEL_OFFSET 0xB8
|
||||
#define CHARINFO_SUMMARY_STATUS_OFFSET 0xB4
|
||||
#define CHARINFO_PORTRAIT_GFX_OFFSET 0x72
|
||||
#define CHARINFO_PORTRAIT_COLOR_OFFSET 0x7E
|
||||
|
||||
#define CHARSAVE_LEVEL_OFFSET 0x2B
|
||||
#define CHARSAVE_STATUS_OFFSET 0x24
|
||||
#define CHARSAVE_GFX_OFFSET 0x88
|
||||
#define CHARSAVE_COLOR_OFFSET 0x98
|
||||
|
||||
#define charstatus_to_portstatus(status) ((((status & 0xFF00) << 1) | (status & 0x00FF)) | 0x8080)
|
||||
#define portstatus_to_charstatus(status) (((status & 0x7F00) >> 1) | (status & 0x007F))
|
||||
|
||||
static void dbs_packet_set_charinfo_level(const char* char_name, char* charinfo)
|
||||
{
|
||||
if (prefs_get_difficulty_hack())
|
||||
{ /* difficulty hack enabled */
|
||||
unsigned int level = bn_int_get((bn_basic*)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET]);
|
||||
unsigned int plevel = bn_byte_get((bn_basic*)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET]);
|
||||
|
||||
/* levels 257 thru 355 */
|
||||
if (level != plevel)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "level mis-match for {} ( {} != {} ) setting to 255", char_name, level, plevel);
|
||||
bn_byte_set((bn_byte*)&charinfo[CHARINFO_PORTRAIT_LEVEL_OFFSET], 255);
|
||||
bn_int_set((bn_int*)&charinfo[CHARINFO_SUMMARY_LEVEL_OFFSET], 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dbs_packet_fix_charinfo(t_d2dbs_connection* c, const char* AccountName, const char* CharName, const char* charsave)
|
||||
{
|
||||
if (prefs_get_difficulty_hack())
|
||||
{
|
||||
unsigned int level = bn_byte_get((bn_basic*)&charsave[CHARSAVE_LEVEL_OFFSET]);
|
||||
unsigned short status = bn_short_get((bn_basic*)&charsave[CHARSAVE_STATUS_OFFSET]);
|
||||
unsigned short pstatus = charstatus_to_portstatus(status);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* charinfo is only updated from level 1 to 99 (d2gs issue)
|
||||
* from 100 to 256 d2gs does not send it
|
||||
* when value rolls over (level 256 = 0)
|
||||
* and charactar reaches level 257 (rolled over to level 1)
|
||||
* d2gs starts sending it agian until level 356 (rolled over to 100)
|
||||
* is reached agian. etc. etc. etc.
|
||||
*/
|
||||
if (level == 0) /* level 256, 512, 768, etc */
|
||||
level = 255;
|
||||
|
||||
if (level < 100)
|
||||
return 1; /* d2gs will send charinfo - level will be set to 255 at that std::time if needed */
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "level {} > 99 for {}", level, CharName);
|
||||
|
||||
nonstd::optional<std::vector<std::uint8_t>> charinfo = dbs_packet_getdata_charinfo(c, AccountName, CharName);
|
||||
if (!charinfo.has_value())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unable to get charinfo for {}", CharName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if level in charinfo file is already set to 255,
|
||||
* then is must have been set when d2gs sent the charinfo
|
||||
* and got a level mis-match (levels 257 - 355)
|
||||
* or level is actually 255. In eather case we set to 255
|
||||
* this should work for any level mod
|
||||
*/
|
||||
if (bn_byte_get(&charinfo.value().data()[CHARINFO_PORTRAIT_LEVEL_OFFSET]) == 255)
|
||||
level = 255;
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "updating charinfo for {} -> level = {} , status = 0x{:04X} , pstatus = 0x{:04X}", CharName, level, status, pstatus);
|
||||
bn_byte_set((bn_byte*)&charinfo.value().data()[CHARINFO_PORTRAIT_LEVEL_OFFSET], level);
|
||||
bn_int_set((bn_int*)&charinfo.value().data()[CHARINFO_SUMMARY_LEVEL_OFFSET], level);
|
||||
bn_short_set((bn_short*)&charinfo.value().data()[CHARINFO_PORTRAIT_STATUS_OFFSET], pstatus);
|
||||
bn_int_set((bn_int*)&charinfo.value().data()[CHARINFO_SUMMARY_STATUS_OFFSET], status);
|
||||
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
bn_byte_set((bn_byte*)&charinfo.value().data()[CHARINFO_PORTRAIT_GFX_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i]));
|
||||
bn_byte_set((bn_byte*)&charinfo.value().data()[CHARINFO_PORTRAIT_COLOR_OFFSET + i], bn_byte_get((bn_basic*)&charsave[CHARSAVE_GFX_OFFSET + i]));
|
||||
}
|
||||
|
||||
if (!(dbs_packet_savedata_charinfo(c, AccountName, CharName, reinterpret_cast<const char*>(charinfo.value().data()), charinfo.value().size())))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "unable to save charinfo for {}", CharName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; /* charinfo updated */
|
||||
}
|
||||
|
||||
return 1; /* difficulty hack not enabled */
|
||||
}
|
||||
}
|
||||
}
|
35
src/d2dbs/handle_d2gs.h
Normal file
35
src/d2dbs/handle_d2gs.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_HANDLE_D2GS_H
|
||||
#define INCLUDED_HANDLE_D2GS_H
|
||||
|
||||
#include "connection.h"
|
||||
#include "common/packet.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
int handle_d2gs_packet(t_d2dbs_connection* conn, const t_packet* const packet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
150
src/d2dbs/handle_init.cpp
Normal file
150
src/d2dbs/handle_init.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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 "common/setup_before.h"
|
||||
#include "setup.h"
|
||||
#include "handle_init.h"
|
||||
|
||||
#include "common/addr.h"
|
||||
#include "common/bn_type.h"
|
||||
#include "common/eventlog.h"
|
||||
#include "common/init_protocol.h"
|
||||
#include "common/packet.h"
|
||||
#include "common/xalloc.h"
|
||||
|
||||
#include "compat/strsep.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "prefs.h"
|
||||
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
static bool dbs_verify_ipaddr(t_d2dbs_connection* c);
|
||||
|
||||
|
||||
int handle_init_packet(t_d2dbs_connection* c, const t_packet* const packet)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!packet)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got NULL packet", c->sd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (packet_get_class(packet) != packet_class_init)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] got bad packet (class {})", c->sd, (int)packet_get_class(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (packet_get_type(packet))
|
||||
{
|
||||
case CLIENT_INITCONN:
|
||||
switch (bn_byte_get(packet->u.client_initconn.cclass))
|
||||
{
|
||||
case CLIENT_INITCONN_CLASS_D2GS_D2DBS:
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] client initiated d2gs connection", c->sd);
|
||||
|
||||
if (!dbs_verify_ipaddr(c))
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] d2gs connection from unknown ip address {}", c->sd, addr_num_to_ip_str(c->ipaddr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn_set_class(c, conn_class_d2gs);
|
||||
conn_set_state(c, conn_state_loggedin);
|
||||
|
||||
break;
|
||||
default:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] client requested unknown class 0x{:02x} (length {}) (closing connection)", c->sd, bn_byte_get(packet->u.client_initconn.cclass), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "[{}] unknown init packet type 0x{:04x}, len {}", c->sd, packet_get_type(packet), packet_get_size(packet));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dbs_verify_ipaddr(t_d2dbs_connection* c)
|
||||
{
|
||||
bool c_ipaddr_is_valid = false;
|
||||
|
||||
{
|
||||
char* adlist = xstrdup(d2dbs_prefs_get_d2gs_list());
|
||||
char* temp = adlist;
|
||||
char* s;
|
||||
while ((s = strsep(&temp, ",")))
|
||||
{
|
||||
unsigned int resolveipaddr = 0;
|
||||
host_lookup(s, &resolveipaddr);
|
||||
if (resolveipaddr == 0) continue;
|
||||
|
||||
if (c->ipaddr == resolveipaddr)
|
||||
{
|
||||
c_ipaddr_is_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xfree(adlist);
|
||||
}
|
||||
|
||||
if (c_ipaddr_is_valid)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] ip address {} is valid", c->sd, addr_num_to_ip_str(c->ipaddr));
|
||||
|
||||
for (auto tempc : connlist())
|
||||
{
|
||||
if (!tempc)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tempc != c && tempc->ipaddr == c->ipaddr)
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] destroying previous connection {} from same ip address", c->sd, tempc->serverid);
|
||||
conn_set_state(tempc, conn_state_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
c->verified = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "[{}] ip address {} is invalid", c->sd, addr_num_to_ip_str(c->ipaddr));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
34
src/d2dbs/handle_init.h
Normal file
34
src/d2dbs/handle_init.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_HANDLE_INIT_H
|
||||
#define INCLUDED_HANDLE_INIT_H
|
||||
|
||||
#include "connection.h"
|
||||
#include "common/packet.h"
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
int handle_init_packet(t_d2dbs_connection* c, const t_packet* const packet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -79,7 +79,7 @@ namespace pvpgn
|
|||
}
|
||||
if (signal_data.exit_time) {
|
||||
now = std::time(NULL);
|
||||
if (now >= (signed)signal_data.exit_time) {
|
||||
if (now >= signal_data.exit_time) {
|
||||
signal_data.exit_time = 0;
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "shutdown server due to std::signal");
|
||||
return -1;
|
||||
|
@ -182,6 +182,11 @@ namespace pvpgn
|
|||
}
|
||||
#endif
|
||||
|
||||
std::time_t d2dbs_get_exit_time()
|
||||
{
|
||||
return signal_data.exit_time;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#ifndef INCLUDED_HANDLE_SIGNAL_H
|
||||
#define INCLUDED_HANDLE_SIGNAL_H
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
|
@ -35,6 +37,8 @@ namespace pvpgn
|
|||
|
||||
extern int d2dbs_handle_signal(void);
|
||||
|
||||
extern std::time_t d2dbs_get_exit_time();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
using namespace pvpgn::d2dbs;
|
||||
using namespace pvpgn;
|
||||
|
||||
static std::FILE * eventlog_fp;
|
||||
|
||||
char serviceLongName[] = "d2dbs service";
|
||||
char serviceName[] = "d2dbs";
|
||||
|
@ -64,8 +63,6 @@ char serviceDescription[] = "Diablo 2 DataBase Server";
|
|||
|
||||
int g_ServiceStatus = -1;
|
||||
|
||||
static int init(void);
|
||||
static int cleanup(void);
|
||||
static int config_init(int argc, char * * argv);
|
||||
static int config_cleanup(void);
|
||||
static int setup_daemon(void);
|
||||
|
@ -137,16 +134,6 @@ static char * write_to_pidfile(void)
|
|||
return pidfile;
|
||||
}
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cleanup(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_init(int argc, char * * argv)
|
||||
{
|
||||
char const * levels;
|
||||
|
@ -221,8 +208,9 @@ static int config_cleanup(void)
|
|||
{
|
||||
d2dbs_prefs_unload();
|
||||
cmdline_unload();
|
||||
#ifndef WIN32_GUI
|
||||
eventlog_close();
|
||||
if (eventlog_fp) std::fclose(eventlog_fp);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -230,11 +218,11 @@ static int config_cleanup(void)
|
|||
#ifdef WIN32_GUI
|
||||
extern int app_main(int argc, char ** argv)
|
||||
#else
|
||||
extern int main(int argc, char ** argv)
|
||||
extern int main(int argc, char** argv)
|
||||
#endif
|
||||
{
|
||||
int pid;
|
||||
char * pidfile;
|
||||
char* pidfile;
|
||||
|
||||
#ifdef WIN32
|
||||
// create a dump file whenever the gateway crashes
|
||||
|
@ -243,29 +231,36 @@ extern int main(int argc, char ** argv)
|
|||
|
||||
eventlog_set(stderr);
|
||||
pid = config_init(argc, argv);
|
||||
if (!(pid == 0)) {
|
||||
if (!(pid == 0))
|
||||
{
|
||||
// if (pid==1) pid=0;
|
||||
return pid;
|
||||
}
|
||||
pidfile = write_to_pidfile();
|
||||
eventlog(eventlog_level_info, __FUNCTION__, D2DBS_VERSION);
|
||||
if (init() < 0) {
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to init");
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "server initialized");
|
||||
}
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
d2dbs_handle_signal_init();
|
||||
#endif
|
||||
dbs_server_main();
|
||||
cleanup();
|
||||
|
||||
int startup_status = pre_server_startup();
|
||||
|
||||
if (startup_status == 0)
|
||||
{
|
||||
if (!server_process())
|
||||
{
|
||||
eventlog(eventlog_level_fatal, __FUNCTION__, "failed to initialize network (exiting)");
|
||||
}
|
||||
}
|
||||
|
||||
post_server_shutdown(startup_status);
|
||||
|
||||
if (pidfile) {
|
||||
if (std::remove(pidfile) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not remove pid file \"{}\" (std::remove: {})", pidfile, std::strerror(errno));
|
||||
xfree((void *)pidfile); /* avoid warning */
|
||||
}
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "server has shut down");
|
||||
config_cleanup();
|
||||
return 0;
|
||||
}
|
||||
}
|
75
src/d2dbs/pgsid.cpp
Normal file
75
src/d2dbs/pgsid.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 "common/setup_before.h"
|
||||
#include "setup.h"
|
||||
#include "pgsid.h"
|
||||
|
||||
#include "common/xalloc.h"
|
||||
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
static int dbs_packet_gs_id = 0;
|
||||
static t_preset_d2gsid* preset_d2gsid_head = nullptr;
|
||||
|
||||
|
||||
unsigned int pgsid_get_id(unsigned int ipaddr)
|
||||
{
|
||||
t_preset_d2gsid* pgsid;
|
||||
|
||||
pgsid = preset_d2gsid_head;
|
||||
while (pgsid)
|
||||
{
|
||||
if (pgsid->ipaddr == ipaddr)
|
||||
return pgsid->d2gsid;
|
||||
pgsid = pgsid->next;
|
||||
}
|
||||
|
||||
// not found, build a new item
|
||||
pgsid = (t_preset_d2gsid*)xmalloc(sizeof(t_preset_d2gsid));
|
||||
pgsid->ipaddr = ipaddr;
|
||||
pgsid->d2gsid = ++dbs_packet_gs_id;
|
||||
|
||||
// add to list
|
||||
pgsid->next = preset_d2gsid_head;
|
||||
preset_d2gsid_head = pgsid;
|
||||
return preset_d2gsid_head->d2gsid;
|
||||
}
|
||||
|
||||
void pgsid_destroy()
|
||||
{
|
||||
if (preset_d2gsid_head)
|
||||
{
|
||||
t_preset_d2gsid* curr;
|
||||
t_preset_d2gsid* next;
|
||||
|
||||
for (curr = preset_d2gsid_head; curr; curr = next)
|
||||
{
|
||||
next = curr->next;
|
||||
xfree(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
41
src/d2dbs/pgsid.h
Normal file
41
src/d2dbs/pgsid.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INCLUDED_PGSID_H
|
||||
#define INCLUDED_PGSID_H
|
||||
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace d2dbs
|
||||
{
|
||||
|
||||
typedef struct raw_preset_d2gsid
|
||||
{
|
||||
unsigned int ipaddr;
|
||||
unsigned int d2gsid;
|
||||
struct raw_preset_d2gsid* next;
|
||||
} t_preset_d2gsid;
|
||||
|
||||
|
||||
unsigned int pgsid_get_id(unsigned int ipaddr);
|
||||
void pgsid_destroy();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -38,6 +38,8 @@ constexpr long kBufferSize = 1024L * 20L;
|
|||
#ifndef D2DBS_DEFAULT_CONF_FILE
|
||||
# define D2DBS_DEFAULT_CONF_FILE "conf/d2dbs.conf"
|
||||
#endif
|
||||
#define D2DBS_POLL_INTERVAL 20 /* 20 ms */
|
||||
#define D2DBS_FDWATCH_MAX_CONNECTIONS 512
|
||||
#define DEFAULT_MEMLOG_FILE "/tmp/d2dbs-mem.std::log"
|
||||
#define DEFAULT_LISTEN_PORT 6114
|
||||
#define D2DBS_SERVER_ADDRS "0.0.0.0"
|
||||
|
|
Loading…
Add table
Reference in a new issue