add a bit more of dc nte
This commit is contained in:
@@ -47,7 +47,7 @@ Current known issues / missing features / things to do:
|
||||
newserv supports several versions of PSO. Specifically:
|
||||
| Version | Basic commands | Lobbies | Games | Proxy |
|
||||
|----------------------|----------------|---------------|---------------|---------------|
|
||||
| Dreamcast Trial | Not supported | Not supported | Not supported | Not supported |
|
||||
| Dreamcast Trial | Partial (6) | Not supported | Not supported | Not supported |
|
||||
| Dreamcast V1 | Supported (1) | Supported | Supported | Supported |
|
||||
| Dreamcast V2 | Supported (1) | Supported | Supported | Supported |
|
||||
| PC | Supported | Supported | Supported | Supported |
|
||||
@@ -65,6 +65,7 @@ newserv supports several versions of PSO. Specifically:
|
||||
3. *Episode 3 players can download quests, join lobbies, create and join games, and trade cards, but CARD battles are not implemented yet. Tournaments are also not supported.*
|
||||
4. *newserv's implementation of PSOX is based on disassembly of the client executable; it has never been tested with a real client and most likely doesn't work.*
|
||||
5. *Some basic features are not implemented in Blue Burst games, so the games are not very playable. A lot of work has to be done to get BB games to a playable state.*
|
||||
6. *Support for PSO Dreamcast Trial Edition is very incomplete and probably never will be complete. This is really just exploring a curiosity that sheds some light on early network engineering done by Sega, not an actual attempt at supporting this version of the game.*
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
+1
-1
@@ -610,7 +610,7 @@ static void server_command_convert_char_to_bb(shared_ptr<ServerState> s,
|
||||
|
||||
// Request the player data. The client will respond with a 61, and the handler
|
||||
// for that command will execute the conversion
|
||||
send_command(c, 0x95, 0x00);
|
||||
send_get_player_info(c);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1268,11 +1268,18 @@ struct C_Login_DCNTE_8B {
|
||||
ptext<char, 0x30> password;
|
||||
ptext<char, 0x10> name;
|
||||
parray<uint8_t, 2> unused;
|
||||
};
|
||||
|
||||
struct C_LoginExtended_DCNTE_8B : C_Login_DCNTE_8B {
|
||||
SC_MeetUserExtension<char> extension;
|
||||
};
|
||||
|
||||
// 8C: Invalid command
|
||||
// 8D: Invalid command
|
||||
|
||||
// 8D (S->C): Request player data (DC NTE only)
|
||||
// Behaves the same as 95 (S->C) on all other versions. DC NTE crashes if it
|
||||
// receives 95, so this is used instead.
|
||||
|
||||
// 8E: Invalid command
|
||||
// 8F: Invalid command
|
||||
|
||||
|
||||
+12
-8
@@ -68,19 +68,23 @@ namespace ProxyOptionsMenuItemID {
|
||||
|
||||
struct MenuItem {
|
||||
enum Flag {
|
||||
INVISIBLE_ON_DC = 0x01,
|
||||
INVISIBLE_ON_PC = 0x02,
|
||||
INVISIBLE_ON_GC = 0x04,
|
||||
INVISIBLE_ON_XB = 0x08,
|
||||
INVISIBLE_ON_BB = 0x10,
|
||||
// For menu items to be visible on DCNTE, they must not have either of the
|
||||
// following two flags. (The INVISIBLE_ON_GCNTE flag behaves similarly.)
|
||||
INVISIBLE_ON_DCNTE = 0x001,
|
||||
INVISIBLE_ON_DC = 0x002,
|
||||
INVISIBLE_ON_PC = 0x004,
|
||||
INVISIBLE_ON_GC_TRIAL_EDITION = 0x008,
|
||||
INVISIBLE_ON_GC = 0x010,
|
||||
INVISIBLE_ON_XB = 0x020,
|
||||
INVISIBLE_ON_BB = 0x040,
|
||||
DC_ONLY = INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||
PC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||
GC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
|
||||
XB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
|
||||
BB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB,
|
||||
REQUIRES_MESSAGE_BOXES = 0x20,
|
||||
REQUIRES_SEND_FUNCTION_CALL = 0x40,
|
||||
REQUIRES_SAVE_DISABLED = 0x80,
|
||||
REQUIRES_MESSAGE_BOXES = 0x080,
|
||||
REQUIRES_SEND_FUNCTION_CALL = 0x100,
|
||||
REQUIRES_SAVE_DISABLED = 0x200,
|
||||
};
|
||||
|
||||
uint32_t item_id;
|
||||
|
||||
+27
-7
@@ -336,7 +336,8 @@ static void on_login_8_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
|
||||
static void on_login_b_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
uint16_t, uint32_t, const string& data) { // 8B
|
||||
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data);
|
||||
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data,
|
||||
sizeof(C_Login_DCNTE_8B), sizeof(C_LoginExtended_DCNTE_8B));
|
||||
c->channel.version = GameVersion::DC;
|
||||
c->flags |= flags_for_version(c->version(), -1);
|
||||
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION;
|
||||
@@ -346,7 +347,7 @@ static void on_login_b_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||
serial_number, cmd.access_key);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x8B, 0x01);
|
||||
// send_command(c, 0x8B, 0x01);
|
||||
|
||||
} catch (const incorrect_access_key& e) {
|
||||
send_message_box(c, u"Incorrect access key");
|
||||
@@ -361,9 +362,21 @@ static void on_login_b_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
serial_number, cmd.access_key, true);
|
||||
s->license_manager->add(l);
|
||||
c->set_license(l);
|
||||
send_command(c, 0x8B, 0x01);
|
||||
// send_command(c, 0x8B, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd.is_extended) {
|
||||
const auto& ext_cmd = check_size_t<C_LoginExtended_DCNTE_8B>(data);
|
||||
if (ext_cmd.extension.menu_id == MenuID::LOBBY) {
|
||||
c->preferred_lobby_id = ext_cmd.extension.lobby_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->should_disconnect) {
|
||||
send_update_client_config(c);
|
||||
on_login_complete(s, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_login_0_dc_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
@@ -1188,8 +1201,14 @@ static void on_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
c->should_send_to_lobby_server = true;
|
||||
if (!(c->flags & Client::Flag::SAVE_ENABLED)) {
|
||||
c->flags |= Client::Flag::SAVE_ENABLED;
|
||||
send_command(c, 0x97, 0x01);
|
||||
send_update_client_config(c);
|
||||
// DC NTE crashes if it receives a 97 command, so we instead do the
|
||||
// redirect immediately
|
||||
if ((c->version() == GameVersion::DC) && (c->flags & Client::Flag::IS_TRIAL_EDITION)) {
|
||||
send_client_to_lobby_server(s, c);
|
||||
} else {
|
||||
send_command(c, 0x97, 0x01);
|
||||
send_update_client_config(c);
|
||||
}
|
||||
} else {
|
||||
send_client_to_lobby_server(s, c);
|
||||
}
|
||||
@@ -3178,7 +3197,7 @@ static on_command_t handlers[0x100][6] = {
|
||||
/* 88 */ {nullptr, on_login_8_dcnte, nullptr, on_login_8_dcnte, nullptr, nullptr, }, /* 88 */
|
||||
/* 89 */ {nullptr, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, }, /* 89 */
|
||||
/* 8A */ {nullptr, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, }, /* 8A */
|
||||
/* 8B */ {nullptr, on_login_b_dcnte, nullptr, nullptr, nullptr, nullptr, }, /* 8B */
|
||||
/* 8B */ {nullptr, on_login_b_dcnte, nullptr, on_login_b_dcnte, nullptr, nullptr, }, /* 8B */
|
||||
/* 8C */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8C */
|
||||
/* 8D */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8D */
|
||||
/* 8E */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8E */
|
||||
@@ -3317,13 +3336,14 @@ static void check_unlicensed_command(GameVersion version, uint8_t command) {
|
||||
case GameVersion::XB:
|
||||
// See comment in the DC case above for why DC commands are included here.
|
||||
if (command != 0x88 && // DC NTE
|
||||
command != 0x8B && // DC NTE
|
||||
command != 0x90 && // DC v1
|
||||
command != 0x93 && // DC v1
|
||||
command != 0x9A && // DC v2
|
||||
command != 0x9D && // DC v2, GC trial edition
|
||||
command != 0x9E && // GC non-trial
|
||||
command != 0xDB) { // GC non-trial
|
||||
throw runtime_error("only commands 90, 93, 9A, 9D, 9E, and DB may be sent before login");
|
||||
throw runtime_error("only commands 88, 8B, 90, 93, 9A, 9D, 9E, and DB may be sent before login");
|
||||
}
|
||||
break;
|
||||
case GameVersion::BB:
|
||||
|
||||
+50
-15
@@ -23,6 +23,8 @@ using namespace std;
|
||||
|
||||
const unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
||||
0x00260088, // (17) DCNTE license check
|
||||
0x00B0008B, // (02) DCNTE login
|
||||
0x0114008B, // (02) DCNTE extended login
|
||||
0x00280090, // (17) DCv1 license check
|
||||
0x00B00093, // (02) DCv1 login
|
||||
0x01140093, // (02) DCv1 extended login
|
||||
@@ -881,21 +883,49 @@ void send_menu_t(
|
||||
}
|
||||
|
||||
for (const auto& item : items) {
|
||||
if (((c->version() == GameVersion::DC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_DC)) ||
|
||||
((c->version() == GameVersion::PC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_PC)) ||
|
||||
((c->version() == GameVersion::GC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_GC)) ||
|
||||
((c->version() == GameVersion::XB) && (item.flags & MenuItem::Flag::INVISIBLE_ON_XB)) ||
|
||||
((c->version() == GameVersion::BB) && (item.flags & MenuItem::Flag::INVISIBLE_ON_BB)) ||
|
||||
((item.flags & MenuItem::Flag::REQUIRES_MESSAGE_BOXES) && (c->flags & Client::Flag::NO_D6)) ||
|
||||
((item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) && (c->flags & Client::Flag::NO_SEND_FUNCTION_CALL)) ||
|
||||
((item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) && (c->flags & Client::Flag::SAVE_ENABLED))) {
|
||||
continue;
|
||||
bool is_visible = true;
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DC);
|
||||
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_DCNTE);
|
||||
}
|
||||
break;
|
||||
case GameVersion::PC:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_PC);
|
||||
break;
|
||||
case GameVersion::GC:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC);
|
||||
if (c->flags & Client::Flag::IS_TRIAL_EDITION) {
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_GC_TRIAL_EDITION);
|
||||
}
|
||||
break;
|
||||
case GameVersion::XB:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_XB);
|
||||
break;
|
||||
case GameVersion::BB:
|
||||
is_visible &= !(item.flags & MenuItem::Flag::INVISIBLE_ON_BB);
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("menus not supported for this game version");
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_MESSAGE_BOXES) {
|
||||
is_visible &= !(c->flags & Client::Flag::NO_D6);
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) {
|
||||
is_visible &= !(c->flags & Client::Flag::NO_SEND_FUNCTION_CALL);
|
||||
}
|
||||
if (item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) {
|
||||
is_visible &= !(c->flags & Client::Flag::SAVE_ENABLED);
|
||||
}
|
||||
|
||||
if (is_visible) {
|
||||
auto& e = entries.emplace_back();
|
||||
e.menu_id = menu_id;
|
||||
e.item_id = item.item_id;
|
||||
e.flags = (c->version() == GameVersion::BB) ? 0x0004 : 0x0F04;
|
||||
e.text = item.name;
|
||||
}
|
||||
auto& e = entries.emplace_back();
|
||||
e.menu_id = menu_id;
|
||||
e.item_id = item.item_id;
|
||||
e.flags = (c->version() == GameVersion::BB) ? 0x0004 : 0x0F04;
|
||||
e.text = item.name;
|
||||
}
|
||||
|
||||
send_command_vt(c, is_info_menu ? 0x1F : 0x07, entries.size() - 1, entries);
|
||||
@@ -1294,7 +1324,12 @@ void send_self_leave_notification(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
void send_get_player_info(shared_ptr<Client> c) {
|
||||
send_command(c, 0x95, 0x00);
|
||||
if ((c->version() == GameVersion::DC) &&
|
||||
(c->flags & Client::Flag::IS_TRIAL_EDITION)) {
|
||||
send_command(c, 0x8D, 0x00);
|
||||
} else {
|
||||
send_command(c, 0x95, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-3
@@ -406,8 +406,11 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
|
||||
this->main_menu.emplace_back(MainMenuItemID::GO_TO_LOBBY, u"Go to lobby",
|
||||
u"Join the lobby", 0);
|
||||
this->main_menu.emplace_back(MainMenuItemID::INFORMATION, u"Information",
|
||||
u"View server\ninformation", MenuItem::Flag::REQUIRES_MESSAGE_BOXES);
|
||||
u"View server\ninformation", MenuItem::Flag::INVISIBLE_ON_DCNTE | MenuItem::Flag::REQUIRES_MESSAGE_BOXES);
|
||||
uint32_t proxy_destinations_menu_item_flags =
|
||||
// DCNTE doesn't support multiple ship select menus without changing
|
||||
// servers (via a 19 command) apparently :(
|
||||
MenuItem::Flag::INVISIBLE_ON_DCNTE |
|
||||
(this->proxy_destinations_dc.empty() ? MenuItem::Flag::INVISIBLE_ON_DC : 0) |
|
||||
(this->proxy_destinations_pc.empty() ? MenuItem::Flag::INVISIBLE_ON_PC : 0) |
|
||||
(this->proxy_destinations_gc.empty() ? MenuItem::Flag::INVISIBLE_ON_GC : 0) |
|
||||
@@ -416,7 +419,7 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
|
||||
this->main_menu.emplace_back(MainMenuItemID::PROXY_DESTINATIONS, u"Proxy server",
|
||||
u"Connect to another\nserver", proxy_destinations_menu_item_flags);
|
||||
this->main_menu.emplace_back(MainMenuItemID::DOWNLOAD_QUESTS, u"Download quests",
|
||||
u"Download quests", MenuItem::Flag::INVISIBLE_ON_BB);
|
||||
u"Download quests", MenuItem::Flag::INVISIBLE_ON_DCNTE | MenuItem::Flag::INVISIBLE_ON_BB);
|
||||
if (!this->function_code_index->patch_menu_empty()) {
|
||||
this->main_menu.emplace_back(MainMenuItemID::PATCHES, u"Patches",
|
||||
u"Change game\nbehaviors", MenuItem::Flag::GC_ONLY | MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
|
||||
@@ -429,7 +432,7 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
|
||||
u"Disconnect", 0);
|
||||
this->main_menu.emplace_back(MainMenuItemID::CLEAR_LICENSE, u"Clear license",
|
||||
u"Disconnect with an\ninvalid license error\nso you can enter a\ndifferent serial\nnumber, access key,\nor password",
|
||||
MenuItem::Flag::INVISIBLE_ON_BB);
|
||||
MenuItem::Flag::INVISIBLE_ON_DCNTE | MenuItem::Flag::INVISIBLE_ON_BB);
|
||||
|
||||
try {
|
||||
this->welcome_message = decode_sjis(d.at("WelcomeMessage")->as_string());
|
||||
|
||||
Reference in New Issue
Block a user