make some progress toward getting psogc to work again

This commit is contained in:
Martin Michelsen
2018-11-10 23:52:23 -08:00
parent 00bbd6a0a3
commit 833bf90333
20 changed files with 286 additions and 124 deletions
+5 -2
View File
@@ -5,6 +5,7 @@
#include <poll.h>
#include <netinet/in.h>
#include <phosg/Encoding.hh>
#include <phosg/Network.hh>
#include <phosg/Strings.hh>
#include <vector>
@@ -93,8 +94,9 @@ void DNSServer::run_thread() {
if (bytes > 0) {
input.resize(bytes);
uint32_t remote_address = bswap32(remote.sin_addr.s_addr);
uint32_t connect_address;
if (is_local_address(remote.sin_addr.s_addr)) {
if (is_local_address(remote_address)) {
connect_address = this->local_connect_address;
} else {
connect_address = this->external_connect_address;
@@ -121,11 +123,12 @@ string DNSServer::build_response(const std::string& input,
string ret;
size_t name_len = strlen(input.data() + 0x0C) + 1;
uint32_t connect_address_be = bswap32(connect_address);
ret.append(input.substr(0, 2));
ret.append("\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00", 10);
ret.append(input.substr(12, name_len));
ret.append("\x00\x01\x00\x01\xC0\x0C\x00\x01\x00\x01\x00\x00\x00\x3C\x00\x04", 16);
ret.append(reinterpret_cast<const char*>(&connect_address), 4);
ret.append(reinterpret_cast<const char*>(&connect_address_be), 4);
return ret;
}
+1 -1
View File
@@ -34,7 +34,7 @@ struct License {
char gc_password[12]; // GC password
uint32_t privileges; // privilege level
uint64_t ban_end_time; // end time of ban (zero = not banned)
};
} __attribute__((packed));
class LicenseManager {
public:
+2 -4
View File
@@ -35,7 +35,7 @@ void Lobby::reassign_leader_on_client_departure_locked(size_t leaving_client_ind
return;
}
}
throw out_of_range("no clients remain");
this->leader_id = 0;
}
bool Lobby::any_client_loading() const {
@@ -68,11 +68,11 @@ void Lobby::add_client(shared_ptr<Client> c) {
}
void Lobby::add_client_locked(shared_ptr<Client> c) {
rw_guard g(this->lock, true);
size_t index;
for (index = 0; index < this->max_clients; index++) {
if (!this->clients[index].get()) {
this->clients[index] = c;
break;
}
}
if (index >= this->max_clients) {
@@ -101,8 +101,6 @@ void Lobby::remove_client(shared_ptr<Client> c) {
}
void Lobby::remove_client_locked(shared_ptr<Client> c) {
rw_guard g(this->lock, true);
if (this->clients[c->lobby_client_id] != c) {
throw logic_error("client\'s lobby client id does not match client list");
}
+46 -34
View File
@@ -1,12 +1,15 @@
#include <arpa/inet.h>
#include <signal.h>
#include <event2/event.h>
#include <event2/thread.h>
#include <unordered_map>
#include <phosg/JSON.hh>
#include <phosg/Strings.hh>
#include <phosg/Filesystem.hh>
#include <set>
#include "NetworkAddresses.hh"
#include "SendCommands.hh"
#include "DNSServer.hh"
#include "ServerState.hh"
#include "Server.hh"
@@ -61,7 +64,7 @@ void populate_state_from_config(shared_ptr<ServerState> s,
shared_ptr<JSONObject> config_json) {
const auto& d = config_json->as_dict();
s->name = d.at("ServerName")->as_string();
s->name = decode_sjis(d.at("ServerName")->as_string());
// TODO: make this configurable
s->port_configuration = default_port_to_behavior;
@@ -76,45 +79,50 @@ void populate_state_from_config(shared_ptr<ServerState> s,
box_categories, unit_types));
shared_ptr<vector<MenuItem>> information_menu(new vector<MenuItem>());
shared_ptr<unordered_map<uint32_t, u16string>> id_to_information_contents(
new unordered_map<uint32_t, u16string>());
shared_ptr<vector<u16string>> information_contents(new vector<u16string>());
uint32_t item_id = 1;
information_menu->emplace_back(INFORMATION_MENU_GO_BACK, u"Go back",
u"Return to the\nmain menu", 0);
uint32_t item_id = 0;
for (const auto& item : d.at("InformationMenuContents")->as_list()) {
auto& v = item->as_list();
information_menu->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
decode_sjis(v.at(1)->as_string()), MenuItemFlag::RequiresMessageBoxes);
id_to_information_contents->emplace(item_id, decode_sjis(v.at(2)->as_string()));
information_contents->emplace_back(decode_sjis(v.at(2)->as_string()));
item_id++;
}
s->information_menu = information_menu;
s->id_to_information_contents = id_to_information_contents;
s->information_contents = information_contents;
s->num_threads = d.at("Threads")->as_int();
auto local_address_str = d.at("LocalAddress")->as_string();
uint32_t local_address = inet_addr(local_address_str.c_str());
if (s->all_addresses.emplace(local_address).second) {
log(INFO, "added local address: %hhu.%hhu.%hhu.%hhu",
static_cast<uint8_t>(local_address >> 24), static_cast<uint8_t>(local_address >> 16),
static_cast<uint8_t>(local_address >> 8), static_cast<uint8_t>(local_address));
}
s->local_address = address_for_string(local_address_str.c_str());
s->all_addresses.emplace(s->local_address);
log(INFO, "added local address: %s", local_address_str.c_str());
auto external_address_str = d.at("LocalAddress")->as_string();
uint32_t external_address = inet_addr(external_address_str.c_str());
if (s->all_addresses.emplace(external_address).second) {
log(INFO, "added external address: %hhu.%hhu.%hhu.%hhu",
static_cast<uint8_t>(external_address >> 24), static_cast<uint8_t>(external_address >> 16),
static_cast<uint8_t>(external_address >> 8), static_cast<uint8_t>(external_address));
auto external_address_str = d.at("ExternalAddress")->as_string();
s->external_address = address_for_string(external_address_str.c_str());
s->all_addresses.emplace(s->external_address);
log(INFO, "added external address: %s", external_address_str.c_str());
try {
s->run_dns_server = d.at("RunDNSServer")->as_bool();
} catch (const JSONObject::key_error&) {
s->run_dns_server = true;
}
}
int main(int argc,char* argv[]) {
int main(int argc, char* argv[]) {
log(INFO, "fuzziqer software newserv");
signal(SIGPIPE, SIG_IGN);
if (evthread_use_pthreads()) {
log(ERROR, "cannot enable multithreading in libevent");
}
log(INFO, "creating server state");
shared_ptr<ServerState> state(new ServerState());
@@ -122,9 +130,8 @@ int main(int argc,char* argv[]) {
log(INFO, "reading network addresses");
state->all_addresses = get_local_address_list();
for (uint32_t addr : state->all_addresses) {
log(INFO, "found address: %hhu.%hhu.%hhu.%hhu",
static_cast<uint8_t>(addr >> 24), static_cast<uint8_t>(addr >> 16),
static_cast<uint8_t>(addr >> 8), static_cast<uint8_t>(addr));
string addr_str = string_for_address(addr);
log(INFO, "found address: %s", addr_str.c_str());
}
log(INFO, "loading configuration");
@@ -143,15 +150,20 @@ int main(int argc,char* argv[]) {
log(INFO, "collecting quest metadata");
state->quest_index.reset(new QuestIndex("system/quests"));
log(INFO, "starting dns server");
DNSServer dns_server(state->local_address, state->external_address);
// TODO: call dns_server.listen appropriately
dns_server.start();
shared_ptr<DNSServer> dns_server;
if (state->run_dns_server) {
log(INFO, "starting dns server on port 53");
dns_server.reset(new DNSServer(state->local_address, state->external_address));
dns_server->listen("", 53);
dns_server->start();
}
log(INFO, "starting game server");
Server game_server(state);
// TODO: call game_server.listen appropriately
game_server.start();
shared_ptr<Server> game_server(new Server(state));
for (const auto& it : state->port_configuration) {
game_server->listen("", it.second.port, it.second.version, it.second.behavior);
}
game_server->start();
for (;;) {
sigset_t s;
@@ -160,10 +172,10 @@ int main(int argc,char* argv[]) {
}
log(INFO, "waiting for servers to terminate");
dns_server.schedule_stop();
game_server.schedule_stop();
dns_server.wait_for_stop();
game_server.wait_for_stop();
dns_server->schedule_stop();
game_server->schedule_stop();
dns_server->wait_for_stop();
game_server->wait_for_stop();
return 0;
}
+1 -1
View File
@@ -5,7 +5,7 @@ OBJECTS=FileContentsCache.o Menu.o PSOProtocol.o Client.o Lobby.o \
Text.o DNSServer.o Main.o
CXX=g++
CXXFLAGS=-I/opt/local/include -I/usr/local/include -std=c++14 -g -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H -Wall -Werror
LDFLAGS=-L/opt/local/lib -L/usr/local/lib -std=c++14 -levent -lphosg -lpthread
LDFLAGS=-L/opt/local/lib -L/usr/local/lib -std=c++14 -levent -levent_pthreads -lphosg -lpthread
EXECUTABLE=newserv
all: $(EXECUTABLE)
+20 -2
View File
@@ -41,7 +41,7 @@ uint32_t resolve_address(const char* address) {
}
struct sockaddr_in* res_sin = (struct sockaddr_in*)res4->ai_addr;
return res_sin->sin_addr.s_addr;
return bswap32(res_sin->sin_addr.s_addr);
}
set<uint32_t> get_local_address_list() {
@@ -71,9 +71,27 @@ set<uint32_t> get_local_address_list() {
}
bool is_local_address(uint32_t addr) {
uint8_t net = addr & 0xFF;
uint8_t net = (addr >> 24) & 0xFF;
if ((net != 127) && (net != 172) && (net != 10) && (net != 192)) {
return false;
}
return true;
}
bool is_local_address(const sockaddr_storage& daddr) {
if (daddr.ss_family != AF_INET) {
return false;
}
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&daddr);
return is_local_address(bswap32(sin->sin_addr.s_addr));
}
string string_for_address(uint32_t address) {
return string_printf("%hhu.%hhu.%hhu.%hhu",
static_cast<uint8_t>(address >> 24), static_cast<uint8_t>(address >> 16),
static_cast<uint8_t>(address >> 8), static_cast<uint8_t>(address));
}
uint32_t address_for_string(const char* address) {
return bswap32(inet_addr(address));
}
+6
View File
@@ -1,8 +1,10 @@
#pragma once
#include <netinet/in.h>
#include <stdint.h>
#include <set>
#include <string>
@@ -13,3 +15,7 @@ uint32_t resolve_address(const char* address);
std::set<uint32_t> get_local_address_list();
uint32_t get_connected_address(int fd);
bool is_local_address(uint32_t daddr);
bool is_local_address(const sockaddr_storage& daddr);
std::string string_for_address(uint32_t address);
uint32_t address_for_string(const char* address);
+5 -8
View File
@@ -130,25 +130,22 @@ PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
basekey = basekey & 0x7FFFFFFF;
}
}
this->stream[this->offset] = basekey;
this->stream[this->offset++] = basekey;
}
this->stream[this->offset] = (((this->stream[0] >> 9) ^ (this->stream[this->offset] << 23)) ^ this->stream[15]);
this->stream[this->offset - 1] = (((this->stream[0] >> 9) ^ (this->stream[this->offset - 1] << 23)) ^ this->stream[15]);
source1 = 0;
source2 = 1;
source3 = this->offset - 1;
while (this->offset != GC_STREAM_LENGTH) {
this->stream[this->offset] = (this->stream[source3] ^ (((this->stream[source1] << 23) & 0xFF800000) ^ ((this->stream[source2] >> 9) & 0x007FFFFF)));
this->offset++;
source1++;
source2++;
source3++;
this->stream[this->offset++] = (this->stream[source3++] ^ (((this->stream[source1++] << 23) & 0xFF800000) ^ ((this->stream[source2++] >> 9) & 0x007FFFFF)));
}
for (size_t x = 0; x < 4; x++) {
for (size_t x = 0; x < 3; x++) {
this->update_stream();
}
this->offset = GC_STREAM_LENGTH - 1;
}
void PSOGCEncryption::encrypt(void* vdata, size_t size) {
+4 -1
View File
@@ -308,7 +308,10 @@ shared_ptr<const string> Quest::dat_contents() const {
QuestIndex::QuestIndex(const char* directory) : directory(directory) {
for (const auto& filename : list_directory(this->directory)) {
auto filename_set = list_directory(this->directory);
vector<string> filenames(filename_set.begin(), filename_set.end());
sort(filenames.begin(), filenames.end());
for (const auto& filename : filenames) {
string full_path = this->directory + "/" + filename;
if (ends_with(filename, ".gba")) {
+88 -28
View File
@@ -43,9 +43,10 @@ void process_connect(std::shared_ptr<ServerState> s, std::shared_ptr<Client> c)
switch (c->server_behavior) {
case ServerBehavior::SplitReconnect: {
uint16_t pc_port = s->port_configuration.at("pc-login").port;
send_pc_gc_split_reconnect(c, 0, pc_port);
c->server_behavior = ServerBehavior::LoginServer;
// intentional fallthrough
uint16_t gc_port = s->port_configuration.at("gc-jp10").port;
send_pc_gc_split_reconnect(c, s->connect_address_for_client(c), pc_port, gc_port);
c->should_disconnect = true;
break;
}
case ServerBehavior::LoginServer:
@@ -68,7 +69,7 @@ void process_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
send_ep3_rank_update(c);
}
send_menu(c, u"Main menu", MAIN_MENU_ID, s->main_menu, false);
send_menu(c, s->name.c_str(), MAIN_MENU_ID, s->main_menu, false);
} else if (c->server_behavior == ServerBehavior::LobbyServer) {
@@ -146,6 +147,7 @@ void process_verify_license_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint32_t sub_version;
char unused3[0x60];
char password[0x10];
char unused4[0x20];
};
check_size(size, sizeof(Cmd));
const auto* cmd = reinterpret_cast<const Cmd*>(data);
@@ -242,6 +244,7 @@ void process_login_d_e_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
char unused3[0x60];
char name[0x10];
ClientConfig cfg;
uint8_t unused4[0x64];
};
check_size(size, sizeof(Cmd));
const auto* cmd = reinterpret_cast<const Cmd*>(data);
@@ -315,7 +318,8 @@ void process_login_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
switch (c->config.cfg.bb_game_state) {
case ClientStateBB::InitialLogin:
// first login? send them to the other port
send_reconnect(c, 0, s->port_configuration.at("bb-data1").port);
send_reconnect(c, s->connect_address_for_client(c),
s->port_configuration.at("bb-data1").port);
break;
case ClientStateBB::DownloadData: {
@@ -340,13 +344,13 @@ void process_login_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
break;
default:
send_reconnect(c, 0, s->port_configuration.at("bb-login").port);
send_reconnect(c, s->connect_address_for_client(c),
s->port_configuration.at("bb-login").port);
}
}
void process_client_checksum(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // 96
check_size(size, 0);
send_command(c, 0x97, 0x01);
}
@@ -479,13 +483,61 @@ void process_message_box_closed(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (c->in_information_menu) {
// add a reference to ensure it's not destroyed by another thread
auto info_menu = s->information_menu;
send_menu(c, u"Information", INFORMATION_MENU_ID, *info_menu, true);
send_menu(c, u"Information", INFORMATION_MENU_ID, *info_menu, false);
return;
}
}
void process_menu_item_info_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // 09
struct Cmd {
uint32_t menu_id;
uint32_t item_id;
};
check_size(size, sizeof(Cmd), sizeof(Cmd));
const auto* cmd = reinterpret_cast<const Cmd*>(data);
switch (cmd->menu_id) {
case MAIN_MENU_ID:
switch (cmd->item_id) {
case MAIN_MENU_GO_TO_LOBBY:
send_ship_info(c, u"Go to the lobby.");
break;
case MAIN_MENU_INFORMATION:
send_ship_info(c, u"View server information.");
break;
case MAIN_MENU_DISCONNECT:
send_ship_info(c, u"End your session.");
break;
default:
send_ship_info(c, u"Incorrect menu item ID.");
break;
}
break;
case INFORMATION_MENU_ID:
if (cmd->item_id == INFORMATION_MENU_GO_BACK) {
send_ship_info(c, u"Return to the\nmain menu.");
} else {
try {
// add a reference to ensure it's not destroyed by another thread
// we use item_id + 1 here because "go back" is the first item
auto info_menu = s->information_menu;
send_ship_info(c, info_menu->at(cmd->item_id + 1).description.c_str());
} catch (const out_of_range&) {
send_ship_info(c, u"$C6No such information exists.");
}
}
break;
default:
send_ship_info(c, u"Incorrect menu ID.");
break;
}
}
void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // 09 10
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // 10
bool uses_unicode = ((c->version == GameVersion::PC) || (c->version == GameVersion::BB));
struct Cmd {
@@ -504,16 +556,17 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
switch (cmd->item_id) {
case MAIN_MENU_GO_TO_LOBBY: {
static const vector<string> version_to_port_name({
"dc-lobby", "dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "bb-lobby"});
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "bb-lobby"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
send_reconnect(c, 0, s->port_configuration.at(port_name).port);
send_reconnect(c, s->connect_address_for_client(c),
s->port_configuration.at(port_name).port);
break;
}
case MAIN_MENU_INFORMATION: {
auto info_menu = s->information_menu;
send_menu(c, u"Information", INFORMATION_MENU_ID, *info_menu, true);
send_menu(c, u"Information", INFORMATION_MENU_ID, *info_menu, false);
c->in_information_menu = true;
break;
}
@@ -526,17 +579,18 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_message_box(c, u"Incorrect menu item ID.");
break;
}
break;
}
case INFORMATION_MENU_ID: {
if (cmd->item_id == 0) {
if (cmd->item_id == INFORMATION_MENU_GO_BACK) {
c->in_information_menu = false;
send_menu(c, u"Main menu", MAIN_MENU_ID, s->main_menu, false);
send_menu(c, s->name.c_str(), MAIN_MENU_ID, s->main_menu, false);
} else {
try {
// add a reference to ensure it's not destroyed by another thread
auto info_menu = s->id_to_information_contents;
auto info_menu = s->information_contents;
send_message_box(c, info_menu->at(cmd->item_id).c_str());
} catch (const out_of_range&) {
send_message_box(c, u"$C6No such information exists.");
@@ -720,14 +774,14 @@ void process_game_list_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_change_ship(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // A0
check_size(size, 0);
send_message_box(c, u""); // we do this to avoid the "log window in message box" bug
static const vector<string> version_to_port_name({
"dc-login", "dc-login", "pc-login", "bb-patch", "gc-login", "bb-login"});
"dc-login", "pc-login", "bb-patch", "gc-us3", "bb-login"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
send_reconnect(c, 0, s->port_configuration.at(port_name).port);
send_reconnect(c, s->connect_address_for_client(c),
s->port_configuration.at(port_name).port);
}
void process_change_block(shared_ptr<ServerState> s, shared_ptr<Client> c,
@@ -850,18 +904,20 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) { // 61 98
{
// note: we add extra buffer on the end when checking sizes because the
// autoreply text is a variable length
rw_guard g(c->lock, true);
switch (c->version) {
case GameVersion::PC:
check_size(size, sizeof(PSOPlayerDataPC));
check_size(size, sizeof(PSOPlayerDataPC), sizeof(PSOPlayerDataPC) + 2 * 0xAC);
c->player.import(*reinterpret_cast<const PSOPlayerDataPC*>(data));
break;
case GameVersion::GC:
check_size(size, sizeof(PSOPlayerDataGC));
check_size(size, sizeof(PSOPlayerDataGC), sizeof(PSOPlayerDataGC) + 0xAC);
c->player.import(*reinterpret_cast<const PSOPlayerDataGC*>(data));
break;
case GameVersion::BB:
check_size(size, sizeof(PSOPlayerDataBB));
check_size(size, sizeof(PSOPlayerDataBB), sizeof(PSOPlayerDataBB) + 2 * 0xAC);
c->player.import(*reinterpret_cast<const PSOPlayerDataBB*>(data));
break;
default:
@@ -1609,7 +1665,7 @@ static process_command_t dc_handlers[0x100] = {
// 00
NULL, NULL, NULL, NULL,
NULL, process_ignored_command, process_chat_dc_gc, NULL,
process_game_list_request, process_menu_selection, NULL, NULL,
process_game_list_request, process_menu_item_info_request, NULL, NULL,
NULL, NULL, NULL, NULL,
// 10
@@ -1707,7 +1763,7 @@ static process_command_t pc_handlers[0x100] = {
// 00
NULL, NULL, NULL, NULL,
NULL, process_ignored_command, process_chat_pc_bb, NULL,
process_game_list_request, process_menu_selection, NULL, NULL,
process_game_list_request, process_menu_item_info_request, NULL, NULL,
NULL, NULL, NULL, NULL,
// 10
@@ -1805,7 +1861,7 @@ static process_command_t gc_handlers[0x100] = {
// 00
NULL, NULL, NULL, NULL,
NULL, process_ignored_command, process_chat_dc_gc, NULL,
process_game_list_request, process_menu_selection, NULL, NULL,
process_game_list_request, process_menu_item_info_request, NULL, NULL,
NULL, NULL, NULL, NULL,
// 10
@@ -1882,7 +1938,7 @@ static process_command_t gc_handlers[0x100] = {
// D0
NULL, NULL, NULL, NULL,
NULL, NULL, process_ignored_command, NULL,
NULL, NULL, process_message_box_closed, NULL,
process_info_board_request, process_write_info_board_dc_gc, NULL, process_verify_license_gc,
process_ep3_menu_challenge, NULL, NULL, NULL,
@@ -1903,7 +1959,7 @@ static process_command_t bb_handlers[0x100] = {
// 00
NULL, NULL, NULL, NULL,
NULL, process_ignored_command, process_chat_pc_bb, NULL,
process_game_list_request, process_menu_selection, NULL, NULL,
process_game_list_request, process_menu_item_info_request, NULL, NULL,
NULL, NULL, NULL, NULL,
// 10
@@ -2037,12 +2093,16 @@ static process_command_t patch_handlers[0x100] = {
};
static process_command_t* handlers[6] = {
dc_handlers, dc_handlers, pc_handlers, patch_handlers, gc_handlers, bb_handlers};
dc_handlers, pc_handlers, patch_handlers, gc_handlers, bb_handlers};
void process_command(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, uint16_t size, const void* data) {
log(INFO, "received version=%d size=%04hX command=%04hX flag=%08X",
static_cast<int>(c->version), size, command, flag);
print_data(stderr, data, size);
auto fn = handlers[static_cast<size_t>(c->version)][command & 0xFF];
if (!fn) {
if (fn) {
fn(s, c, command, flag, size, data);
} else {
process_unimplemented_command(s, c, command, flag, size, data);
+5 -2
View File
@@ -31,13 +31,15 @@ struct ItemSubcommand {
void check_size(uint16_t size, uint16_t min_size, uint16_t max_size) {
if (size < min_size) {
throw runtime_error("command too small");
throw runtime_error(string_printf("command too small (expected at least %zX bytes, got %zX bytes)",
min_size, size));
}
if (max_size == 0) {
max_size = min_size;
}
if (size > max_size) {
throw runtime_error("command too large");
throw runtime_error(string_printf("command too large (expected at most %zX bytes, got %zX bytes)",
max_size, size));
}
}
@@ -64,6 +66,7 @@ void forward_subcommand(shared_ptr<Lobby> l, shared_ptr<Client> c,
}
send_command(target, command, flag, p, count * 4);
} else {
// TODO: don't send the command back to the client it originated from
send_command(l, command, flag, p, count * 4);
}
}
+39 -28
View File
@@ -1,6 +1,7 @@
#include "SendCommands.hh"
#include <memory>
#include <phosg/Encoding.hh>
#include <phosg/Random.hh>
#include <phosg/Strings.hh>
#include <phosg/Time.hh>
@@ -66,6 +67,9 @@ void send_command(shared_ptr<Client> c, uint16_t command, uint32_t flag,
throw logic_error("unimplemented game version in send_command");
}
log(INFO, "sending command");
print_data(stderr, send_data.data(), send_data.size());
c->send(move(send_data));
}
@@ -121,8 +125,19 @@ static void send_server_init_dc_pc_gc(shared_ptr<Client> c, const char* copyrigh
send_command(c, command, 0x00, cmd);
rw_guard g(c->lock, true);
c->crypt_out.reset(new PSOPCEncryption(server_key));
c->crypt_in.reset(new PSOPCEncryption(client_key));
switch (c->version) {
case GameVersion::DC:
case GameVersion::PC:
c->crypt_out.reset(new PSOPCEncryption(server_key));
c->crypt_in.reset(new PSOPCEncryption(client_key));
break;
case GameVersion::GC:
c->crypt_out.reset(new PSOGCEncryption(server_key));
c->crypt_in.reset(new PSOGCEncryption(client_key));
break;
default:
throw invalid_argument("incorrect client version");
}
}
static void send_server_init_pc(shared_ptr<Client> c, bool initial_connection) {
@@ -211,28 +226,18 @@ void send_update_client_config(shared_ptr<Client> c) {
void send_reconnect(shared_ptr<Client> c, uint32_t address, uint16_t port) {
if (!address) {
const sockaddr_in* local_addr = reinterpret_cast<const sockaddr_in*>(&c->local_addr);
address = local_addr->sin_addr.s_addr;
}
struct {
uint32_t address;
uint16_t port;
uint16_t unused;
} cmd = {address, port, 0};
} cmd = {bswap32(address), port, 0};
send_command(c, 0x19, 0x00, cmd);
}
// sends the command (first used by Schthack) that separates PC and GC users
// that connect on the same port
void send_pc_gc_split_reconnect(shared_ptr<Client> c, uint32_t address,
uint16_t pc_port) {
if (!address) {
const sockaddr_in* local_addr = reinterpret_cast<const sockaddr_in*>(&c->local_addr);
address = local_addr->sin_addr.s_addr;
}
uint16_t pc_port, uint16_t gc_port) {
struct {
uint32_t pc_address;
uint16_t pc_port;
@@ -240,13 +245,17 @@ void send_pc_gc_split_reconnect(shared_ptr<Client> c, uint32_t address,
uint8_t gc_command;
uint8_t gc_flag;
uint16_t gc_size;
uint8_t unused2[0xB0 - 0x1D];
uint32_t gc_address;
uint16_t gc_port;
uint8_t unused2[0xB0 - 0x23];
} __attribute__((packed)) cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.pc_address = address;
cmd.pc_address = bswap32(address);
cmd.pc_port = pc_port;
cmd.gc_command = 0x19;
cmd.gc_size = 0x97;
cmd.gc_address = bswap32(address);
cmd.gc_port = gc_port;
send_command(c, 0x19, 0x00, cmd);
}
@@ -445,8 +454,7 @@ static void send_large_message_pc_patch_bb(shared_ptr<Client> c, uint8_t command
{0, from_serial_number};
}
data += text;
add_color_inplace(const_cast<char16_t*>(data.data()) +
(include_header ? sizeof(LargeMessageOptionalHeader) : 0));
add_color_inplace(data, (include_header ? sizeof(LargeMessageOptionalHeader) : 0));
data.resize((data.size() + 4) & ~3);
send_command(c, command, 0x00, data);
}
@@ -460,8 +468,7 @@ static void send_large_message_dc_gc(shared_ptr<Client> c, uint8_t command,
{0, from_serial_number};
}
data += encode_sjis(text);
add_color_inplace(const_cast<char*>(data.data()) +
(include_header ? sizeof(LargeMessageOptionalHeader) : 0));
add_color_inplace(data, (include_header ? sizeof(LargeMessageOptionalHeader) : 0));
data.resize((data.size() + 4) & ~3);
send_command(c, command, 0x00, data);
}
@@ -517,7 +524,7 @@ void send_chat_message(shared_ptr<Client> c, uint32_t from_serial_number,
data.append(u"\x09J");
}
data.append(from_name);
data.append(u"\x09J");
data.append(u"\x09\x09J");
data.append(text);
send_large_message(c, 0x06, data.c_str(), from_serial_number, true);
}
@@ -637,18 +644,21 @@ static void send_card_search_result_dc_pc_gc(shared_ptr<ServerState> s,
cmd.destination_command.dcgc_flag = 0x00;
cmd.destination_command.dcgc_size = 0x000C;
}
// TODO: make this actually make sense... currently we just take the sockname
// for the target client
const sockaddr_in* local_addr = reinterpret_cast<const sockaddr_in*>(&result->local_addr);
cmd.destination_command.address = local_addr->sin_addr.s_addr;
cmd.destination_command.port = ntohs(local_addr->sin_port);
cmd.destination_command.unused = 0;
auto encoded_server_name = encode_sjis(s->name);
if (result_lobby->is_game()) {
string encoded_lobby_name = encode_sjis(result_lobby->name);
snprintf(cmd.location_string, sizeof(cmd.location_string),
"%s, Block 00, ,%s", encoded_lobby_name.c_str(), s->name.c_str());
"%s, Block 00, ,%s", encoded_lobby_name.c_str(), encoded_server_name.c_str());
} else {
snprintf(cmd.location_string, sizeof(cmd.location_string), "Block 00, ,%s",
s->name.c_str());
encoded_server_name.c_str());
}
cmd.menu_id = LOBBY_MENU_ID;
cmd.lobby_id = result->lobby_id;
@@ -696,13 +706,14 @@ static void send_card_search_result_bb(shared_ptr<ServerState> s,
cmd.destination_command.port = ntohs(local_addr->sin_port);
cmd.destination_command.unused = 0;
auto encoded_server_name = encode_sjis(s->name);
if (result_lobby->is_game()) {
string encoded_lobby_name = encode_sjis(result_lobby->name);
snprintf(cmd.location_string, sizeof(cmd.location_string),
"%s, Block 00, ,%s", encoded_lobby_name.c_str(), s->name.c_str());
"%s, Block 00, ,%s", encoded_lobby_name.c_str(), encoded_server_name.c_str());
} else {
snprintf(cmd.location_string, sizeof(cmd.location_string), "Block 00, ,%s",
s->name.c_str());
encoded_server_name.c_str());
}
cmd.menu_id = LOBBY_MENU_ID;
cmd.lobby_id = result->lobby_id;
@@ -927,7 +938,7 @@ static void send_game_menu_pc(shared_ptr<Client> c, shared_ptr<ServerState> s) {
auto& e = entries.back();
e.menu_id = GAME_MENU_ID;
e.game_id = 0;
decode_sjis(e.name, s->name.c_str(), 0x10);
char16cpy(e.name, s->name.c_str(), 0x10);
}
for (shared_ptr<Lobby> l : s->all_lobbies()) {
if (!l->is_game()) {
@@ -970,7 +981,7 @@ static void send_game_menu_gc(shared_ptr<Client> c, shared_ptr<ServerState> s) {
auto& e = entries.back();
e.menu_id = GAME_MENU_ID;
e.game_id = 0;
strncpy(e.name, s->name.c_str(), 0x10);
encode_sjis(e.name, s->name.c_str(), 0x10);
e.flags = 0x0004;
}
for (shared_ptr<Lobby> l : s->all_lobbies()) {
@@ -1019,7 +1030,7 @@ static void send_game_menu_bb(shared_ptr<Client> c, shared_ptr<ServerState> s) {
e.menu_id = GAME_MENU_ID;
e.game_id = 0;
e.flags = 0x0004;
decode_sjis(e.name, s->name.c_str(), 0x10);
char16cpy(e.name, s->name.c_str(), 0x10);
}
for (shared_ptr<Lobby> l : s->all_lobbies()) {
if (!l->is_game()) {
+8 -1
View File
@@ -23,6 +23,7 @@
#define QUEST_MENU_ID 0x7F02CA94
#define QUEST_FILTER_MENU_ID 0xC38CA039
#define INFORMATION_MENU_GO_BACK 0xFFFFFFFF
#define MAIN_MENU_GO_TO_LOBBY 0x00000001
#define MAIN_MENU_INFORMATION 0x00000002
#define MAIN_MENU_DISCONNECT 0x00000003
@@ -41,6 +42,12 @@ void send_command(std::shared_ptr<TARGET> c, uint16_t command, uint32_t flag,
send_command(c, command, flag, &data, sizeof(data));
}
template <typename TARGET>
void send_command(std::shared_ptr<TARGET> c, uint16_t command, uint32_t flag,
const std::string& data) {
send_command(c, command, flag, data.data(), data.size());
}
template <typename TARGET, typename STRUCT>
void send_command(std::shared_ptr<TARGET> c, uint16_t command, uint32_t flag,
const std::vector<STRUCT>& data) {
@@ -65,7 +72,7 @@ void send_update_client_config(std::shared_ptr<Client> c);
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
void send_pc_gc_split_reconnect(std::shared_ptr<Client> c, uint32_t address,
uint16_t pc_port);
uint16_t pc_port, uint16_t gc_port);
void send_client_init_bb(std::shared_ptr<Client> c, uint32_t error);
void send_team_and_key_config_bb(std::shared_ptr<Client> c);
+14 -2
View File
@@ -143,6 +143,12 @@ void Server::on_client_input(Server::WorkerThread& wt,
c->last_recv_time = now();
this->receive_and_process_commands(c, bev);
if (c->should_disconnect) {
wt.disconnect_client(bev);
this->process_client_disconnect(c);
return;
}
}
void Server::on_client_error(Server::WorkerThread& wt,
@@ -206,8 +212,14 @@ void Server::receive_and_process_commands(shared_ptr<Client> c, struct buffereve
// to call string functions on the buffer in command handlers
string data = c->recv_buffer.substr(offset + header_size, size - header_size);
data.append(4, '\0');
process_command(this->state, c, header->command(c->version),
header->flag(c->version), size - header_size, data.data());
try {
process_command(this->state, c, header->command(c->version),
header->flag(c->version), size - header_size, data.data());
} catch (const exception& e) {
log(INFO, "[Server] error in client stream: %s", e.what());
c->should_disconnect = true;
return;
}
// BB pads commands to 8-byte boundaries, so if we see a shorter command,
// skip over the padding
+17 -3
View File
@@ -3,13 +3,15 @@
#include <memory>
#include "SendCommands.hh"
#include "NetworkAddresses.hh"
#include "Text.hh"
using namespace std;
ServerState::ServerState() : next_lobby_id(1), next_game_id(-1) {
ServerState::ServerState() : run_dns_server(true), next_lobby_id(1),
next_game_id(-1) {
this->main_menu.emplace_back(MAIN_MENU_GO_TO_LOBBY, u"Go to lobby",
u"Join the lobby.", 0);
this->main_menu.emplace_back(MAIN_MENU_INFORMATION, u"Information",
@@ -85,7 +87,9 @@ void ServerState::send_lobby_join_notifications(shared_ptr<Lobby> l,
shared_ptr<Client> joining_client) {
rw_guard g2(l->lock, false);
for (auto& other_client : l->clients) {
if (other_client == joining_client) {
if (!other_client) {
continue;
} else if (other_client == joining_client) {
send_join_lobby(joining_client, l);
} else {
send_player_join_notification(other_client, l, joining_client);
@@ -172,4 +176,14 @@ shared_ptr<Client> ServerState::find_client(const char16_t* identifier,
}
throw out_of_range("client not found");
}
}
uint32_t ServerState::connect_address_for_client(std::shared_ptr<Client> c) {
// TODO: we can do something much smarter here, like use the sockname to find
// out which interface the client is connected to, and return that address
if (is_local_address(c->remote_addr)) {
return this->local_address;
} else {
return this->external_address;
}
}
+4 -2
View File
@@ -26,7 +26,7 @@ struct PortConfiguration {
};
struct ServerState {
std::string name;
std::u16string name;
std::unordered_map<std::string, PortConfiguration> port_configuration;
std::shared_ptr<const QuestIndex> quest_index;
std::shared_ptr<const LevelTable> level_table;
@@ -37,7 +37,7 @@ struct ServerState {
std::vector<MenuItem> main_menu;
std::shared_ptr<std::vector<MenuItem>> information_menu;
std::shared_ptr<std::unordered_map<uint32_t, std::u16string>> id_to_information_contents;
std::shared_ptr<std::vector<std::u16string>> information_contents;
size_t num_threads;
@@ -71,4 +71,6 @@ struct ServerState {
std::shared_ptr<Client> find_client(const char16_t* identifier = NULL,
uint64_t serial_number = 0, std::shared_ptr<Lobby> l = NULL);
uint32_t connect_address_for_client(std::shared_ptr<Client> c);
};
+2 -2
View File
@@ -153,7 +153,7 @@ void add_language_marker_inplace(char* a, char e, size_t dest_count) {
if (existing_count > dest_count - 3) {
existing_count = dest_count - 3;
}
memmove(&a[2], a, existing_count + 1);
memmove(&a[2], a, (existing_count + 1) * sizeof(char));
a[0] = '\t';
a[1] = e;
a[existing_count + 2] = 0;
@@ -168,7 +168,7 @@ void add_language_marker_inplace(char16_t* a, char16_t e, size_t dest_count) {
if (existing_count > dest_count - 3) {
existing_count = dest_count - 3;
}
memmove(&a[2], a, existing_count + 1);
memmove(&a[2], a, (existing_count + 1) * sizeof(char16_t));
a[0] = '\t';
a[1] = e;
a[existing_count + 2] = 0;
+13 -1
View File
@@ -40,8 +40,9 @@ void replace_char_inplace(T* a, T f, T r) {
}
template <typename T>
void add_color_inplace(T* a) {
size_t add_color_inplace(T* a) {
T* d = a;
T* orig_d = d;
while (*a) {
if (*a == '$') {
@@ -59,7 +60,18 @@ void add_color_inplace(T* a) {
} else {
*(d++) = *a;
}
} else {
*(d++) = *a;
}
a++;
}
*d = 0;
return d - orig_d;
}
template <typename T>
void add_color_inplace(std::basic_string<T>& a, size_t header_bytes) {
size_t count = add_color_inplace(const_cast<T*>(a.data() + header_bytes));
a.resize(count + header_bytes);
}
+4 -2
View File
@@ -11,11 +11,13 @@
// Server's name (max. 16 characters)
"ServerName": "Alexandria",
// Address to connect local clients to
"LocalAddress": "10.0.1.6",
"LocalAddress": "192.168.0.5",
// Address to connect external clients to
"ExternalAddress": "10.0.1.6",
// Number of worker threads to run
"Threads": 1,
// Set to false to disable the DNS server
"RunDNSServer": false,
// ****************
// INFORMATION MENU
@@ -23,7 +25,7 @@
// Each entry is a 3-list of [title, short-description, full-contents].
"InformationMenuContents": [
["Text", "$C7Some things you\nmay need to know\nabout text on\nthis server", "$C7Everything you type will be filtered.\n\nDollar signs will become tab chars, which can be\nused to color team names and info boards.\nTo color your text, type %sCx, where x is a\nvalue from the Text Colors list.\n\nPound signs (number signs) will become returns\n(newlines).\n\nA percent sign will create a special character.\nTyping a percent sign followed by one of these\nletters will make one of these special characters:\n%%d = %d %%x = %x %%p = %p %%+ = %+\n%%1 = %1 %%2 = %2 %%3 = %3 %%c = %c\n%%l = %l %%y = %y %%X = %X %%Y = %Y\n%%Z = %Z %%? = %? %%C = %C %%R = %R\n%%s = %s %%%% = %% %%n = %n"],
["Text", "$C7Some things you\nmay need to know\nabout text on\nthis server", "$C7Everything you type will be filtered.\n\nDollar signs will become tab chars, which can be\nused to color team names and info boards.\nTo color your text, type %sCx, where x is a\nvalue from the Text Colors list.\n\nPound signs (number signs) will become returns\n(newlines), the sequence %%s will become %s,\nand the sequence %%%% will become %%."],
["Text colors", "$C7Display color values", "These values can be used to color text.\n\n$C0Color 0$C7 - Black\n$C1Color 1$C7 - Blue\n$C2Color 2$C7 - Green\n$C3Color 3$C7 - Cyan\n$C4Color 4$C7 - Red\n$C5Color 5$C7 - Purple\n$C6Color 6$C7 - Yellow\n$C7Color 7$C7 - White\n$C8Color 8$C7 - Pink\n$C9Color 9$C7 - Violet\n$CGColor G$C7 - Orange Pulse"],
["Lobby commands", "$C7Display commands\nfor use in the\nlobby", "Lobby commands: you must be a moderator to use\nthese commands.\n\n%sallevent <event> - change the server's event\n%sevent <event> - change this lobby's event\n%stype <type> - change this lobby's type\n%sann <message> - announce a message\n%sax <message> - send a message to the server"],
["Game commands", "$C7Display commands\nfor use in games", "Game commands: you must be the game leader to\nuse these commands.\n\n%spassword <password> - set the game's password\n%smaxlevel <%n> - set the game's maximum level\n%sminlevel <%n> - set the game's minimum level\n%scheat - enable or disable cheat mode"],
+2
View File
@@ -1,3 +1,5 @@
0x09 0x0009 # TAB
0x0A 0x000A # NEWLINE
0x20 0x0020 # SPACE
0x21 0x0021 # EXCLAMATION MARK
0x22 0x0022 # QUOTATION MARK