make some progress toward getting psogc to work again
This commit is contained in:
+5
-2
@@ -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
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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"],
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
0x09 0x0009 # TAB
|
||||
0x0A 0x000A # NEWLINE
|
||||
0x20 0x0020 # SPACE
|
||||
0x21 0x0021 # EXCLAMATION MARK
|
||||
0x22 0x0022 # QUOTATION MARK
|
||||
|
||||
Reference in New Issue
Block a user