Merge upstream newserv master
This commit is contained in:
+57
-14
@@ -7,6 +7,7 @@
|
||||
#include <phosg/Network.hh>
|
||||
#include <phosg/Time.hh>
|
||||
|
||||
#include "CommandCensorData.hh"
|
||||
#include "Loggers.hh"
|
||||
#include "StaticGameData.hh"
|
||||
#include "Version.hh"
|
||||
@@ -20,12 +21,16 @@ Channel::Channel(
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials)
|
||||
: version(version),
|
||||
language(language),
|
||||
name(name),
|
||||
terminal_send_color(terminal_send_color),
|
||||
terminal_recv_color(terminal_recv_color) {
|
||||
terminal_recv_color(terminal_recv_color),
|
||||
censor_received_credentials(censor_received_credentials),
|
||||
censor_sent_credentials(censor_sent_credentials) {
|
||||
}
|
||||
|
||||
void Channel::send(uint16_t cmd, uint32_t flag, bool silent) {
|
||||
@@ -132,7 +137,20 @@ void Channel::send(
|
||||
command_data_log.info_f("Sending to {} (version={} command={:02X} flag={:02X})",
|
||||
this->name, phosg::name_for_enum(version), cmd, flag);
|
||||
}
|
||||
phosg::print_data(stderr, send_data.data(), logical_size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
|
||||
struct iovec iov{.iov_base = send_data.data(), .iov_len = send_data.size()};
|
||||
|
||||
if (this->censor_sent_credentials) {
|
||||
auto [censor_data, censor_size] = censor_data_for_client_command(this->version, cmd);
|
||||
struct iovec censor_iovs[2] = {
|
||||
// const_casts are OK here because print_data does not modify the buffers
|
||||
{.iov_base = const_cast<char*>("\0\0\0\0\0\0\0\0"), .iov_len = static_cast<size_t>(is_v4(this->version) ? 8 : 4)},
|
||||
{.iov_base = const_cast<void*>(censor_data), .iov_len = censor_size}};
|
||||
phosg::print_data(stderr, &iov, 1, 0, nullptr, 0, censor_iovs, 2, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
} else {
|
||||
phosg::print_data(stderr, &iov, 1, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
}
|
||||
|
||||
if (use_terminal_colors && this->terminal_send_color != phosg::TerminalFormat::NORMAL) {
|
||||
print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
|
||||
}
|
||||
@@ -201,6 +219,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
|
||||
}
|
||||
command_data.resize(command_logical_size - header_size);
|
||||
|
||||
uint16_t command = header.command(this->version);
|
||||
if (command_data_log.should_log(phosg::LogLevel::L_INFO) && (this->terminal_recv_color != phosg::TerminalFormat::END)) {
|
||||
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
|
||||
print_color_escape(stderr, this->terminal_recv_color, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::END);
|
||||
@@ -221,10 +240,20 @@ asio::awaitable<Channel::Message> Channel::recv() {
|
||||
header.flag(this->version));
|
||||
}
|
||||
|
||||
vector<struct iovec> iovs;
|
||||
iovs.emplace_back(iovec{.iov_base = &header, .iov_len = header_size});
|
||||
iovs.emplace_back(iovec{.iov_base = command_data.data(), .iov_len = command_data.size()});
|
||||
phosg::print_data(stderr, iovs, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
struct iovec iovs[2] = {
|
||||
{.iov_base = &header, .iov_len = header_size},
|
||||
{.iov_base = command_data.data(), .iov_len = command_data.size()}};
|
||||
|
||||
if (this->censor_received_credentials) {
|
||||
auto [censor_data, censor_size] = censor_data_for_client_command(this->version, command);
|
||||
struct iovec censor_iovs[2] = {
|
||||
// const_casts are OK here because print_data does not modify the buffers
|
||||
{.iov_base = const_cast<char*>("\0\0\0\0\0\0\0\0"), .iov_len = header_size},
|
||||
{.iov_base = const_cast<void*>(censor_data), .iov_len = censor_size}};
|
||||
phosg::print_data(stderr, iovs, 2, 0, nullptr, 0, censor_iovs, 2, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
} else {
|
||||
phosg::print_data(stderr, iovs, 2, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
}
|
||||
|
||||
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
|
||||
phosg::print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
|
||||
@@ -232,7 +261,7 @@ asio::awaitable<Channel::Message> Channel::recv() {
|
||||
}
|
||||
|
||||
co_return Message{
|
||||
.command = header.command(this->version),
|
||||
.command = command,
|
||||
.flag = header.flag(this->version),
|
||||
.data = std::move(command_data),
|
||||
};
|
||||
@@ -245,9 +274,19 @@ shared_ptr<SocketChannel> SocketChannel::create(
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color) {
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials) {
|
||||
shared_ptr<SocketChannel> ret(new SocketChannel(
|
||||
io_context, std::move(sock), version, language, name, terminal_send_color, terminal_recv_color));
|
||||
io_context,
|
||||
std::move(sock),
|
||||
version,
|
||||
language,
|
||||
name,
|
||||
terminal_send_color,
|
||||
terminal_recv_color,
|
||||
censor_received_credentials,
|
||||
censor_sent_credentials));
|
||||
asio::co_spawn(*io_context, ret->send_task(), asio::detached);
|
||||
return ret;
|
||||
}
|
||||
@@ -259,8 +298,10 @@ SocketChannel::SocketChannel(
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color),
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
|
||||
sock(std::move(sock)),
|
||||
local_addr(this->sock->local_endpoint()),
|
||||
remote_addr(this->sock->remote_endpoint()),
|
||||
@@ -327,8 +368,10 @@ PeerChannel::PeerChannel(
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color),
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
|
||||
send_buffer_nonempty_signal(io_context->get_executor()) {}
|
||||
|
||||
void PeerChannel::link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2) {
|
||||
|
||||
+19
-9
@@ -19,6 +19,8 @@ public:
|
||||
std::string name;
|
||||
phosg::TerminalFormat terminal_send_color;
|
||||
phosg::TerminalFormat terminal_recv_color;
|
||||
bool censor_received_credentials;
|
||||
bool censor_sent_credentials;
|
||||
|
||||
struct Message {
|
||||
uint16_t command;
|
||||
@@ -87,8 +89,10 @@ protected:
|
||||
Version version,
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials);
|
||||
Channel(const Channel& other) = delete;
|
||||
Channel(Channel&& other) = delete;
|
||||
Channel& operator=(const Channel& other) = delete;
|
||||
@@ -114,9 +118,11 @@ public:
|
||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||
Version version,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials);
|
||||
|
||||
virtual std::string default_name() const;
|
||||
|
||||
@@ -134,7 +140,9 @@ private:
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color);
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials);
|
||||
|
||||
std::deque<std::string> outbound_data;
|
||||
bool should_disconnect = false;
|
||||
@@ -152,9 +160,11 @@ public:
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
Version version,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials);
|
||||
|
||||
static void link_peers(std::shared_ptr<PeerChannel> peer1, std::shared_ptr<PeerChannel> peer2);
|
||||
|
||||
|
||||
+16
-4
@@ -438,9 +438,21 @@ bool Client::can_use_chat_commands() const {
|
||||
|
||||
void Client::set_login(shared_ptr<Login> login) {
|
||||
this->login = login;
|
||||
|
||||
auto s = this->require_server_state();
|
||||
if (!s->allow_same_account_concurrent_logins) {
|
||||
auto it = s->client_for_account.find(login->account->account_id);
|
||||
if ((it != s->client_for_account.end()) && (it->second.get() != this)) {
|
||||
if (it->second->channel) {
|
||||
it->second->channel->disconnect();
|
||||
}
|
||||
s->client_for_account.erase(it);
|
||||
}
|
||||
s->client_for_account.emplace(this->login->account->account_id, this->shared_from_this());
|
||||
}
|
||||
|
||||
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
|
||||
string login_str = this->login->str();
|
||||
this->log.info_f("Login: {}", login_str);
|
||||
this->log.info_f("Login: {}", this->login->str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +698,7 @@ void Client::create_challenge_overlay(
|
||||
|
||||
for (size_t z = 0; z < overlay->inventory.items.size(); z++) {
|
||||
auto& i = overlay->inventory.items[z];
|
||||
i.present = 0;
|
||||
i.state = 0;
|
||||
i.unknown_a1 = 0;
|
||||
i.extension_data1 = 0;
|
||||
i.extension_data2 = 0;
|
||||
@@ -712,7 +724,7 @@ void Client::create_challenge_overlay(
|
||||
|
||||
for (size_t z = 0; z < tpl.items.size(); z++) {
|
||||
auto& inv_item = overlay->inventory.items[z];
|
||||
inv_item.present = tpl.items[z].present;
|
||||
inv_item.state = tpl.items[z].state;
|
||||
inv_item.unknown_a1 = tpl.items[z].unknown_a1;
|
||||
inv_item.flags = tpl.items[z].flags;
|
||||
inv_item.data = tpl.items[z].data;
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
#include "CommandCensorData.hh"
|
||||
|
||||
#include "CommandFormats.hh"
|
||||
|
||||
std::pair<const void*, size_t> censor_data_for_client_command(Version version, uint16_t command) {
|
||||
switch (command) {
|
||||
case 0x03: {
|
||||
static const C_LegacyLogin_PC_V3_03 ret{
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused = 0,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x04:
|
||||
if (is_patch(version)) {
|
||||
static const C_Login_Patch_04 ret{.unused{0}, .username{1}, .password{1}, .email_address{1}};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else if (!is_v4(version)) {
|
||||
static const C_LegacyLogin_PC_V3_04 ret{
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused = 0,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
static const C_LegacyLogin_BB_04 ret{
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused = 0,
|
||||
.username = 1,
|
||||
.password = 1};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x88:
|
||||
if (version == Version::DC_NTE) {
|
||||
static const C_Login_DCNTE_88 ret{.serial_number{1}, .access_key{1}};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
case 0x8A:
|
||||
if (version == Version::DC_NTE) {
|
||||
static const C_ConnectionInfo_DCNTE_8A ret{
|
||||
.hardware_id = 0, .sub_version = 0, .unused = 0, .username = 1, .password = 1, .email_address = 1};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
case 0x8B:
|
||||
if (version == Version::DC_NTE) {
|
||||
static const C_Login_DCNTE_8B ret{
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused1 = 0,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.username = 1,
|
||||
.password = 1,
|
||||
.login_character_name = 0,
|
||||
.unused = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
case 0x90: {
|
||||
static const C_LoginV1_DC_PC_V3_90 ret{.serial_number = 1, .access_key = 1};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x92: {
|
||||
static const C_RegisterV1_DC_92 ret{
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.unused1 = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused2 = 0,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
.email_address = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x93:
|
||||
if (!is_v4(version)) {
|
||||
static const C_LoginV1_DC_93 ret{
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused1 = 0,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
.login_character_name = 0,
|
||||
.unused2 = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
static const C_LoginBase_BB_93 ret{
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.sub_version = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.character_slot = 0,
|
||||
.connection_phase = 0,
|
||||
.client_code = 0,
|
||||
.security_token = 0,
|
||||
.username = 1,
|
||||
.password = 1,
|
||||
.menu_id = 0,
|
||||
.preferred_lobby_id = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x9A: {
|
||||
static const C_Login_DC_PC_V3_9A ret{
|
||||
.v1_serial_number = 1,
|
||||
.v1_access_key = 1,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.sub_version = 0,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
.email_address = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x9C:
|
||||
if (!is_v4(version)) {
|
||||
static const C_Register_DC_PC_V3_9C ret{
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.unused1 = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused2 = 0,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.password = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
static const C_Register_BB_9C ret{
|
||||
.sub_version = 0,
|
||||
.unused1 = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused2 = 0,
|
||||
.username = 1,
|
||||
.password = 1,
|
||||
.game_tag = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0x9D:
|
||||
case 0x9E:
|
||||
if (!is_v4(version)) {
|
||||
static const C_Login_DC_PC_GC_9D ret{
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.is_extended = 0,
|
||||
.language = Language::JAPANESE,
|
||||
.unused3 = 0,
|
||||
.v1_serial_number = 1,
|
||||
.v1_access_key = 1,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
.login_character_name = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
static const C_LoginExtended_BB_9E ret{
|
||||
.player_tag = 0,
|
||||
.guild_card_number = 0,
|
||||
.sub_version = 0,
|
||||
.language32 = 0,
|
||||
.unknown_a2 = 0,
|
||||
.v1_serial_number = 1,
|
||||
.v1_access_key = 1,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.username = 1,
|
||||
.password = 1,
|
||||
.guild_card_number_str = 0,
|
||||
.client_config = 0,
|
||||
.extension{},
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
case 0xDB:
|
||||
if (!is_v4(version)) {
|
||||
static const C_VerifyAccount_V3_DB ret{
|
||||
.v1_serial_number = 1,
|
||||
.v1_access_key = 1,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.hardware_id = 0,
|
||||
.sub_version = 0,
|
||||
.serial_number2 = 1,
|
||||
.access_key2 = 1,
|
||||
.password = 1,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
} else {
|
||||
static const C_VerifyAccount_BB_DB ret{
|
||||
.v1_serial_number = 1,
|
||||
.v1_access_key = 1,
|
||||
.serial_number = 1,
|
||||
.access_key = 1,
|
||||
.sub_version = 0,
|
||||
.username = 1,
|
||||
.password = 1,
|
||||
.game_tag = 0,
|
||||
};
|
||||
return std::make_pair(&ret, sizeof(ret));
|
||||
}
|
||||
default:
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "Version.hh"
|
||||
|
||||
std::pair<const void*, size_t> censor_data_for_client_command(Version version, uint16_t command);
|
||||
+11
-11
@@ -257,7 +257,7 @@ struct S_ReconnectT {
|
||||
U16T<BE> port = 0;
|
||||
le_uint16_t unused = 0;
|
||||
} __packed_ws_be__(S_ReconnectT, 0x08);
|
||||
using S_Reconnect_Patch_14 = S_ReconnectT<false>;
|
||||
using S_Reconnect_Patch_14 = S_ReconnectT<true>;
|
||||
|
||||
// 15 (S->C): Login failure
|
||||
// No arguments. The client shows a message like "Incorrect game ID or password" and disconnects.
|
||||
@@ -318,7 +318,7 @@ struct S_ServerInitWithAfterMessageT_DC_PC_V3_02_17_91_9B {
|
||||
// Internal name: SndRegist
|
||||
|
||||
struct C_LegacyLogin_PC_V3_03 {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 00 */ be_uint64_t hardware_id = 0;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t is_extended = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
@@ -364,7 +364,7 @@ struct S_ServerInitWithAfterMessageT_BB_03_9B {
|
||||
// likely a relic of an older, now-unused sequence. Like 03, this command isn't used by any known PSO version.
|
||||
|
||||
struct C_LegacyLogin_PC_V3_04 {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 00 */ be_uint64_t hardware_id = 0;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t is_extended = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
@@ -1509,7 +1509,7 @@ struct S_ArrowUpdateEntry_88 {
|
||||
// The server should respond with an 8A command.
|
||||
|
||||
struct C_ConnectionInfo_DCNTE_8A {
|
||||
be_uint64_t hardware_id;
|
||||
be_uint64_t hardware_id = 0;
|
||||
le_uint32_t sub_version = 0x20;
|
||||
le_uint32_t unused = 0;
|
||||
pstring<TextEncoding::ASCII, 0x30> username;
|
||||
@@ -1536,7 +1536,7 @@ struct C_ConnectionInfo_DCNTE_8A {
|
||||
struct C_Login_DCNTE_8B {
|
||||
le_uint32_t player_tag = 0x00010000;
|
||||
le_uint32_t guild_card_number = 0;
|
||||
be_uint64_t hardware_id;
|
||||
be_uint64_t hardware_id = 0;
|
||||
le_uint32_t sub_version = 0x20;
|
||||
uint8_t is_extended = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
@@ -1589,7 +1589,7 @@ struct C_LoginV1_DC_PC_V3_90 {
|
||||
// 92 (C->S): Register (DC)
|
||||
|
||||
struct C_RegisterV1_DC_92 {
|
||||
be_uint64_t hardware_id;
|
||||
be_uint64_t hardware_id = 0;
|
||||
le_uint32_t sub_version;
|
||||
uint8_t unused1 = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
@@ -1608,7 +1608,7 @@ struct C_RegisterV1_DC_92 {
|
||||
struct C_LoginV1_DC_93 {
|
||||
/* 00 */ le_uint32_t player_tag = 0x00010000;
|
||||
/* 04 */ le_uint32_t guild_card_number = 0;
|
||||
/* 08 */ be_uint64_t hardware_id;
|
||||
/* 08 */ be_uint64_t hardware_id = 0;
|
||||
/* 10 */ le_uint32_t sub_version = 0;
|
||||
/* 14 */ uint8_t is_extended = 0;
|
||||
/* 15 */ Language language = Language::JAPANESE;
|
||||
@@ -1662,7 +1662,7 @@ struct C_LoginWithoutHardwareInfo_BB_93 : C_LoginBase_BB_93 {
|
||||
|
||||
struct C_LoginWithHardwareInfo_BB_93 : C_LoginBase_BB_93 {
|
||||
// See the comment in the above structure. This format is used on newer client versions.
|
||||
/* 7C */ be_uint64_t hardware_id;
|
||||
/* 7C */ be_uint64_t hardware_id = 0;
|
||||
/* 84 */ parray<uint8_t, 0x28> client_config;
|
||||
/* AC */
|
||||
} __packed_ws__(C_LoginWithHardwareInfo_BB_93, 0xAC);
|
||||
@@ -1773,7 +1773,7 @@ struct C_Login_DC_PC_V3_9A {
|
||||
// It appears PSO GC sends uninitialized data in the header.flag field here.
|
||||
|
||||
struct C_Register_DC_PC_V3_9C {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 00 */ be_uint64_t hardware_id = 0;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t unused1 = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
@@ -1819,7 +1819,7 @@ struct C_Login_DC_PC_GC_9D {
|
||||
// other bytes are all zeroes.
|
||||
// - V3: the hardware ID is all zeroes.
|
||||
// On the client, this is actually an array of 8 bytes, but we treat it as a single integer for simplicity.
|
||||
/* 08 */ be_uint64_t hardware_id;
|
||||
/* 08 */ be_uint64_t hardware_id = 0;
|
||||
/* 10 */ le_uint32_t sub_version = 0;
|
||||
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
|
||||
/* 15 */ Language language = Language::JAPANESE;
|
||||
@@ -5231,7 +5231,7 @@ struct G_UpdateEntityStat_6x9A {
|
||||
// Used in battle mode if the rules specify that techniques should level up upon character death.
|
||||
|
||||
struct G_LevelUpAllTechniques_6x9B {
|
||||
G_UnusedHeader header;
|
||||
G_ClientIDHeader header;
|
||||
uint8_t num_levels = 0;
|
||||
parray<uint8_t, 3> unused;
|
||||
} __packed_ws__(G_LevelUpAllTechniques_6x9B, 8);
|
||||
|
||||
@@ -128,7 +128,9 @@ asio::awaitable<void> DownloadSession::run() {
|
||||
this->language,
|
||||
netloc_str,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END);
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
|
||||
false,
|
||||
false);
|
||||
this->log.info_f("Server channel connected");
|
||||
|
||||
while (this->channel->connected()) {
|
||||
@@ -529,7 +531,9 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
||||
this->language,
|
||||
netloc_str,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END);
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
|
||||
false,
|
||||
false);
|
||||
this->log.info_f("Server channel connected");
|
||||
break;
|
||||
}
|
||||
|
||||
+9
-9
@@ -26,8 +26,8 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::AL_RAPPY, EP1 | RARE, 0x06, 0x06, {0x19}, {0x19}, {0x19}, {0x19}, "AL_RAPPY", "Al Rappy", "Pal Rappy"},
|
||||
{EnemyType::ASTARK, EP4, 0x58, 0x41, {0x09}, {0x0B, 0x0A, 0x0C}, {0x09}, {0x09}, "ASTARK", "Astark", nullptr},
|
||||
{EnemyType::BA_BOOTA, EP4, 0x62, 0x4F, {0x03}, {0x03, 0x02, 0x04}, {0x03}, {0x03}, "BA_BOOTA", "Ba Boota", nullptr},
|
||||
{EnemyType::BARBA_RAY, EP2 | BOSS, 0x49, 0x49, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "BARBA_RAY", "Barba Ray", nullptr},
|
||||
{EnemyType::BARBA_RAY_JOINT, EP2 | BOSS, 0x49, 0x49, {0x10}, {0x0F}, {0x10}, {}, "BARBA_RAY_JOINT", "Barba Ray (joint)", nullptr},
|
||||
{EnemyType::BARBA_RAY, EP2 | BOSS, 0x49, 0x49, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "BARBA_RAY", "Barba Ray", nullptr},
|
||||
{EnemyType::BARBAROUS_WOLF, EP1 | EP2, 0x08, 0x08, {0x03}, {0x03}, {0x03}, {0x03}, "BARBAROUS_WOLF", "Barbarous Wolf", "Gulgus-gue"},
|
||||
{EnemyType::BEE_L, EP1 | EP2, NONE, NONE, {0x0C}, {0x0C}, {0x0C}, {0x0C}, "BEE_L", "Bee L", "Gee L"},
|
||||
{EnemyType::BEE_R, EP1 | EP2, NONE, NONE, {0x0B}, {0x0B}, {0x0B}, {0x0B}, "BEE_R", "Bee R", "Gee R"},
|
||||
@@ -35,8 +35,8 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::BOOTA, EP4, 0x60, 0x4D, {0x00}, {0x00, 0x02, 0x04}, {0x00}, {0x00}, "BOOTA", "Boota", nullptr},
|
||||
{EnemyType::BULCLAW, EP1, 0x28, 0x28, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULCLAW", "Bulclaw", nullptr},
|
||||
{EnemyType::BULK, EP1, 0x27, 0x27, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULK", "Bulk", nullptr},
|
||||
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
|
||||
{EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x1C, {0x08}, {0x08}, {0x08}, {0x08}, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"},
|
||||
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
|
||||
{EnemyType::CANANE, EP1, 0x1D, 0x1D, {0x09}, {0x09}, {0x09}, {0x09}, "CANANE", "Canane", "Canune"},
|
||||
{EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x24, {0x0D}, {0x0D}, {0x0D}, {0x0A, 0x0D}, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"},
|
||||
{EnemyType::CHAOS_SORCERER, EP1 | EP2, 0x1F, 0x1F, {0x0A}, {0x0A}, {0x0A}, {}, "CHAOS_SORCERER", "Chaos Sorceror", "Gran Sorceror"},
|
||||
@@ -45,12 +45,12 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::DARK_FALZ_1, EP1 | BOSS, NONE, NONE, {0x36}, {0x36}, {0x36}, {0x36, 0x39}, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr},
|
||||
{EnemyType::DARK_FALZ_2, EP1 | BOSS, 0x2F, 0x2F, {0x37}, {0x37}, {0x37}, {0x37}, "DARK_FALZ_2", "Dark Falz (phase 2)", nullptr},
|
||||
{EnemyType::DARK_FALZ_3, EP1 | BOSS, 0x2F, 0x2F, {0x38}, {0x38}, {0x38}, {0x38}, "DARK_FALZ_3", "Dark Falz (phase 3)", nullptr},
|
||||
{EnemyType::DARK_GUNNER, EP1, 0x22, 0x22, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DARK_GUNNER", "Dark Gunner", nullptr},
|
||||
{EnemyType::DARK_GUNNER_CONTROL, EP1, NONE, NONE, {}, {}, {}, {}, "DARK_GUNNER_CONTROL", "Dark Gunner (control)", nullptr},
|
||||
{EnemyType::DARK_GUNNER, EP1, 0x22, 0x22, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DARK_GUNNER", "Dark Gunner", nullptr},
|
||||
{EnemyType::DARVANT, EP1, NONE, NONE, {0x35}, {0x35}, {0x35}, {0x35, 0x39}, "DARVANT", "Darvant", nullptr},
|
||||
{EnemyType::DE_ROL_LE, EP1 | BOSS, 0x2D, 0x2D, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"},
|
||||
{EnemyType::DE_ROL_LE_BODY, EP1 | BOSS, NONE, NONE, {0x10}, {0x0F}, {0x10}, {0x0F}, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"},
|
||||
{EnemyType::DE_ROL_LE_MINE, EP1 | BOSS, NONE, NONE, {0x11}, {0x0F}, {0x11}, {0x0F}, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"},
|
||||
{EnemyType::DE_ROL_LE, EP1 | BOSS, 0x2D, 0x2D, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"},
|
||||
{EnemyType::DEATH_GUNNER, EP1, 0x23, 0x23, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DEATH_GUNNER", "Death Gunner", nullptr},
|
||||
{EnemyType::DEL_LILY, EP2, 0x53, 0x53, {0x25}, {0x25}, {0x25}, {0x25}, "DEL_LILY", "Del Lily", nullptr},
|
||||
{EnemyType::DEL_RAPPY_CRATER, EP4, 0x69, 0x57, {0x06}, {0x06}, {0x06}, {0x06}, "DEL_RAPPY_CRATER", "Del Rappy (crater)", nullptr},
|
||||
@@ -61,8 +61,8 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x29, {0x53}, {0x5A}, {0x52}, {0x52}, "DIMENIAN", "Dimenian", "Arlan"},
|
||||
{EnemyType::DOLMDARL, EP2, 0x41, 0x41, {0x50}, {0x55}, {0x4F}, {0x4F}, "DOLMDARL", "Dolmdarl", nullptr},
|
||||
{EnemyType::DOLMOLM, EP2, 0x40, 0x40, {0x4F}, {0x54}, {0x4E}, {0x4E}, "DOLMOLM", "Dolmolm", nullptr},
|
||||
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
|
||||
{EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x64, 0x51, {0x10}, {0x10}, {0x10}, {0x10}, "DORPHON_ECLAIR", "Dorphon Eclair", nullptr},
|
||||
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
|
||||
{EnemyType::DRAGON, EP1 | BOSS, 0x2C, 0x2C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, {0x11}, "DRAGON", "Dragon", "Sil Dragon"},
|
||||
{EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x18, {0x1B}, {0x1B}, {0x1B}, {0x1B}, "DUBCHIC", "Dubchic", "Dubchich"},
|
||||
{EnemyType::DUBWITCH, EP1 | EP2, NONE, NONE, {}, {}, {}, {}, "DUBWITCH", "Dubwitch", "Duvuik"},
|
||||
@@ -81,8 +81,8 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::GIRTABLULU, EP4, 0x5D, 0x48, {0x1F}, {0x1F}, {0x1F}, {0x1F}, "GIRTABLULU", "Girtablulu", nullptr},
|
||||
{EnemyType::GOBOOMA, EP1, 0x0A, 0x0A, {0x4C}, {0x4F}, {0x4B}, {0x4B}, "GOBOOMA", "Gobooma", "Barble"},
|
||||
{EnemyType::GOL_DRAGON, EP2 | BOSS, 0x4C, 0x4C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, {0x11, 0x12, 0x13}, "GOL_DRAGON", "Gol Dragon", nullptr},
|
||||
{EnemyType::GORAN, EP4, 0x65, 0x52, {0x11}, {0x11, 0x14}, {0x11}, {0x11}, "GORAN", "Goran", nullptr},
|
||||
{EnemyType::GORAN_DETONATOR, EP4, 0x66, 0x53, {0x13}, {0x13, 0x16}, {0x13}, {0x13}, "GORAN_DETONATOR", "Goran Detonator", nullptr},
|
||||
{EnemyType::GORAN, EP4, 0x65, 0x52, {0x11}, {0x11, 0x14}, {0x11}, {0x11}, "GORAN", "Goran", nullptr},
|
||||
{EnemyType::GRASS_ASSASSIN, EP1 | EP2, 0x0C, 0x0C, {0x4E}, {0x51, 0x52, 0x53}, {0x4D}, {0x4D}, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"},
|
||||
{EnemyType::GUIL_SHARK, EP1, 0x12, 0x12, {0x51}, {0x56}, {0x50}, {0x50}, "GUIL_SHARK", "Guil Shark", "Melqueek"},
|
||||
{EnemyType::HALLO_RAPPY, EP2, 0x50, 0x50, {0x19}, {0x19}, {0x19}, {0x19}, "HALLO_RAPPY", "Hallo Rappy", nullptr},
|
||||
@@ -90,8 +90,8 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::HILDEBEAR, EP1 | EP2, 0x01, 0x01, {0x49}, {0x48, 0x49, 0x4A}, {0x48}, {0x48}, "HILDEBEAR", "Hildebear", "Hildelt"},
|
||||
{EnemyType::HILDEBLUE, EP1 | EP2 | RARE, 0x02, 0x02, {0x4A}, {0x4B, 0x4C, 0x4D}, {0x49}, {0x49}, "HILDEBLUE", "Hildeblue", "Hildetorr"},
|
||||
{EnemyType::ILL_GILL, EP2, 0x52, 0x52, {0x26}, {0x26, 0x27, 0x28, 0x29}, {0x26}, {0x26}, "ILL_GILL", "Ill Gill", nullptr},
|
||||
{EnemyType::KONDRIEU, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, "KONDRIEU", "Kondrieu", nullptr},
|
||||
{EnemyType::KONDRIEU_SPINNER, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x29, 0x2B}, {0x29, 0x2B}, {0x29, 0x2B}, {0x29, 0x2B}, "KONDRIEU_SPINNER", "Kondrieu (spinner)", nullptr},
|
||||
{EnemyType::KONDRIEU, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, "KONDRIEU", "Kondrieu", nullptr},
|
||||
{EnemyType::LA_DIMENIAN, EP1 | EP2, 0x2A, 0x2A, {0x54}, {0x5B}, {0x53}, {0x53}, "LA_DIMENIAN", "La Dimenian", "Merlan"},
|
||||
{EnemyType::LOVE_RAPPY, EP2, 0x33, 0x33, {0x19}, {0x19}, {0x19}, {0x19}, "LOVE_RAPPY", "Love Rappy", nullptr},
|
||||
{EnemyType::MERICARAND, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, {0x3A}, "MERICARAND", "Mericarand", nullptr},
|
||||
@@ -122,16 +122,16 @@ static const vector<EnemyTypeDefinition> type_defs{
|
||||
{EnemyType::RAG_RAPPY, EP1 | EP2, 0x05, 0x05, {0x18}, {0x18}, {0x18}, {0x18}, "RAG_RAPPY", "Rag Rappy", "El Rappy"},
|
||||
{EnemyType::RECOBOX, EP2, 0x43, 0x43, {0x41}, {0x41}, {0x41}, {0x41}, "RECOBOX", "Recobox", nullptr},
|
||||
{EnemyType::RECON, EP2, 0x44, 0x44, {0x42}, {0x42}, {0x42}, {0x42}, "RECON", "Recon", nullptr},
|
||||
{EnemyType::SAINT_MILION, EP4 | BOSS, 0x6A, 0x59, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, "SAINT_MILION", "Saint-Milion", nullptr},
|
||||
{EnemyType::SAINT_MILION_SPINNER, EP4 | BOSS, 0x6A, 0x59, {0x21, 0x23}, {0x21, 0x23}, {0x21, 0x23}, {0x21, 0x23}, "SAINT_MILION_SPINNER", "Saint-Milion (spinner)", nullptr},
|
||||
{EnemyType::SAINT_MILION, EP4 | BOSS, 0x6A, 0x59, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, "SAINT_MILION", "Saint-Milion", nullptr},
|
||||
{EnemyType::SAINT_RAPPY, EP2, 0x4F, 0x4F, {0x19}, {0x19}, {0x19}, {0x19}, "SAINT_RAPPY", "Saint Rappy", nullptr},
|
||||
{EnemyType::SAND_RAPPY_CRATER, EP4, 0x68, 0x55, {0x05}, {0x05}, {0x05}, {0x05}, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", nullptr},
|
||||
{EnemyType::SAND_RAPPY_DESERT, EP4, 0x68, 0x56, {0x17}, {0x17}, {0x17}, {0x17}, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", nullptr},
|
||||
{EnemyType::SATELLITE_LIZARD_CRATER, EP4, 0x5A, 0x44, {0x0D}, {0x0D}, {0x0D}, {0x0D}, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", nullptr},
|
||||
{EnemyType::SATELLITE_LIZARD_DESERT, EP4, 0x5A, 0x45, {0x1D}, {0x1D}, {0x1D}, {0x1D}, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", nullptr},
|
||||
{EnemyType::SAVAGE_WOLF, EP1 | EP2, 0x07, 0x07, {0x02}, {0x02}, {0x02}, {0x02}, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"},
|
||||
{EnemyType::SHAMBERTIN, EP4 | BOSS, 0x6B, 0x5A, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, "SHAMBERTIN", "Shambertin", nullptr},
|
||||
{EnemyType::SHAMBERTIN_SPINNER, EP4 | BOSS, 0x6B, 0x5A, {0x25, 0x27}, {0x25, 0x27}, {0x25, 0x27}, {0x25, 0x27}, "SHAMBERTIN_SPINNER", "Shambertin (spinner)", nullptr},
|
||||
{EnemyType::SHAMBERTIN, EP4 | BOSS, 0x6B, 0x5A, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, "SHAMBERTIN", "Shambertin", nullptr},
|
||||
{EnemyType::SINOW_BEAT, EP1, 0x1A, 0x1A, {0x06}, {0x06}, {0x06}, {0x06}, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"},
|
||||
{EnemyType::SINOW_BERILL, EP2, 0x3E, 0x3E, {0x06}, {0x06}, {0x06}, {0x06}, "SINOW_BERILL", "Sinow Berill", nullptr},
|
||||
{EnemyType::SINOW_GOLD, EP1, 0x1B, 0x1B, {0x13}, {0x47}, {0x13}, {0x10}, "SINOW_GOLD", "Sinow Gold", "Sinow Red"},
|
||||
|
||||
+10
-10
@@ -21,8 +21,8 @@ enum class EnemyType : uint8_t {
|
||||
AL_RAPPY,
|
||||
ASTARK,
|
||||
BA_BOOTA,
|
||||
BARBA_RAY,
|
||||
BARBA_RAY_JOINT,
|
||||
BARBA_RAY,
|
||||
BARBAROUS_WOLF,
|
||||
BEE_L,
|
||||
BEE_R,
|
||||
@@ -30,8 +30,8 @@ enum class EnemyType : uint8_t {
|
||||
BOOTA,
|
||||
BULCLAW,
|
||||
BULK,
|
||||
CANADINE,
|
||||
CANADINE_GROUP,
|
||||
CANADINE,
|
||||
CANANE,
|
||||
CHAOS_BRINGER,
|
||||
CHAOS_SORCERER,
|
||||
@@ -40,12 +40,12 @@ enum class EnemyType : uint8_t {
|
||||
DARK_FALZ_1,
|
||||
DARK_FALZ_2,
|
||||
DARK_FALZ_3,
|
||||
DARK_GUNNER,
|
||||
DARK_GUNNER_CONTROL,
|
||||
DARK_GUNNER,
|
||||
DARVANT,
|
||||
DE_ROL_LE,
|
||||
DE_ROL_LE_BODY,
|
||||
DE_ROL_LE_MINE,
|
||||
DE_ROL_LE,
|
||||
DEATH_GUNNER,
|
||||
DEL_LILY,
|
||||
DEL_RAPPY_CRATER,
|
||||
@@ -56,8 +56,8 @@ enum class EnemyType : uint8_t {
|
||||
DIMENIAN,
|
||||
DOLMDARL,
|
||||
DOLMOLM,
|
||||
DORPHON,
|
||||
DORPHON_ECLAIR,
|
||||
DORPHON,
|
||||
DRAGON,
|
||||
DUBCHIC,
|
||||
DUBWITCH, // Has no entry in battle params
|
||||
@@ -76,8 +76,8 @@ enum class EnemyType : uint8_t {
|
||||
GIRTABLULU,
|
||||
GOBOOMA,
|
||||
GOL_DRAGON,
|
||||
GORAN,
|
||||
GORAN_DETONATOR,
|
||||
GORAN,
|
||||
GRASS_ASSASSIN,
|
||||
GUIL_SHARK,
|
||||
HALLO_RAPPY,
|
||||
@@ -85,8 +85,8 @@ enum class EnemyType : uint8_t {
|
||||
HILDEBEAR,
|
||||
HILDEBLUE,
|
||||
ILL_GILL,
|
||||
KONDRIEU,
|
||||
KONDRIEU_SPINNER,
|
||||
KONDRIEU,
|
||||
LA_DIMENIAN,
|
||||
LOVE_RAPPY,
|
||||
MERICARAND,
|
||||
@@ -111,22 +111,22 @@ enum class EnemyType : uint8_t {
|
||||
PAZUZU_DESERT,
|
||||
PIG_RAY,
|
||||
POFUILLY_SLIME,
|
||||
POUILLY_SLIME,
|
||||
POISON_LILY,
|
||||
POUILLY_SLIME,
|
||||
PYRO_GORAN,
|
||||
RAG_RAPPY,
|
||||
RECOBOX,
|
||||
RECON,
|
||||
SAINT_MILION,
|
||||
SAINT_MILION_SPINNER,
|
||||
SAINT_MILION,
|
||||
SAINT_RAPPY,
|
||||
SAND_RAPPY_CRATER,
|
||||
SAND_RAPPY_DESERT,
|
||||
SATELLITE_LIZARD_CRATER,
|
||||
SATELLITE_LIZARD_DESERT,
|
||||
SAVAGE_WOLF,
|
||||
SHAMBERTIN,
|
||||
SHAMBERTIN_SPINNER,
|
||||
SHAMBERTIN,
|
||||
SINOW_BEAT,
|
||||
SINOW_BERILL,
|
||||
SINOW_GOLD,
|
||||
|
||||
@@ -10,9 +10,8 @@ using namespace std;
|
||||
namespace Episode3 {
|
||||
|
||||
void BattleRecord::PlayerEntry::print(FILE* stream) const {
|
||||
// TODO: Format this nicely somehow. Maybe factor out the functions in
|
||||
// QuestScript that format some of these structures
|
||||
phosg::print_data(stream, this, sizeof(*this), 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
// TODO: Format this nicely somehow. Maybe factor out the functions in QuestScript that format some of these structs
|
||||
phosg::print_data(stream, this, sizeof(*this), 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
}
|
||||
|
||||
BattleRecord::Event::Event(phosg::StringReader& r) {
|
||||
@@ -103,23 +102,23 @@ void BattleRecord::Event::print(FILE* stream) const {
|
||||
break;
|
||||
case Type::BATTLE_COMMAND:
|
||||
phosg::fwrite_fmt(stream, "BATTLE_COMMAND\n");
|
||||
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
break;
|
||||
case Type::GAME_COMMAND:
|
||||
phosg::fwrite_fmt(stream, "GAME_COMMAND\n");
|
||||
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
break;
|
||||
case Type::EP3_GAME_COMMAND:
|
||||
phosg::fwrite_fmt(stream, "EP3_GAME_COMMAND\n");
|
||||
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
break;
|
||||
case Type::CHAT_MESSAGE:
|
||||
phosg::fwrite_fmt(stream, "CHAT_MESSAGE {:08X}\n", this->guild_card_number);
|
||||
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
break;
|
||||
case Type::SERVER_DATA_COMMAND:
|
||||
phosg::fwrite_fmt(stream, "SERVER_DATA_COMMAND\n");
|
||||
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stream, this->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("unknown event type in battle record");
|
||||
|
||||
@@ -261,7 +261,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
|
||||
|
||||
} else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) &&
|
||||
this->log().info_f("Generated command")) {
|
||||
phosg::print_data(stderr, data, size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, data, size, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -132,7 +132,9 @@ shared_ptr<Client> GameServer::create_client(
|
||||
Language::ENGLISH,
|
||||
"",
|
||||
phosg::TerminalFormat::FG_YELLOW,
|
||||
phosg::TerminalFormat::FG_GREEN);
|
||||
phosg::TerminalFormat::FG_GREEN,
|
||||
this->state->censor_credentials,
|
||||
false);
|
||||
auto c = make_shared<Client>(this->shared_from_this(), channel, listen_sock->behavior);
|
||||
c->listener_port = listen_sock->endpoint.port();
|
||||
this->log.info_f("Client connected: C-{:X} via {}", c->id, listen_sock->name);
|
||||
@@ -195,4 +197,11 @@ asio::awaitable<void> GameServer::destroy_client(std::shared_ptr<Client> c) {
|
||||
c->log.warning_f("Disconnect hook {} failed: {}", h_it.first, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (c->login) {
|
||||
auto it = this->state->client_for_account.find(c->login->account->account_id);
|
||||
if ((it != this->state->client_for_account.end()) && (it->second == c)) {
|
||||
this->state->client_for_account.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -15,9 +15,7 @@ struct GameServerSocket : ServerSocket {
|
||||
ServerBehavior behavior;
|
||||
};
|
||||
|
||||
class GameServer
|
||||
: public Server<Client, GameServerSocket>,
|
||||
public std::enable_shared_from_this<GameServer> {
|
||||
class GameServer : public Server<Client, GameServerSocket>, public std::enable_shared_from_this<GameServer> {
|
||||
public:
|
||||
GameServer() = delete;
|
||||
GameServer(const GameServer&) = delete;
|
||||
|
||||
+15
-3
@@ -165,8 +165,10 @@ IPSSChannel::IPSSChannel(
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color),
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials)
|
||||
: Channel(version, language, name, terminal_send_color, terminal_recv_color, censor_received_credentials, censor_sent_credentials),
|
||||
sim(sim),
|
||||
ipss_client(ipss_client),
|
||||
tcp_conn(tcp_conn),
|
||||
@@ -1379,7 +1381,17 @@ asio::awaitable<void> IPStackSimulator::open_server_connection(
|
||||
}
|
||||
const auto& port_config = port_config_it->second;
|
||||
|
||||
conn->server_channel = make_shared<IPSSChannel>(this->shared_from_this(), c, conn, port_config->version, Language::ENGLISH);
|
||||
conn->server_channel = make_shared<IPSSChannel>(
|
||||
this->shared_from_this(),
|
||||
c,
|
||||
conn,
|
||||
port_config->version,
|
||||
Language::ENGLISH,
|
||||
"",
|
||||
phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat::END,
|
||||
false,
|
||||
this->state->censor_credentials);
|
||||
|
||||
if (!this->state->game_server.get()) {
|
||||
this->log.error_f("No server available for TCP connection {}", conn_str);
|
||||
|
||||
+10
-13
@@ -94,10 +94,8 @@ struct IPSSClient : std::enable_shared_from_this<IPSSClient> {
|
||||
void reschedule_idle_timeout();
|
||||
};
|
||||
|
||||
// IPSSChannel provides an "unwrapped" connection to the rest of the server. It
|
||||
// implements the Channel interface and can be used in place of an
|
||||
// SocketChannel, so the rest of the server doesn't have to know about
|
||||
// IPStackSimulator.
|
||||
// IPSSChannel provides an "unwrapped" connection to the rest of the server. It implements the Channel interface and
|
||||
// can be used in place of an SocketChannel, so the rest of the server doesn't have to know about IPStackSimulator.
|
||||
class IPSSChannel : public Channel {
|
||||
public:
|
||||
std::shared_ptr<IPStackSimulator> sim;
|
||||
@@ -110,18 +108,19 @@ public:
|
||||
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
||||
Version version,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color,
|
||||
bool censor_received_credentials,
|
||||
bool censor_sent_credentials);
|
||||
|
||||
virtual std::string default_name() const;
|
||||
|
||||
virtual bool connected() const;
|
||||
virtual void disconnect();
|
||||
|
||||
// Adds inbound data, which will then be available via recv_raw(). This
|
||||
// function is called by IPStackSimulator to forward "unwrapped" data to
|
||||
// the game/proxy servers.
|
||||
// Adds inbound data, which will then be available via recv_raw(). This function is called by IPStackSimulator to
|
||||
// forward "unwrapped" data to the game/proxy servers.
|
||||
void add_inbound_data(const void* data, size_t size);
|
||||
|
||||
virtual void send_raw(std::string&& data);
|
||||
@@ -134,9 +133,7 @@ private:
|
||||
size_t recv_buf_size = 0;
|
||||
};
|
||||
|
||||
class IPStackSimulator
|
||||
: public Server<IPSSClient, IPSSSocket>,
|
||||
public std::enable_shared_from_this<IPStackSimulator> {
|
||||
class IPStackSimulator : public Server<IPSSClient, IPSSSocket>, public std::enable_shared_from_this<IPStackSimulator> {
|
||||
public:
|
||||
IPStackSimulator(std::shared_ptr<ServerState> state);
|
||||
~IPStackSimulator() = default;
|
||||
|
||||
@@ -832,7 +832,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
w.type,
|
||||
w.skin,
|
||||
w.team_points,
|
||||
w.class_flags,
|
||||
w.usability_flags,
|
||||
w.atp_min,
|
||||
w.atp_max,
|
||||
w.atp_required,
|
||||
@@ -910,7 +910,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
a.evp,
|
||||
a.block_particle,
|
||||
a.block_effect,
|
||||
a.class_flags,
|
||||
a.usability_flags,
|
||||
static_cast<uint8_t>(a.required_level + 1),
|
||||
a.efr,
|
||||
a.eth,
|
||||
@@ -1001,7 +1001,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
m.on_low_hp_flag,
|
||||
m.on_death_flag,
|
||||
m.on_boss_flag,
|
||||
m.class_flags,
|
||||
m.usability_flags,
|
||||
divisor_str,
|
||||
name);
|
||||
}
|
||||
|
||||
+21
-21
@@ -143,7 +143,7 @@ phosg::JSON ItemParameterTable::ItemBase::json() const {
|
||||
ItemParameterTable::Weapon ItemParameterTable::Weapon::from_json(const phosg::JSON& json) {
|
||||
ItemParameterTable::Weapon ret;
|
||||
ret.parse_base_from_json(json);
|
||||
ret.class_flags = json.get_int("ClassFlags");
|
||||
ret.usability_flags = json.get_int("UsabilityFlags");
|
||||
ret.atp_min = json.get_int("ATPMin");
|
||||
ret.atp_max = json.get_int("ATPMax");
|
||||
ret.atp_required = json.get_int("ATPRequired");
|
||||
@@ -173,7 +173,7 @@ ItemParameterTable::Weapon ItemParameterTable::Weapon::from_json(const phosg::JS
|
||||
}
|
||||
phosg::JSON ItemParameterTable::Weapon::json() const {
|
||||
phosg::JSON ret = this->ItemBase::json();
|
||||
ret.emplace("ClassFlags", this->class_flags);
|
||||
ret.emplace("UsabilityFlags", this->usability_flags);
|
||||
ret.emplace("ATPMin", this->atp_min);
|
||||
ret.emplace("ATPMax", this->atp_max);
|
||||
ret.emplace("ATPRequired", this->atp_required);
|
||||
@@ -206,7 +206,7 @@ ItemParameterTable::ArmorOrShield ItemParameterTable::ArmorOrShield::from_json(c
|
||||
ret.evp = json.get_int("EVP");
|
||||
ret.block_particle = json.get_int("BlockParticle");
|
||||
ret.block_effect = json.get_int("BlockEffect");
|
||||
ret.class_flags = json.get_int("ClassFlags");
|
||||
ret.usability_flags = json.get_int("UsabilityFlags");
|
||||
ret.required_level = json.get_int("RequiredLevel");
|
||||
ret.efr = json.get_int("EFR");
|
||||
ret.eth = json.get_int("ETH");
|
||||
@@ -227,7 +227,7 @@ phosg::JSON ItemParameterTable::ArmorOrShield::json() const {
|
||||
ret.emplace("EVP", this->evp);
|
||||
ret.emplace("BlockParticle", this->block_particle);
|
||||
ret.emplace("BlockEffect", this->block_effect);
|
||||
ret.emplace("ClassFlags", this->class_flags);
|
||||
ret.emplace("UsabilityFlags", this->usability_flags);
|
||||
ret.emplace("RequiredLevel", this->required_level);
|
||||
ret.emplace("EFR", this->efr);
|
||||
ret.emplace("ETH", this->eth);
|
||||
@@ -273,7 +273,7 @@ ItemParameterTable::Mag ItemParameterTable::Mag::from_json(const phosg::JSON& js
|
||||
ret.on_low_hp_flag = json.get_int("OnLowHPFlag");
|
||||
ret.on_death_flag = json.get_int("OnDeathFlag");
|
||||
ret.on_boss_flag = json.get_int("OnBossFlag");
|
||||
ret.class_flags = json.get_int("ClassFlags");
|
||||
ret.usability_flags = json.get_int("UsabilityFlags");
|
||||
return ret;
|
||||
}
|
||||
phosg::JSON ItemParameterTable::Mag::json() const {
|
||||
@@ -289,7 +289,7 @@ phosg::JSON ItemParameterTable::Mag::json() const {
|
||||
ret.emplace("OnLowHPFlag", this->on_low_hp_flag);
|
||||
ret.emplace("OnDeathFlag", this->on_death_flag);
|
||||
ret.emplace("OnBossFlag", this->on_boss_flag);
|
||||
ret.emplace("ClassFlags", this->class_flags);
|
||||
ret.emplace("UsabilityFlags", this->usability_flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1223,7 +1223,7 @@ struct ItemBaseV4 : ItemBaseV3T<false> {
|
||||
|
||||
struct WeaponDCProtos {
|
||||
/* 00 */ ItemBaseV2T<false> base;
|
||||
/* 04 */ le_uint16_t class_flags = 0;
|
||||
/* 04 */ le_uint16_t usability_flags = 0;
|
||||
/* 06 */ le_uint16_t atp_min = 0;
|
||||
/* 08 */ le_uint16_t atp_max = 0;
|
||||
/* 0A */ le_uint16_t atp_required = 0;
|
||||
@@ -1237,7 +1237,7 @@ struct WeaponDCProtos {
|
||||
WeaponDCProtos() = default;
|
||||
WeaponDCProtos(const ItemParameterTable::Weapon& w)
|
||||
: base(w),
|
||||
class_flags(w.class_flags),
|
||||
usability_flags(w.usability_flags),
|
||||
atp_min(w.atp_min),
|
||||
atp_max(w.atp_max),
|
||||
atp_required(w.atp_required),
|
||||
@@ -1250,7 +1250,7 @@ struct WeaponDCProtos {
|
||||
operator ItemParameterTable::Weapon() const {
|
||||
ItemParameterTable::Weapon ret;
|
||||
this->base.parse_into(ret);
|
||||
ret.class_flags = this->class_flags;
|
||||
ret.usability_flags = this->usability_flags;
|
||||
ret.atp_min = this->atp_min;
|
||||
ret.atp_max = this->atp_max;
|
||||
ret.atp_required = this->atp_required;
|
||||
@@ -1282,7 +1282,7 @@ struct WeaponV1V2 : WeaponDCProtos {
|
||||
template <bool BE>
|
||||
struct WeaponGCNTET {
|
||||
/* 00 */ ItemBaseV3T<BE> base;
|
||||
/* 08 */ U16T<BE> class_flags = 0;
|
||||
/* 08 */ U16T<BE> usability_flags = 0;
|
||||
/* 0A */ U16T<BE> atp_min = 0;
|
||||
/* 0C */ U16T<BE> atp_max = 0;
|
||||
/* 0E */ U16T<BE> atp_required = 0;
|
||||
@@ -1305,7 +1305,7 @@ struct WeaponGCNTET {
|
||||
WeaponGCNTET() = default;
|
||||
WeaponGCNTET(const ItemParameterTable::Weapon& w)
|
||||
: base(w),
|
||||
class_flags(w.class_flags),
|
||||
usability_flags(w.usability_flags),
|
||||
atp_min(w.atp_min),
|
||||
atp_max(w.atp_max),
|
||||
atp_required(w.atp_required),
|
||||
@@ -1327,7 +1327,7 @@ struct WeaponGCNTET {
|
||||
operator ItemParameterTable::Weapon() const {
|
||||
ItemParameterTable::Weapon ret;
|
||||
this->base.parse_into(ret);
|
||||
ret.class_flags = this->class_flags;
|
||||
ret.usability_flags = this->usability_flags;
|
||||
ret.atp_min = this->atp_min;
|
||||
ret.atp_max = this->atp_max;
|
||||
ret.atp_required = this->atp_required;
|
||||
@@ -1379,7 +1379,7 @@ using WeaponXB = WeaponV3T<false>;
|
||||
|
||||
struct WeaponV4 {
|
||||
/* 00 */ ItemBaseV4 base;
|
||||
/* 0C */ le_uint16_t class_flags = 0x00FF;
|
||||
/* 0C */ le_uint16_t usability_flags = 0x00FF;
|
||||
/* 0E */ le_uint16_t atp_min = 0;
|
||||
/* 10 */ le_uint16_t atp_max = 0;
|
||||
/* 12 */ le_uint16_t atp_required = 0;
|
||||
@@ -1406,7 +1406,7 @@ struct WeaponV4 {
|
||||
WeaponV4() = default;
|
||||
WeaponV4(const ItemParameterTable::Weapon& w)
|
||||
: base(w),
|
||||
class_flags(w.class_flags),
|
||||
usability_flags(w.usability_flags),
|
||||
atp_min(w.atp_min),
|
||||
atp_max(w.atp_max),
|
||||
atp_required(w.atp_required),
|
||||
@@ -1432,7 +1432,7 @@ struct WeaponV4 {
|
||||
operator ItemParameterTable::Weapon() const {
|
||||
ItemParameterTable::Weapon ret;
|
||||
this->base.parse_into(ret);
|
||||
ret.class_flags = this->class_flags;
|
||||
ret.usability_flags = this->usability_flags;
|
||||
ret.atp_min = this->atp_min;
|
||||
ret.atp_max = this->atp_max;
|
||||
ret.atp_required = this->atp_required;
|
||||
@@ -1467,7 +1467,7 @@ struct ArmorOrShieldT {
|
||||
/* 06 */ U16T<BE> evp = 0;
|
||||
/* 08 */ uint8_t block_particle = 0;
|
||||
/* 09 */ uint8_t block_effect = 0;
|
||||
/* 0A */ U16T<BE> class_flags = 0x00FF;
|
||||
/* 0A */ U16T<BE> usability_flags = 0x00FF;
|
||||
/* 0C */ uint8_t required_level = 0;
|
||||
/* 0D */ uint8_t efr = 0;
|
||||
/* 0E */ uint8_t eth = 0;
|
||||
@@ -1484,7 +1484,7 @@ struct ArmorOrShieldT {
|
||||
evp(as.evp),
|
||||
block_particle(as.block_particle),
|
||||
block_effect(as.block_effect),
|
||||
class_flags(as.class_flags),
|
||||
usability_flags(as.usability_flags),
|
||||
required_level(as.required_level),
|
||||
efr(as.efr),
|
||||
eth(as.eth),
|
||||
@@ -1500,7 +1500,7 @@ struct ArmorOrShieldT {
|
||||
ret.evp = this->evp;
|
||||
ret.block_particle = this->block_particle;
|
||||
ret.block_effect = this->block_effect;
|
||||
ret.class_flags = this->class_flags;
|
||||
ret.usability_flags = this->usability_flags;
|
||||
ret.required_level = this->required_level;
|
||||
ret.efr = this->efr;
|
||||
ret.eth = this->eth;
|
||||
@@ -1655,13 +1655,13 @@ using MagV1 = MagT<ItemBaseV2T<false>, false>;
|
||||
|
||||
template <typename BaseT, bool BE>
|
||||
struct MagV2V3V4T : MagT<BaseT, BE> {
|
||||
U16T<BE> class_flags = 0x00FF;
|
||||
U16T<BE> usability_flags = 0x00FF;
|
||||
parray<uint8_t, 2> unused = 0;
|
||||
MagV2V3V4T() = default;
|
||||
MagV2V3V4T(const ItemParameterTable::Mag& m) : MagT<BaseT, BE>(m), class_flags(m.class_flags) {}
|
||||
MagV2V3V4T(const ItemParameterTable::Mag& m) : MagT<BaseT, BE>(m), usability_flags(m.usability_flags) {}
|
||||
operator ItemParameterTable::Mag() const {
|
||||
ItemParameterTable::Mag ret = this->MagT<BaseT, BE>::operator ItemParameterTable::Mag();
|
||||
ret.class_flags = this->class_flags;
|
||||
ret.usability_flags = this->usability_flags;
|
||||
return ret;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -56,7 +56,16 @@ public:
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
struct Weapon : ItemBase {
|
||||
uint16_t class_flags = 0;
|
||||
// Bits in usability_flags (to be usable, all bits corresponding to the character's attributes must be set):
|
||||
// 01 = hunter
|
||||
// 02 = ranger
|
||||
// 04 = force
|
||||
// 08 = human
|
||||
// 10 = android
|
||||
// 20 = newman
|
||||
// 40 = male
|
||||
// 80 = female
|
||||
uint16_t usability_flags = 0;
|
||||
uint16_t atp_min = 0;
|
||||
uint16_t atp_max = 0;
|
||||
uint16_t atp_required = 0;
|
||||
@@ -95,7 +104,7 @@ public:
|
||||
uint16_t evp = 0;
|
||||
uint8_t block_particle = 0;
|
||||
uint8_t block_effect = 0;
|
||||
uint16_t class_flags = 0x00FF;
|
||||
uint16_t usability_flags = 0x00FF; // See Weapon::usability_flags for details
|
||||
uint8_t required_level = 0;
|
||||
uint8_t efr = 0;
|
||||
uint8_t eth = 0;
|
||||
@@ -154,7 +163,7 @@ public:
|
||||
uint8_t on_low_hp_flag = 0;
|
||||
uint8_t on_death_flag = 0;
|
||||
uint8_t on_boss_flag = 0;
|
||||
uint16_t class_flags = 0x00FF;
|
||||
uint16_t usability_flags = 0x00FF; // See Weapon::usability_flags for details
|
||||
|
||||
static Mag from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
|
||||
+6
-7
@@ -3579,7 +3579,7 @@ Action a_check_quests(
|
||||
phosg::fwritex(stdout, vq->map_file->disassemble(false, vq->meta.version));
|
||||
phosg::log_info_f("... BINDIFF:");
|
||||
phosg::print_binary_diff(
|
||||
stdout, dat.data(), dat.size(), serialized.data(), serialized.size(), isatty(fileno(stdout)), 3);
|
||||
stdout, dat.data(), dat.size(), serialized.data(), serialized.size(), isatty(fileno(stdout)));
|
||||
phosg::log_info_f("... {} {} {} ({}) MAP FAILED",
|
||||
phosg::name_for_enum(vq->meta.version),
|
||||
name_for_language(vq->meta.language),
|
||||
@@ -3821,9 +3821,9 @@ Action a_diff_executables(
|
||||
throw runtime_error("the two files are not the same type of executable, or are neither dol nor xbe");
|
||||
}
|
||||
for (const auto& it : result) {
|
||||
string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataFlags::HEX_ONLY);
|
||||
string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
|
||||
if (show_pre) {
|
||||
string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataFlags::HEX_ONLY);
|
||||
string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
|
||||
phosg::fwrite_fmt(stdout, "{:08X}: {} => {}\n", it.address, a_str, b_str);
|
||||
} else {
|
||||
phosg::fwrite_fmt(stdout, "{:08X} {}\n", it.address, b_str);
|
||||
@@ -3962,7 +3962,7 @@ Action a_replay_ep3_battle_record(
|
||||
}
|
||||
if (output_queue->empty()) {
|
||||
phosg::fwrite_fmt(stderr, "Output queue is empty, but expected battle command:\n");
|
||||
phosg::print_data(stderr, ev.data, 0, nullptr, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII);
|
||||
phosg::print_data(stderr, ev.data, 0, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
|
||||
throw std::runtime_error("Output did not match expectations");
|
||||
}
|
||||
// Hack: don't check the last field in 6xB4x46 since it contains a timestamp on non-NTE
|
||||
@@ -3977,11 +3977,10 @@ Action a_replay_ep3_battle_record(
|
||||
matched = (output_queue->front() == ev.data);
|
||||
}
|
||||
if (!matched) {
|
||||
const void* prev = (ev.data.size() == output_queue->front().size()) ? ev.data.data() : nullptr;
|
||||
phosg::fwrite_fmt(stderr, "Output queue front did not match expected command; expected:\n");
|
||||
phosg::print_data(stderr, ev.data, 0, nullptr, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII);
|
||||
phosg::print_data(stderr, ev.data, 0, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
|
||||
phosg::fwrite_fmt(stderr, "Received:\n");
|
||||
phosg::print_data(stderr, output_queue->front(), 0, prev, phosg::PrintDataFlags::OFFSET_16_BITS | phosg::PrintDataFlags::PRINT_ASCII);
|
||||
phosg::print_data(stderr, output_queue->front(), 0, ev.data, phosg::FormatDataFlags::OFFSET_16_BITS | phosg::FormatDataFlags::PRINT_ASCII);
|
||||
throw std::runtime_error("Output did not match expectations");
|
||||
}
|
||||
output_queue->pop_front();
|
||||
|
||||
+7
-8
@@ -4608,10 +4608,9 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
case 0x0060: // TObjGrass
|
||||
add(EnemyType::GRASS_ASSASSIN);
|
||||
break;
|
||||
case 0x0061: { // TObjEneRe2Flower
|
||||
case 0x0061: // TObjEneRe2Flower
|
||||
add((this->area_for_floor(floor) == 0x23) ? EnemyType::DEL_LILY : EnemyType::POISON_LILY);
|
||||
break;
|
||||
}
|
||||
case 0x0062: // TObjEneNanoDrago
|
||||
add(EnemyType::NANO_DRAGON);
|
||||
break;
|
||||
@@ -6573,7 +6572,7 @@ void MapState::import_object_states_from_sync(
|
||||
throw logic_error("super object link is incorrect");
|
||||
}
|
||||
if (obj_st->game_flags != entry.flags) {
|
||||
this->log.warning_f("({:04X} => K-{:03X}) Game flags from client ({:04X}) do not match game flags from map ({:04X})",
|
||||
this->log.info_f("({:04X} => K-{:03X}) Game flags from client ({:04X}) do not match game flags from map ({:04X})",
|
||||
object_index, obj_st->k_id, entry.flags, obj_st->game_flags);
|
||||
obj_st->game_flags = entry.flags;
|
||||
}
|
||||
@@ -6608,12 +6607,12 @@ void MapState::import_enemy_states_from_sync(Version from_version, const SyncEne
|
||||
// Only set the state if it's not an alias
|
||||
if (ene_st->super_ene == ene) {
|
||||
if (ene_st->game_flags != entry.flags) {
|
||||
this->log.warning_f("({:04X} => E-{:03X}) Game flags from client ({:08X}) do not match game flags from map ({:08X})",
|
||||
this->log.info_f("({:04X} => E-{:03X}) Game flags from client ({:08X}) do not match game flags from map ({:08X})",
|
||||
enemy_index, ene_st->e_id, entry.flags, ene_st->game_flags);
|
||||
ene_st->game_flags = entry.flags;
|
||||
}
|
||||
if (ene_st->total_damage != entry.total_damage) {
|
||||
this->log.warning_f("({:04X} => E-{:03X}) Total damage from client ({}) does not match total damage from map ({})",
|
||||
this->log.info_f("({:04X} => E-{:03X}) Total damage from client ({}) does not match total damage from map ({})",
|
||||
enemy_index, ene_st->e_id, entry.total_damage, ene_st->total_damage);
|
||||
ene_st->total_damage = entry.total_damage;
|
||||
}
|
||||
@@ -6665,7 +6664,7 @@ void MapState::import_flag_states_from_sync(
|
||||
throw logic_error("super object link is incorrect");
|
||||
}
|
||||
if (obj_st->set_flags != set_flags) {
|
||||
this->log.warning_f("({:04X} => K-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
|
||||
this->log.info_f("({:04X} => K-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
|
||||
object_index, obj_st->k_id, set_flags, obj_st->set_flags);
|
||||
obj_st->set_flags = set_flags;
|
||||
}
|
||||
@@ -6701,7 +6700,7 @@ void MapState::import_flag_states_from_sync(
|
||||
throw logic_error("super enemy link is incorrect");
|
||||
}
|
||||
if (ene_st->set_flags != set_flags) {
|
||||
this->log.warning_f("({:04X} => E-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
|
||||
this->log.info_f("({:04X} => E-{:03X}) Set flags from client ({:04X}) do not match set flags from map ({:04X})",
|
||||
enemy_set_index, ene_st->e_id, set_flags, ene_st->set_flags);
|
||||
ene_st->set_flags = set_flags;
|
||||
}
|
||||
@@ -6733,7 +6732,7 @@ void MapState::import_flag_states_from_sync(
|
||||
const auto& ev = entities.events.at(event_index - base_indexes.base_event_index);
|
||||
auto& ev_st = this->event_states.at(fc.base_super_ids.base_event_index + ev->super_id);
|
||||
if (ev_st->flags != flags) {
|
||||
this->log.warning_f("({:04X} => W-{:03X}) Set flags from client ({:04X}) do not match flags from map ({:04X})",
|
||||
this->log.info_f("({:04X} => W-{:03X}) Set flags from client ({:04X}) do not match flags from map ({:04X})",
|
||||
event_index, ev_st->w_id, flags, ev_st->flags);
|
||||
ev_st->flags = flags;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,9 @@ asio::awaitable<void> PatchDownloadSession::run() {
|
||||
Language::ENGLISH,
|
||||
netloc_str,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END);
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END,
|
||||
false,
|
||||
false);
|
||||
this->log.info_f("Server channel connected");
|
||||
|
||||
while (this->channel->connected()) {
|
||||
@@ -267,7 +269,9 @@ asio::awaitable<void> PatchDownloadSession::on_message(Channel::Message& msg) {
|
||||
this->channel->language,
|
||||
netloc_str,
|
||||
this->channel->terminal_send_color,
|
||||
this->channel->terminal_recv_color);
|
||||
this->channel->terminal_recv_color,
|
||||
false,
|
||||
false);
|
||||
this->channel = new_channel;
|
||||
old_channel->disconnect();
|
||||
this->log.info_f("Server channel connected");
|
||||
|
||||
+20
-5
@@ -42,19 +42,34 @@ class ItemParameterTable;
|
||||
|
||||
template <bool BE>
|
||||
struct PlayerInventoryItemT {
|
||||
/* 00 */ uint8_t present = 0;
|
||||
// Values for state:
|
||||
// 0 = on floor (used in TItem; not used in PlayerInventoryItem)
|
||||
// 1 = in player's inventory, not equipped
|
||||
// 2 = in player's inventory, equipped
|
||||
// 3 = destroying
|
||||
/* 00 */ uint8_t state = 0;
|
||||
/* 01 */ uint8_t unknown_a1 = 0;
|
||||
// See note above about these fields
|
||||
/* 02 */ uint8_t extension_data1 = 0;
|
||||
/* 03 */ uint8_t extension_data2 = 0;
|
||||
/* 04 */ U32T<BE> flags = 0; // 8 = equipped
|
||||
// Bits in the flags field:
|
||||
// 0004 = is equippable item type ("Equip" appears in menu)
|
||||
// 0008 = is equipped
|
||||
// 0010 = is consumable item type (item is automatically deleted when used; also applies to Present)
|
||||
// 0020 = TODO (3OE1:8010B2DC, 3OE1:80120744)
|
||||
// 0040 = hidden (model does not render, no related effects are created, no related sounds are played)
|
||||
// 0080 = TODO (3OE1:TItem_check_flag80; pssibly temp flag only used in Tekker sequence)
|
||||
// 0100 = has kill count
|
||||
// 0200 = kill count limit has been reached (item can be unsealed)
|
||||
// 0400 = wrapped
|
||||
/* 04 */ U32T<BE> flags = 0;
|
||||
/* 08 */ ItemData data;
|
||||
/* 1C */
|
||||
|
||||
PlayerInventoryItemT() = default;
|
||||
|
||||
PlayerInventoryItemT(const ItemData& item, bool equipped)
|
||||
: present(1),
|
||||
: state(1),
|
||||
unknown_a1(0),
|
||||
extension_data1(0),
|
||||
extension_data2(0),
|
||||
@@ -63,7 +78,7 @@ struct PlayerInventoryItemT {
|
||||
|
||||
operator PlayerInventoryItemT<!BE>() const {
|
||||
PlayerInventoryItemT<!BE> ret;
|
||||
ret.present = this->present;
|
||||
ret.state = this->state;
|
||||
ret.unknown_a1 = this->unknown_a1;
|
||||
ret.extension_data1 = this->extension_data1;
|
||||
ret.extension_data2 = this->extension_data2;
|
||||
@@ -227,7 +242,7 @@ struct PlayerInventoryT {
|
||||
((data1_1 < 0) || (this->items[read_offset].data.data1[1] == static_cast<uint8_t>(data1_1))));
|
||||
if (!should_delete) {
|
||||
if (read_offset != write_offset) {
|
||||
this->items[write_offset].present = this->items[read_offset].present;
|
||||
this->items[write_offset].state = this->items[read_offset].state;
|
||||
this->items[write_offset].unknown_a1 = this->items[read_offset].unknown_a1;
|
||||
this->items[write_offset].flags = this->items[read_offset].flags;
|
||||
this->items[write_offset].data = this->items[read_offset].data;
|
||||
|
||||
@@ -886,7 +886,9 @@ static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::M
|
||||
old_channel->language,
|
||||
std::format("C-{} proxy remote server at {}", c->id, netloc_str),
|
||||
old_channel->terminal_send_color,
|
||||
old_channel->terminal_recv_color);
|
||||
old_channel->terminal_recv_color,
|
||||
old_channel->censor_received_credentials,
|
||||
old_channel->censor_sent_credentials);
|
||||
c->proxy_session->server_channel = new_channel;
|
||||
asio::co_spawn(*s->io_context, handle_proxy_server_commands(c, c->proxy_session, new_channel), asio::detached);
|
||||
c->log.info_f("Server channel connected");
|
||||
|
||||
+12
-18
@@ -107,24 +107,18 @@ static string escape_string(const string& data, TextEncoding encoding = TextEnco
|
||||
}
|
||||
|
||||
static string format_and_indent_data(const void* data, size_t size, uint64_t start_address) {
|
||||
struct iovec iov;
|
||||
iov.iov_base = const_cast<void*>(data);
|
||||
iov.iov_len = size;
|
||||
|
||||
string ret = " ";
|
||||
phosg::format_data(
|
||||
[&ret](const void* vdata, size_t size) -> void {
|
||||
const char* data = reinterpret_cast<const char*>(vdata);
|
||||
for (size_t z = 0; z < size; z++) {
|
||||
if (data[z] == '\n') {
|
||||
ret += "\n ";
|
||||
} else {
|
||||
ret.push_back(data[z]);
|
||||
}
|
||||
}
|
||||
},
|
||||
&iov, 1, start_address, nullptr, 0, phosg::PrintDataFlags::PRINT_ASCII);
|
||||
|
||||
auto write_fn = [&ret](const void* vdata, size_t size) -> void {
|
||||
const char* data = reinterpret_cast<const char*>(vdata);
|
||||
for (size_t z = 0; z < size; z++) {
|
||||
if (data[z] == '\n') {
|
||||
ret += "\n ";
|
||||
} else {
|
||||
ret.push_back(data[z]);
|
||||
}
|
||||
}
|
||||
};
|
||||
phosg::format_data_custom(write_fn, data, size, start_address, phosg::FormatDataFlags::PRINT_ASCII);
|
||||
phosg::strip_trailing_whitespace(ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -3533,7 +3527,7 @@ std::string disassemble_quest_script(
|
||||
line_text = std::format(" {}", dasm_line);
|
||||
} else {
|
||||
size_t opcode_size = label_r.where() - opcode_start_offset;
|
||||
string hex_data = phosg::format_data_string(label_r.preadx(opcode_start_offset, opcode_size), nullptr, phosg::FormatDataFlags::HEX_ONLY);
|
||||
string hex_data = phosg::format_data_string(label_r.preadx(opcode_start_offset, opcode_size), nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
|
||||
if (hex_data.size() > 14) {
|
||||
hex_data.resize(12);
|
||||
hex_data += "...";
|
||||
|
||||
@@ -696,7 +696,9 @@ asio::awaitable<void> start_proxy_session(shared_ptr<Client> c, const string& ho
|
||||
c->channel->language,
|
||||
std::format("C-{} proxy remote server at {}", c->id, netloc_str),
|
||||
phosg::TerminalFormat::FG_YELLOW,
|
||||
phosg::TerminalFormat::FG_RED);
|
||||
phosg::TerminalFormat::FG_RED,
|
||||
false,
|
||||
s->censor_credentials);
|
||||
c->proxy_session = make_shared<ProxySession>(channel, pc);
|
||||
|
||||
if (c->version() == Version::GC_EP3) {
|
||||
|
||||
+59
-10
@@ -936,7 +936,7 @@ static void transcode_inventory_items(
|
||||
}
|
||||
for (size_t z = num_items; z < 30; z++) {
|
||||
auto& item = items[z];
|
||||
item.present = 0;
|
||||
item.state = 0;
|
||||
item.unknown_a1 = 0;
|
||||
item.flags = 0;
|
||||
item.data.clear();
|
||||
@@ -2467,7 +2467,7 @@ static asio::awaitable<void> on_pick_up_item_generic(
|
||||
if (!l->item_exists(floor, item_id)) {
|
||||
// This can happen if the network is slow, and the client tries to pick up the same item multiple times. Or
|
||||
// multiple clients could try to pick up the same item at approximately the same time; only one should get it.
|
||||
l->log.warning_f("Player {} requests to pick up {:08X}, but the item does not exist; dropping command", client_id, item_id);
|
||||
l->log.info_f("Player {} requests to pick up {:08X}, but the item does not exist; dropping command", client_id, item_id);
|
||||
|
||||
} else {
|
||||
// This is handled by the server on BB, and by the leader on other versions. However, the client's logic is to
|
||||
@@ -3096,7 +3096,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
}
|
||||
bool object_ignore_def = (set_entry->param1 > 0.0);
|
||||
if (res.ignore_def != object_ignore_def) {
|
||||
c->log.warning_f("ignore_def value {} from command does not match object\'s expected ignore_def {} (from p1={:g})",
|
||||
c->log.info_f("ignore_def value {} from command does not match object\'s expected ignore_def {} (from p1={:g})",
|
||||
res.ignore_def ? "true" : "false", object_ignore_def ? "true" : "false", set_entry->param1);
|
||||
}
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
@@ -5023,22 +5023,71 @@ static asio::awaitable<void> on_battle_level_up_bb(shared_ptr<Client> c, Subcomm
|
||||
if (!l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
throw runtime_error("6xD0 command sent during free play");
|
||||
}
|
||||
if (!l->quest) {
|
||||
throw runtime_error("6x9B command sent without quest loaded");
|
||||
}
|
||||
if (!l->quest->meta.battle_rules) {
|
||||
throw runtime_error("6x9B command sent without battle quest loaded");
|
||||
}
|
||||
|
||||
const auto& cmd = msg.check_size_t<G_BattleModeLevelUp_BB_6xD0>();
|
||||
if (cmd.num_levels != l->quest->meta.battle_rules->death_level_up) {
|
||||
throw runtime_error("client requested incorrect level count");
|
||||
}
|
||||
|
||||
auto lc = l->clients.at(cmd.header.client_id);
|
||||
if (lc) {
|
||||
auto s = c->require_server_state();
|
||||
auto lp = lc->character_file();
|
||||
uint32_t target_level = min<uint32_t>(lp->disp.stats.level + cmd.num_levels, 199);
|
||||
uint32_t before_exp = lp->disp.stats.exp;
|
||||
int32_t exp_delta = lp->disp.stats.exp - before_exp;
|
||||
if (exp_delta > 0) {
|
||||
s->level_table(lc->version())->advance_to_level(lp->disp.stats, target_level, lp->disp.visual.char_class);
|
||||
if (lc->version() == Version::BB_V4) {
|
||||
send_give_experience(lc, exp_delta, 0xFFFF);
|
||||
send_level_up(lc);
|
||||
s->level_table(lc->version())->advance_to_level(lp->disp.stats, target_level, lp->disp.visual.char_class);
|
||||
if ((lp->disp.stats.exp > before_exp) && (lc->version() == Version::BB_V4)) {
|
||||
send_give_experience(lc, lp->disp.stats.exp - before_exp, 0xFFFF);
|
||||
send_level_up(lc);
|
||||
}
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
static asio::awaitable<void> on_battle_tech_level_up(shared_ptr<Client> c, SubcommandMessage& msg) {
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game()) {
|
||||
throw runtime_error("6x9B command sent in non-game lobby");
|
||||
}
|
||||
if (l->mode != GameMode::BATTLE) {
|
||||
throw runtime_error("6x9B command sent during free play");
|
||||
}
|
||||
if (!l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) {
|
||||
throw runtime_error("6x9B command sent during free play");
|
||||
}
|
||||
if (!l->quest) {
|
||||
throw runtime_error("6x9B command sent without quest loaded");
|
||||
}
|
||||
if (!l->quest->meta.battle_rules) {
|
||||
throw runtime_error("6x9B command sent without battle quest loaded");
|
||||
}
|
||||
|
||||
const auto& cmd = msg.check_size_t<G_LevelUpAllTechniques_6x9B>();
|
||||
if (cmd.num_levels != l->quest->meta.battle_rules->death_tech_level_up) {
|
||||
throw runtime_error("client requested incorrect technique level count");
|
||||
}
|
||||
|
||||
auto lc = l->clients.at(cmd.header.client_id);
|
||||
if (lc) {
|
||||
auto s = c->require_server_state();
|
||||
auto lp = lc->character_file();
|
||||
auto pmt = s->item_parameter_table(lc->version());
|
||||
for (uint8_t tech_num = 0; tech_num < 0x13; tech_num++) {
|
||||
size_t level = lp->get_technique_level(tech_num);
|
||||
if (level != 0xFF) {
|
||||
size_t new_level = std::min<size_t>(
|
||||
level + cmd.num_levels, pmt->get_max_tech_level(lp->disp.visual.char_class, tech_num));
|
||||
lp->set_technique_level(tech_num, new_level);
|
||||
}
|
||||
}
|
||||
|
||||
forward_subcommand(c, msg);
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
@@ -5976,7 +6025,7 @@ const vector<SubcommandDefinition> subcommand_definitions{
|
||||
/* 6x98 */ {NONE, NONE, 0x98, on_forward_check_game},
|
||||
/* 6x99 */ {NONE, NONE, 0x99, on_forward_check_game},
|
||||
/* 6x9A */ {NONE, NONE, 0x9A, on_forward_check_game_client},
|
||||
/* 6x9B */ {NONE, NONE, 0x9B, on_forward_check_game},
|
||||
/* 6x9B */ {NONE, NONE, 0x9B, on_battle_tech_level_up},
|
||||
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_status_effect_flags_ultimate},
|
||||
/* 6x9D */ {NONE, NONE, 0x9D, on_forward_check_game},
|
||||
/* 6x9E */ {NONE, NONE, 0x9E, forward_subcommand_m},
|
||||
|
||||
+15
-7
@@ -50,7 +50,15 @@ ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t
|
||||
: id(id),
|
||||
port(port),
|
||||
version(version),
|
||||
channel(make_shared<PeerChannel>(io_context, this->version, Language::ENGLISH, std::format("R-{:X}", this->id))) {}
|
||||
channel(make_shared<PeerChannel>(
|
||||
io_context,
|
||||
this->version,
|
||||
Language::ENGLISH,
|
||||
std::format("R-{:X}", this->id),
|
||||
phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat::END,
|
||||
false,
|
||||
false)) {}
|
||||
|
||||
string ReplaySession::Client::str() const {
|
||||
return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version));
|
||||
@@ -516,7 +524,7 @@ asio::awaitable<void> ReplaySession::run() {
|
||||
"(ev-line {}) client connected to port missing from configuration", this->first_event->line_num));
|
||||
}
|
||||
|
||||
auto server_channel = make_shared<PeerChannel>(this->state->io_context, port_config->version, c->channel->language);
|
||||
auto server_channel = make_shared<PeerChannel>(this->state->io_context, port_config->version, c->channel->language, "", phosg::TerminalFormat::END, phosg::TerminalFormat::END, false, false);
|
||||
PeerChannel::link_peers(c->channel, server_channel);
|
||||
|
||||
if (this->state->game_server.get()) {
|
||||
@@ -561,24 +569,24 @@ asio::awaitable<void> ReplaySession::run() {
|
||||
this->bytes_received += full_command.size();
|
||||
|
||||
if (c->receive_events.empty()) {
|
||||
phosg::print_data(stderr, full_command, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, full_command, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
throw runtime_error("received unexpected command for client");
|
||||
}
|
||||
|
||||
auto& ev = c->receive_events.front();
|
||||
if ((full_command.size() != ev->data.size()) && !ev->allow_size_disparity) {
|
||||
replay_log.error_f("Expected command:");
|
||||
phosg::print_data(stderr, ev->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
replay_log.error_f("Received command:");
|
||||
phosg::print_data(stderr, full_command, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, full_command, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
throw runtime_error(std::format("(ev-line {}) received command sizes do not match", ev->line_num));
|
||||
}
|
||||
for (size_t x = 0; x < min<size_t>(full_command.size(), ev->data.size()); x++) {
|
||||
if ((full_command[x] & ev->mask[x]) != (ev->data[x] & ev->mask[x])) {
|
||||
replay_log.error_f("Expected command:");
|
||||
phosg::print_data(stderr, ev->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
replay_log.error_f("Received command:");
|
||||
phosg::print_data(stderr, full_command, 0, ev->data.data(), phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
|
||||
phosg::print_data(stderr, full_command, 0, ev->data, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
|
||||
throw runtime_error(std::format("(ev-line {}) received command data does not match expected data", ev->line_num));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,7 +1257,7 @@ void PSOBBCharacterFile::add_item(const ItemData& item, const ItemData::StackLim
|
||||
throw out_of_range("inventory is full");
|
||||
}
|
||||
auto& inv_item = this->inventory.items[this->inventory.num_items];
|
||||
inv_item.present = 1;
|
||||
inv_item.state = 1;
|
||||
inv_item.unknown_a1 = 0;
|
||||
inv_item.flags = 0;
|
||||
inv_item.data = item;
|
||||
@@ -1304,13 +1304,13 @@ ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, cons
|
||||
for (size_t x = index; x < this->inventory.num_items; x++) {
|
||||
auto& to_item = this->inventory.items[x];
|
||||
const auto& from_item = this->inventory.items[x + 1];
|
||||
to_item.present = from_item.present;
|
||||
to_item.state = from_item.state;
|
||||
to_item.unknown_a1 = from_item.unknown_a1;
|
||||
to_item.flags = from_item.flags;
|
||||
to_item.data = from_item.data;
|
||||
}
|
||||
auto& last_item = this->inventory.items[this->inventory.num_items];
|
||||
last_item.present = 0;
|
||||
last_item.state = 0;
|
||||
last_item.unknown_a1 = 0;
|
||||
last_item.flags = 0;
|
||||
last_item.data.clear();
|
||||
|
||||
@@ -882,6 +882,7 @@ void ServerState::load_config_early() {
|
||||
this->ip_stack_debug = this->config_json->get_bool("IPStackDebug", false);
|
||||
this->allow_unregistered_users = this->config_json->get_bool("AllowUnregisteredUsers", false);
|
||||
this->allow_pc_nte = this->config_json->get_bool("AllowPCNTE", false);
|
||||
this->allow_same_account_concurrent_logins = this->config_json->get_bool("AllowSameAccountConcurrentLogins", false);
|
||||
this->allow_saving_accounts = this->config_json->get_bool("AllowSavingAccounts", true);
|
||||
this->use_temp_accounts_for_prototypes = this->config_json->get_bool("UseTemporaryAccountsForPrototypes", true);
|
||||
this->notify_server_for_max_level_achieved = this->config_json->get_bool("NotifyServerForMaxLevelAchieved", false);
|
||||
@@ -1009,6 +1010,7 @@ void ServerState::load_config_early() {
|
||||
this->ep3_behavior_flags = this->config_json->get_int("Episode3BehaviorFlags", 0);
|
||||
this->ep3_card_auction_points = this->config_json->get_int("CardAuctionPoints", 0);
|
||||
this->hide_download_commands = this->config_json->get_bool("HideDownloadCommands", true);
|
||||
this->censor_credentials = this->config_json->get_bool("CensorCredentials", true);
|
||||
this->proxy_allow_save_files = this->config_json->get_bool("ProxyAllowSaveFiles", true);
|
||||
|
||||
try {
|
||||
|
||||
@@ -128,6 +128,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool allow_unregistered_users = false;
|
||||
bool allow_pc_nte = false;
|
||||
bool use_temp_accounts_for_prototypes = true;
|
||||
bool allow_same_account_concurrent_logins = true;
|
||||
std::array<uint16_t, NUM_VERSIONS> compatibility_groups = {};
|
||||
bool enable_chat_commands = true;
|
||||
char chat_command_sentinel = '\0'; // 0 = default (@ on 11/2000; $ on all other versions)
|
||||
@@ -166,6 +167,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
bool ep3_jukebox_is_free = false;
|
||||
uint32_t ep3_behavior_flags = 0;
|
||||
bool hide_download_commands = true;
|
||||
bool censor_credentials = true;
|
||||
RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT;
|
||||
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
|
||||
bool default_switch_assist_enabled = false;
|
||||
@@ -320,6 +322,8 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
uint8_t pre_lobby_event = 0;
|
||||
int32_t ep3_menu_song = -1;
|
||||
|
||||
std::unordered_map<uint32_t, std::shared_ptr<Client>> client_for_account;
|
||||
|
||||
std::map<std::string, uint32_t> all_addresses;
|
||||
uint32_t local_address = 0;
|
||||
uint32_t external_address = 0;
|
||||
|
||||
+8
-2
@@ -486,8 +486,14 @@ struct pstring {
|
||||
|
||||
uint8_t data[Bytes];
|
||||
|
||||
pstring() {
|
||||
memset(this->data, 0, Bytes);
|
||||
pstring(uint8_t v = 0) {
|
||||
memset(this->data, v, Bytes);
|
||||
}
|
||||
pstring(const void* data, size_t size) {
|
||||
memcpy(this->data, data, std::min<size_t>(size, Bytes));
|
||||
if (size < Bytes) {
|
||||
memset(this->data + size, 0, Bytes - size);
|
||||
}
|
||||
}
|
||||
pstring(const pstring<Encoding, Chars, BytesPerChar>& other) {
|
||||
memcpy(this->data, other.data, Bytes);
|
||||
|
||||
Reference in New Issue
Block a user