From 662d78b9a5bebdb98dc178ebd1f8e3384793c104 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 17 Mar 2019 15:30:43 +0200 Subject: Initial commit --- src/game.c | 629 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 src/game.c (limited to 'src/game.c') diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..2337497 --- /dev/null +++ b/src/game.c @@ -0,0 +1,629 @@ +#include "packet.h" +#include "db.h" +#include "game.h" +#include +#include +#include +#include +#include + +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define CENTER(x) (x[0][0] + (x[1][0] - x[0][0]) / 2), (x[0][1] + (x[1][1] - x[0][1]) / 2), (x[0][2] + (x[1][2] - x[0][2]) / 2) + +typedef nfds_t client_id; + +static struct { + struct { + char name[13]; + float pos[3]; + uint32_t id, zone; + uint8_t model, evolution, lvl; + } character[1024]; + uint32_t session_id[1024]; + struct pollfd pfds[1024]; + nfds_t npfds; + struct { struct db login; } db; +} server; + +enum { + FLUSH_TO = 1<<0, + FLUSH_ZONE = 1<<1, + FLUSH_ALL = 1<<2, + FLUSH_EXCLUDE_SENDER = 1<<3, +}; + +union flush_arg { + client_id to; + uint32_t zone; +}; + +static void +flush_packet(client_id sender, union packet *packet, uint32_t filter, union flush_arg arg) +{ + packet_crypt(packet); + + if (filter & FLUSH_TO) { + (void) !write(server.pfds[arg.to].fd, packet->buf, packet->hdr.size); + } else if (filter & FLUSH_ZONE || filter & FLUSH_ALL) { + for (nfds_t i = 1; i < server.npfds; ++i) { + if ((filter & FLUSH_EXCLUDE_SENDER) && i == sender) + continue; + if ((filter & FLUSH_ZONE) && server.character[sender].zone != server.character[i].zone) + continue; + (void) !write(server.pfds[i].fd, packet->buf, packet->hdr.size); + } + } +} + +static void +stub(client_id cid, union packet *packet, uint16_t type, uint8_t byte) +{ + packet->hdr.type = type; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + for (size_t i = 0; i < 1024; ++i) + pbuf_write(&pbuf, (uint8_t[]){byte}, 1); // unknown + pbuf_flush(&pbuf); + warnx("sending stub 0x%.4x", type); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +load_shop_banners(client_id cid, union packet *packet, const char *urls[], size_t num_urls) +{ + packet->hdr.type = PACKET_SHOP_BANNER_LOAD; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint32_t[]){num_urls}, sizeof(uint32_t)); // num banners + for (size_t i = 0; i < num_urls; ++i) { + pbuf_write_str(&pbuf, urls[i]); // url + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + } + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +load_pcs_in_zone(client_id cid, union packet *packet, uint32_t zone) +{ + packet->hdr.type = PACKET_OTHER_PC_INFOS; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint16_t[]){0x00}, sizeof(uint16_t)); // num pcs (placeholder); + + for (uint16_t i = 1; i < server.npfds; ++i) { + if (i == cid || server.character[cid].zone != zone) + continue; + + pbuf_write(&pbuf, &server.character[i].id, sizeof(uint32_t)); // charid + pbuf_write_str_utf16(&pbuf, server.character[i].name); // char name UTF16 + pbuf_write(&pbuf, &server.character[i].model, 1); // char model (lily, haru, ...), 0 for no char + pbuf_write(&pbuf, &server.character[i].evolution, 1); // evolution lvl + for (size_t i = 0; i < 20; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // skin/eye/body/etc + pbuf_write(&pbuf, &server.character[i].lvl, 1); // char lvl + for (size_t i = 0; i < 452; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // fashion? + pbuf_write_str_len(&pbuf, NULL, 0); // guild name + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + pbuf_write(&pbuf, (uint32_t[]){155, 155}, sizeof(uint32_t) * 2); // hp, max hp + pbuf_write(&pbuf, (uint32_t[]){133, 133}, sizeof(uint32_t) * 2); // sf, max sf + for (size_t i = 0; i < 8; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint32_t[]){300, 400}, sizeof(uint32_t) * 2); // unknown + for (size_t i = 0; i < 10; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint32_t[]){0x42f0, 0x42d2}, sizeof(uint32_t) * 2); // movement speed? + for (size_t i = 0; i < 19; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, &zone, sizeof(zone)); // zone + for (size_t i = 0; i < 6; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, server.character[i].pos, sizeof(float) * 3); // xyz + for (size_t i = 0; i < 76; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // prologue? + pbuf_write(&pbuf, (uint8_t[]){0x01}, 1); // unknown + memcpy(packet->buf + sizeof(packet->hdr), &i, sizeof(uint16_t)); + } + + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +send_pc_info(client_id cid, union packet *packet, bool to_self) +{ + packet->hdr.type = (to_self ? PACKET_CHARACTER_INFO_RES : PACKET_IN_PC_INFO); + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, &server.character[cid].id, sizeof(uint32_t)); // charid + pbuf_write_str_utf16(&pbuf, server.character[cid].name); // char name UTF16 + pbuf_write(&pbuf, &server.character[cid].model, 1); // char model (lily, haru, ...), 0 for no char + pbuf_write(&pbuf, &server.character[cid].evolution, 1); // evolution lvl + for (size_t i = 0; i < 20; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // skin/eye/body/etc + pbuf_write(&pbuf, &server.character[cid].lvl, 1); // char lvl + for (size_t i = 0; i < 452; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // fashion? + pbuf_write_str_len(&pbuf, NULL, 0); // guild name + pbuf_write(&pbuf, (uint32_t[]){0x00}, sizeof(uint32_t)); // unknown + pbuf_write(&pbuf, (uint32_t[]){155, 155}, sizeof(uint32_t) * 2); // hp, max hp + pbuf_write(&pbuf, (uint32_t[]){133, 133}, sizeof(uint32_t) * 2); // sf, max sf + for (size_t i = 0; i < 8; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint32_t[]){300, 400}, sizeof(uint32_t) * 2); // unknown + for (size_t i = 0; i < 10; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint32_t[]){0x42f0, 0x42d2}, sizeof(uint32_t) * 2); // movement speed? + for (size_t i = 0; i < 19; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, &server.character[cid].zone, sizeof(uint32_t)); // zone + for (size_t i = 0; i < 6; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, server.character[cid].pos, sizeof(float) * 3); // xyz + for (size_t i = 0; i < 76; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // prologue? + pbuf_write(&pbuf, (uint8_t[]){0x01}, 1); // unknown + pbuf_flush(&pbuf); + flush_packet(cid, packet, (to_self ? FLUSH_TO : FLUSH_ZONE | FLUSH_EXCLUDE_SENDER), (union flush_arg){ .to = cid }); +} + +static void +send_despawn(client_id cid, union packet *packet) +{ + packet->hdr.type = PACKET_OUT_INFO_PC; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, &server.character[cid].id, sizeof(uint32_t)); // charid + pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_ZONE | FLUSH_EXCLUDE_SENDER, (union flush_arg){0}); +} + +static void +spawn_to(client_id cid, union packet *packet, uint32_t zone, float x, float y, float z) +{ + warnx("spawning to: 0x%.4x", zone); + + send_despawn(cid, packet); + server.character[cid].zone = zone; + server.character[cid].pos[0] = x; + server.character[cid].pos[1] = y; + server.character[cid].pos[2] = z; + send_pc_info(cid, packet, true); + + packet->hdr.type = PACKET_SKILLS; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + for (size_t i = 0; i < 232; ++i) pbuf_write(&pbuf, (uint8_t[]){0xff}, 1); // unknown + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); + +} + +static void +zone_to(client_id cid, union packet *packet, uint32_t zone, float x, float y, float z) +{ + warnx("zoning to: 0x%.4x", zone); + + packet->hdr.type = PACKET_GAME_WORLD_ENTER_RES; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, &server.character[cid].id, sizeof(uint32_t)); // charid + for (size_t i = 0; i < 20; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, &zone, sizeof(zone)); // zone + for (size_t i = 0; i < 8; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + + const char *host = getenv("TCPREMOTEIP"); + if (!host || strcmp(host, "127.0.0.1")) { + host = getenv("GAME_SERVER"); + host = (host ? host : "84.248.5.167"); + } + + pbuf_write_str(&pbuf, host); // server ip + pbuf_write(&pbuf, (uint16_t[]){10100}, sizeof(uint16_t)); // port + pbuf_write(&pbuf, (uint8_t[]){0xff, 0xff}, 2); // unknown + for (size_t i = 0; i < 8; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (float[]){x, y, z}, sizeof(float) * 3); // xyz + for (size_t i = 0; i < 17; ++i) pbuf_write(&pbuf, (uint8_t[]){0x00}, 1); // unknown + pbuf_write(&pbuf, (uint8_t[]){0x01}, sizeof(uint8_t)); // unknown + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); + + send_despawn(cid, packet); + server.character[cid].zone = zone; + server.character[cid].pos[0] = x; + server.character[cid].pos[1] = y; + server.character[cid].pos[2] = z; +} + +static void +send_channel_info(client_id cid, union packet *packet) +{ + packet->hdr.type = PACKET_CHANNEL_INFO; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint16_t[]){server.character[cid].zone}, sizeof(uint16_t)); // zone + + uint8_t num_channels = 1; + pbuf_write(&pbuf, &num_channels, sizeof(uint8_t)); // number of channels + for (uint16_t i = 1; i <= num_channels; ++i) { + pbuf_write(&pbuf, &i, sizeof(uint16_t)); // channel index (dunno why 16bit) + pbuf_write(&pbuf, (uint8_t[]){0}, 1); // bar (0..3) + } + + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +handle_character_action_req(client_id cid, union packet *packet) +{ + packet->hdr.type = PACKET_CHARACTER_ACTION_RES; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; +#if 0 + const uint8_t buf[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x65, + 0xeb, 0xa9, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x03, 0xdc, 0x19, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xdc, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + pbuf_write(&pbuf, buf, sizeof(buf)); +#endif + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +handle_shop_cash_load(client_id cid, union packet *packet) +{ + packet->hdr.type = PACKET_SHOP_CASH_LOAD; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint8_t[]){0x01, 0x00, 0x00, 0x00, 0x00}, 5); // unknown + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +handle_event_use_coupon_code(client_id cid, union packet *packet) +{ + // string +} + +static void +handle_maze_create_req(client_id cid, union packet *packet) +{ + uint32_t zone; + memcpy(&zone, packet->buf + 20, sizeof(zone)); + + for (size_t i = 0; i < ARRAY_SIZE(ZONES); ++i) { + if (ZONES[i].id != zone) + continue; + + union packet packet = { .hdr.salt = 2 }; + zone_to(cid, &packet, ZONES[i].id, CENTER(ZONES[i].box)); + break; + } +} + +static void +handle_character_info_req(client_id cid, union packet *packet) +{ + spawn_to(cid, packet, 10003, 10230, 10058, 90); +} + +static void +load_cash_shop(client_id cid, union packet *packet) +{ + load_shop_banners(cid, packet, (const char*[]){"https://cloudef.pw/armpit/sw-encryption.png"}, 1); + handle_shop_cash_load(cid, packet); + + packet->hdr.type = PACKET_SHOP_CASH_TAB_LOAD; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint8_t[]){ + 0x07, 0x00, 0x00, 0x00, 0xf2, 0x03, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0xf3, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0xfd, 0x03, 0x00, 0x00, 0x01, 0x00, 0xfe, 0x03, 0x00, 0x00, 0x02, 0x00, + 0xff, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x05, 0x00, 0x02, 0x04, 0x00, 0x00, 0x06, 0x00, + 0x03, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x04, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x07, 0x04, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x12, 0x04, 0x00, 0x00, 0x02, 0x00, 0x13, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x14, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x04, 0x00, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x1b, 0x04, 0x00, 0x00, 0x01, 0x00, + 0x1c, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x04, 0x00, 0x00, 0x06, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x25, 0x04, 0x00, 0x00, 0x01, 0x00, 0x26, 0x04, + 0x00, 0x00, 0x02, 0x00, 0x27, 0x04, 0x00, 0x00, 0x03, 0x00, 0x28, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x04, 0x00, 0x00, 0x07, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x2f, 0x04, 0x00, 0x00, 0x01, 0x00, 0x30, 0x04, 0x00, 0x00, + 0x02, 0x00, 0x31, 0x04, 0x00, 0x00, 0x03, 0x00, 0x32, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x33, 0x04, 0x00, 0x00, 0x05, 0x00, 0x34, 0x04, 0x00, 0x00, + 0x06, 0x00, 0x35, 0x04, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }, 494); + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); + + packet->hdr.type = PACKET_SHOP_CASH_SET_LOAD; + pbuf = (struct pbuf){ .packet = packet, .cursor = sizeof(packet->hdr) }; + for (size_t i = 0; i < 749; ++i) pbuf_write(&pbuf, (uint8_t[]){0}, 1); + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); + + packet->hdr.type = PACKET_SHOP_CASH_BUY_COUNT_LOAD; + pbuf = (struct pbuf){ .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, (uint8_t[]){0x00, 0x00, 0x00, 0x00}, 4); + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +handle_enter_gameserver_req(client_id cid, union packet *packet) +{ + { + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_read(&pbuf, &server.session_id[cid], sizeof(uint32_t)); + } + + struct db_query_arg args[] = {{ .type = DB_ARG_I32, .u.i32 = server.session_id[cid] }}, cols[5]; + const int results = db_query_single(&server.db.login, + "select id, model, name, evolution, lvl from characters where id = (select char_id from sessions where id = ? limit 1) order by id", + args, ARRAY_SIZE(args), cols, ARRAY_SIZE(cols)); + + assert(results == ARRAY_SIZE(cols)); + assert(cols[0].type == DB_ARG_I32 && cols[1].type == DB_ARG_I32 && cols[3].type == DB_ARG_I32 && cols[4].type == DB_ARG_I32); + assert(cols[2].type == DB_ARG_UTF8 && (size_t)cols[2].u.blob.sz < sizeof(server.character[cid].name)); + + server.character[cid].id = cols[0].u.i32; + server.character[cid].model = cols[1].u.i32; + memcpy(server.character[cid].name, cols[2].u.blob.data, cols[2].u.blob.sz); + server.character[cid].evolution = cols[3].u.i32; + server.character[cid].lvl = cols[4].u.i32; + + // stub(cid, packet, PACKET_AUTH_UNKNOWN1, 0x01); + // stub(cid, packet, PACKET_WORLD_VERSION, 0x01); + // stub(cid, packet, PACKET_AUTH_UNKNOWN17, 0x01); + + stub(cid, packet, PACKET_ENTER_GAMESERVER_RES, 0x01); + + // stub(cid, packet, PACKET_GAME_UNKNOWN1, 0x01); + // stub(cid, packet, PACKET_GAME_UNKNOWN2, 0x01); + // stub(cid, packet, PACKET_GAME_UNKNOWN3, 0x01); + + stub(cid, packet, PACKET_CHARACTER_DB_LOAD_SYNC, 0x01); + + // load_cash_shop(cid, packet); +} + +static void +inject(client_id cid, const char *arg) +{ + warnx("injecting: %s", arg); + + FILE *f; + if (!(f = fopen(arg, "rb"))) { + warn("fopen(%s)", arg); + return; + } + + union packet packet; + (void) !fread(packet.buf, 1, sizeof(packet.buf), f); + fclose(f); + + packet_crypt(&packet); + flush_packet(cid, &packet, FLUSH_TO, (union flush_arg){ .to = cid }); +} + +static void +zone(client_id cid, const char *arg) +{ + warnx("zone: %s", arg); + + for (size_t i = 0; i < ARRAY_SIZE(ZONES); ++i) { + if (strcmp(ZONES[i].name, arg)) + continue; + + union packet packet = { .hdr.salt = 2 }; + zone_to(cid, &packet, ZONES[i].id, CENTER(ZONES[i].box)); + break; + } +} + +static char *const *ARGV; + +static void +rexec(void) +{ + warnx("running exec"); + execv(ARGV[0], ARGV); +} + +static void +handle_chat_normal(client_id cid, union packet *packet) +{ + uint8_t unknown; + struct pbuf_string msg; + { + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_read(&pbuf, &unknown, sizeof(unknown)); // unknown (type of msg?) + pbuf_read_str_utf16(&pbuf, &msg); + } + + const struct { + const char *cmd; + void (*action)(); + } map[] = { + { .cmd = "ex", .action = rexec }, + { .cmd = "inject", .action = inject }, + { .cmd = "zone", .action = zone }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(map); ++i) { + if (strncmp(msg.data, map[i].cmd, strlen(map[i].cmd))) + continue; + + map[i].action(cid, msg.data + strlen(map[i].cmd) + 1); + return; + } + + packet->hdr.type = PACKET_CHAT_NORMAL; + struct pbuf pbuf = { .packet = packet, .cursor = sizeof(packet->hdr) }; + pbuf_write(&pbuf, &server.character[cid].id, sizeof(uint32_t)); // charid + pbuf_write(&pbuf, &unknown, 1); // msg type? + pbuf_write(&pbuf, (uint8_t[]){0, 0, 0}, 3); // unknown + pbuf_write_str_len_utf16(&pbuf, msg.data, msg.len); // msg text UTF16 + pbuf_write(&pbuf, (uint8_t[]){0}, 1); // null terminate + pbuf_flush(&pbuf); + flush_packet(cid, packet, FLUSH_ALL, (union flush_arg){0}); +} + +static void +handle_packet(client_id cid, union packet *packet) +{ + packet_crypt(packet); + warnx("got packet of type 0x%.4x", packet->hdr.type); + + switch (packet->hdr.type) { + case PACKET_ENTER_GAMESERVER_REQ: + handle_enter_gameserver_req(cid, packet); + break; + case PACKET_CHARACTER_INFO_REQ: + handle_character_info_req(cid, packet); + break; + case PACKET_CHANNEL_INFO: + send_channel_info(cid, packet); + /* fallthrough */ + case PACKET_COMPLETE_MAZE_REQ: + load_pcs_in_zone(cid, packet, server.character[cid].zone); + send_pc_info(cid, packet, false); + break; + case PACKET_CHARACTER_MOVE: + packet->hdr.type = PACKET_CHARACTER_MOVE_RES; + flush_packet(cid, packet, FLUSH_ZONE | FLUSH_EXCLUDE_SENDER, (union flush_arg){0}); + break; + case PACKET_CHARACTER_STOP_MOVE: + packet->hdr.type = PACKET_CHARACTER_STOP_MOVE_RES; + flush_packet(cid, packet, FLUSH_ZONE | FLUSH_EXCLUDE_SENDER, (union flush_arg){0}); + break; + case PACKET_CHARACTER_JUMP: + packet->hdr.type = PACKET_CHARACTER_JUMP_RES; + flush_packet(cid, packet, FLUSH_ZONE | FLUSH_EXCLUDE_SENDER, (union flush_arg){0}); + break; + case PACKET_CHAT_NORMAL: + handle_chat_normal(cid, packet); + break; + case PACKET_MAZE_CREATE_REQ: + handle_maze_create_req(cid, packet); + break; + case PACKET_EVENT_USE_COUPON_CODE: + handle_event_use_coupon_code(cid, packet); + break; + case PACKET_SHOP_CASH_LOAD: + handle_shop_cash_load(cid, packet); + break; + case PACKET_CHARACTER_ACTION_REQ: + handle_character_action_req(cid, packet); + break; + default: + warnx("unknown packet"); + break; + } +} + +#include +#include +#include +#include + +static inline size_t +safe_read(int fd, void *dst, const size_t dst_size) +{ + size_t off = 0; + ssize_t ret = 0; + for (; off < dst_size && (ret = read(fd, (char*)dst + off, dst_size - off)) > 0; off += ret); + return (ret < 0 ? 0 : off); +} + +int +main(int argc, char *const argv[]) +{ + (void)argc; + ARGV = argv; + + db_init(&server.db.login, "login.db"); + + int sfd; + if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) + err(EXIT_FAILURE, "socket"); + + struct sockaddr_in serv_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(10100), + }; + + setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (int[]){true}, sizeof(int)); + + if (bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) + err(EXIT_FAILURE, "bind"); + + if (listen(sfd, 0) != 0) + err(EXIT_FAILURE, "listen"); + + union packet packet; + server.pfds[0] = (struct pollfd){ .fd = sfd, .events = POLLIN }; + server.npfds = 1; + while (poll(server.pfds, server.npfds, -1) > 0) { + if (server.pfds[0].revents & POLLIN) { + int cfd; + if ((cfd = accept(sfd, (struct sockaddr*)NULL, NULL)) == -1) { + warnx("failed to accept"); + continue; + } + + if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (int[]){true}, sizeof(int)) != 0) + err(EXIT_FAILURE, "setsockopt"); + + if (fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL, 0) | O_NONBLOCK) == -1) + err(EXIT_FAILURE, "failed to set O_NONBLOCK"); + + server.pfds[server.npfds].fd = cfd; + server.pfds[server.npfds++].events = POLLIN; + warnx("new client connected"); + } + + for (nfds_t i = 1; i < server.npfds; ++i) { + if (!(server.pfds[i].revents & POLLIN)) + continue; + + if (safe_read(server.pfds[i].fd, packet.buf, sizeof(packet.hdr)) != sizeof(packet.hdr) || !packet_verify(&packet)) { + warnx("invalid packet"); + close(server.pfds[i].fd); + memmove(&server.pfds[i], &server.pfds[i + 1], sizeof(struct pollfd) * server.npfds - i); + memmove(&server.session_id[i], &server.session_id[i + 1], sizeof(uint32_t) * server.npfds - i); + memmove(&server.character[i], &server.character[i + 1], sizeof(*server.character) * server.npfds - i); + --server.npfds; + continue; + } + + safe_read(server.pfds[i].fd, packet.buf + sizeof(packet.hdr), packet.hdr.size - sizeof(packet.hdr)); + handle_packet(i, &packet); + } + + db_gc(&server.db.login); + } + + db_release(&server.db.login); + return EXIT_SUCCESS; +} -- cgit v1.2.3