fix GC NTE proxy behavior
This commit is contained in:
@@ -42,7 +42,7 @@ newserv supports several versions of PSO. Specifically:
|
||||
| DC 08/2001 | Untested (1) | Untested (1) | Untested (1) |
|
||||
| DC V2 | Yes | Yes | Yes |
|
||||
| PC | Yes | Yes | Yes |
|
||||
| GC Ep1&2 Trial | Yes | Yes | No |
|
||||
| GC Ep1&2 Trial | Yes | Yes | Yes |
|
||||
| GC Ep1&2 | Yes | Yes | Yes |
|
||||
| GC Ep1&2 Plus | Yes | Yes | Yes |
|
||||
| GC Ep3 Trial | Yes | Partial (3) | Yes |
|
||||
@@ -94,7 +94,7 @@ Within the category directories, quest files should be named like `q###-VERSION-
|
||||
|
||||
For .dat files, the `LANGUAGE` token may be omitted. If it's present, then that .dat file will only be used for that language of the quest; if omitted, then that .dat file will be used for all languages of the quest.
|
||||
|
||||
Some quests (mostly battle and challenge mode quests) have additional JSON metadata files that describe how the server should handle them. This includes flags that can be used to hide the quest unless a preceding quest has been cleared, or to hide the quest unless purchased as a team reward. These metadata files are generally named similarly to their .bin and .dat counterparts, except the `VERSION` token may also be omitted if the metadata applies to all versions of the quest on all PSO versions. See system/quests/battle/b88001.json for documentation on the exact format of the JSON file.
|
||||
Some quests (mostly battle and challenge mode quests) have additional JSON metadata files that describe how the server should handle them. This includes flags that can be used to hide the quest unless a preceding quest has been cleared, or to hide the quest unless purchased as a team reward. These metadata files are generally named similarly to their .bin and .dat counterparts, except the `VERSION` token may also be omitted if the metadata applies to all languages of the quest on all PSO versions. See system/quests/battle/b88001.json for documentation on the exact format of the JSON file.
|
||||
|
||||
Some quests may also include a .pvr file, which contains an image used in the quest. These files are named similarly to their .bin and .dat counterparts.
|
||||
|
||||
|
||||
@@ -1072,6 +1072,7 @@ static HandlerResult C_GXB_61(shared_ptr<ProxyServer::LinkedSession> ses, uint16
|
||||
pd = reinterpret_cast<C_CharacterData_V3_61_98*>(&ep3_pd);
|
||||
} else {
|
||||
if (is_ep3(ses->version())) {
|
||||
ses->log.info("Version changed to GC_EP3_TRIAL_EDITION");
|
||||
ses->set_version(Version::GC_EP3_TRIAL_EDITION);
|
||||
}
|
||||
pd = &check_size_t<C_CharacterData_V3_61_98>(data, 0xFFFF);
|
||||
|
||||
+35
-38
@@ -239,8 +239,7 @@ ProxyServer::UnlinkedSession::UnlinkedSession(
|
||||
string_printf("UnlinkedSession:%p", bev),
|
||||
TerminalFormat::FG_YELLOW,
|
||||
TerminalFormat::FG_GREEN),
|
||||
local_port(local_port),
|
||||
version(version) {
|
||||
local_port(local_port) {
|
||||
memset(&this->next_destination, 0, sizeof(this->next_destination));
|
||||
}
|
||||
|
||||
@@ -264,40 +263,46 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
bool should_close_unlinked_session = false;
|
||||
|
||||
try {
|
||||
switch (ses->version) {
|
||||
case Version::DC_NTE: {
|
||||
// We should only get an 8B while the session is unlinked
|
||||
if (command != 0x8B) {
|
||||
throw runtime_error("command is not 8B");
|
||||
}
|
||||
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
// TODO: Parse cmd.hardware_id
|
||||
ses->version = Version::DC_NTE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ses->version()) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
// We should only get a 93 or 9D while the session is unlinked
|
||||
if (command == 0x93) {
|
||||
case Version::GC_NTE:
|
||||
// We should only get an 8B, 93 or 9D while the session is unlinked
|
||||
if (command == 0x8B) {
|
||||
ses->channel.version = Version::DC_NTE;
|
||||
ses->log.info("Version changed to DC_NTE");
|
||||
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data, sizeof(C_LoginExtended_DCNTE_8B));
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
// TODO: Parse cmd.hardware_id
|
||||
} else if (command == 0x93) { // 11/2000 proto through DC V1
|
||||
ses->channel.version = Version::DC_V1;
|
||||
ses->log.info("Version changed to DC_V1");
|
||||
const auto& cmd = check_size_t<C_LoginV1_DC_93>(data);
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
ses->hardware_id = cmd.hardware_id.decode();
|
||||
ses->version = Version::DC_V1;
|
||||
} else if (command == 0x9D) {
|
||||
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
if (cmd.sub_version >= 0x30) {
|
||||
ses->log.info("Version changed to GC_NTE");
|
||||
ses->channel.version = Version::GC_NTE;
|
||||
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
} else { // DC V2
|
||||
ses->log.info("Version changed to DC_V2");
|
||||
ses->channel.version = Version::DC_V2;
|
||||
ses->license = s->license_index->verify_v1_v2(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
}
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
ses->config.set_flags_for_version(ses->version(), cmd.sub_version);
|
||||
} else {
|
||||
throw runtime_error("command is not 93 or 9D");
|
||||
}
|
||||
@@ -316,20 +321,11 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
break;
|
||||
}
|
||||
|
||||
case Version::GC_NTE:
|
||||
case Version::GC_V3:
|
||||
case Version::GC_EP3_TRIAL_EDITION:
|
||||
case Version::GC_EP3:
|
||||
// We should only get a 9D or 9E while the session is unlinked
|
||||
if (command == 0x9D) {
|
||||
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9D>(data, sizeof(C_LoginExtended_DC_GC_9D));
|
||||
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
ses->channel.language = cmd.language;
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
ses->version = Version::GC_NTE;
|
||||
ses->config.set_flags_for_version(ses->version, cmd.sub_version);
|
||||
} else if (command == 0x9E) {
|
||||
// We should only get a 9E while the session is unlinked
|
||||
if (command == 0x9E) {
|
||||
const auto& cmd = check_size_t<C_Login_GC_9E>(data, sizeof(C_LoginExtended_GC_9E));
|
||||
ses->license = s->license_index->verify_gc(stoul(cmd.serial_number.decode(), nullptr, 16), cmd.access_key.decode());
|
||||
ses->sub_version = cmd.sub_version;
|
||||
@@ -337,7 +333,8 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
ses->character_name = cmd.name.decode(ses->channel.language);
|
||||
ses->config.parse_from(cmd.client_config);
|
||||
if (cmd.sub_version >= 0x40) {
|
||||
ses->version = Version::GC_EP3;
|
||||
ses->log.info("Version changed to GC_EP3");
|
||||
ses->channel.version = Version::GC_EP3;
|
||||
}
|
||||
} else {
|
||||
throw runtime_error("command is not 9D or 9E");
|
||||
@@ -425,10 +422,10 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
// destination somewhere - either in the client config or in the unlinked
|
||||
// session
|
||||
if (ses->config.proxy_destination_address != 0) {
|
||||
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version, ses->license, ses->config);
|
||||
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version(), ses->license, ses->config);
|
||||
linked_ses->log.info("Opened licensed session for unlinked session based on client config");
|
||||
} else if (ses->next_destination.ss_family == AF_INET) {
|
||||
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version, ses->license, ses->next_destination);
|
||||
linked_ses = make_shared<LinkedSession>(server, ses->local_port, ses->version(), ses->license, ses->next_destination);
|
||||
linked_ses->log.info("Opened licensed session for unlinked session based on unlinked default destination");
|
||||
} else {
|
||||
ses->log.error("Cannot open linked session: no valid destination in client config or unlinked session");
|
||||
@@ -437,12 +434,12 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
|
||||
|
||||
if (linked_ses.get()) {
|
||||
server->id_to_session.emplace(ses->license->serial_number, linked_ses);
|
||||
if (linked_ses->version() != ses->version) {
|
||||
if (linked_ses->version() != ses->version()) {
|
||||
linked_ses->log.error("Linked session has different game version");
|
||||
} else {
|
||||
// Resume the linked session using the unlinked session
|
||||
try {
|
||||
if (ses->version == Version::BB_V4) {
|
||||
if (ses->version() == Version::BB_V4) {
|
||||
linked_ses->resume(
|
||||
std::move(ses->channel),
|
||||
ses->detector_crypt,
|
||||
|
||||
+4
-1
@@ -219,7 +219,6 @@ private:
|
||||
PrefixedLogger log;
|
||||
Channel channel;
|
||||
uint16_t local_port;
|
||||
Version version;
|
||||
struct sockaddr_storage next_destination;
|
||||
|
||||
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> detector_crypt;
|
||||
@@ -242,6 +241,10 @@ private:
|
||||
std::shared_ptr<ProxyServer> require_server() const;
|
||||
std::shared_ptr<ServerState> require_server_state() const;
|
||||
|
||||
inline Version version() const {
|
||||
return this->channel.version;
|
||||
}
|
||||
|
||||
void receive_and_process_commands();
|
||||
|
||||
static void on_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
|
||||
|
||||
+1
-1
@@ -370,10 +370,10 @@ const vector<pair<string, uint16_t>>& ServerState::proxy_destinations_for_versio
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
case Version::GC_NTE:
|
||||
return this->proxy_destinations_dc;
|
||||
case Version::PC_V2:
|
||||
return this->proxy_destinations_pc;
|
||||
case Version::GC_NTE:
|
||||
case Version::GC_V3:
|
||||
case Version::GC_EP3_TRIAL_EDITION:
|
||||
case Version::GC_EP3:
|
||||
|
||||
+1
-1
@@ -70,8 +70,8 @@ const char* proxy_port_name_for_version(Version v) {
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
return "dc-proxy";
|
||||
case Version::GC_NTE:
|
||||
return "dc-proxy";
|
||||
case Version::GC_V3:
|
||||
case Version::GC_EP3_TRIAL_EDITION:
|
||||
case Version::GC_EP3:
|
||||
|
||||
+3
-2
@@ -84,8 +84,9 @@ inline bool uses_v2_encryption(Version version) {
|
||||
}
|
||||
inline bool uses_v3_encryption(Version version) {
|
||||
return (version == Version::GC_V3) ||
|
||||
(version == Version::XB_V3) ||
|
||||
(version == Version::GC_EP3);
|
||||
(version == Version::GC_EP3_TRIAL_EDITION) ||
|
||||
(version == Version::GC_EP3) ||
|
||||
(version == Version::XB_V3);
|
||||
}
|
||||
inline bool uses_v4_encryption(Version version) {
|
||||
return (version == Version::BB_V4);
|
||||
|
||||
@@ -147,6 +147,9 @@
|
||||
// version, the proxy server is disabled for that version. Entries in these
|
||||
// dictionaries should be of the form "name": "address:port"; the names are
|
||||
// used in the proxy server menu.
|
||||
// Note that PSO GameCube Episodes 1&2 Trial Edition uses the DC's
|
||||
// ProxyDestinations dictionary here. This is because other servers that
|
||||
// support that version treat it as PSO DC v2.
|
||||
"ProxyDestinations-DC": {},
|
||||
"ProxyDestinations-PC": {},
|
||||
"ProxyDestinations-GC": {},
|
||||
|
||||
Reference in New Issue
Block a user