client/state: Rework packet handling to use lambda functions
This commit is contained in:
parent
84104bc7d9
commit
10f4376ae2
@ -10,11 +10,17 @@
|
|||||||
#include "interface/fs.h"
|
#include "interface/fs.h"
|
||||||
#include <cereal/archives/portable_binary.hpp>
|
#include <cereal/archives/portable_binary.hpp>
|
||||||
#include <cereal/types/string.hpp>
|
#include <cereal/types/string.hpp>
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||||
|
#include <Scene.h>
|
||||||
|
#include <MemoryBuffer.h>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#define MODULE "__state"
|
#define MODULE "__state"
|
||||||
|
namespace magic = Urho3D;
|
||||||
|
|
||||||
extern client::Config g_client_config;
|
extern client::Config g_client_config;
|
||||||
|
|
||||||
@ -36,6 +42,7 @@ struct CState: public State
|
|||||||
// In actuality the whole client application has to be recreated because
|
// In actuality the whole client application has to be recreated because
|
||||||
// otherwise unwanted Lua state remains.
|
// otherwise unwanted Lua state remains.
|
||||||
bool m_connected = false;
|
bool m_connected = false;
|
||||||
|
sm_<ss_, std::function<void(const ss_&, const ss_&)>> m_packet_handlers;
|
||||||
|
|
||||||
CState(sp_<app::App> app):
|
CState(sp_<app::App> app):
|
||||||
m_socket(interface::createTCPSocket()),
|
m_socket(interface::createTCPSocket()),
|
||||||
@ -47,6 +54,8 @@ struct CState: public State
|
|||||||
auto *fs = interface::getGlobalFilesystem();
|
auto *fs = interface::getGlobalFilesystem();
|
||||||
fs->create_directories(m_remote_cache_path);
|
fs->create_directories(m_remote_cache_path);
|
||||||
fs->create_directories(m_tmp_path);
|
fs->create_directories(m_tmp_path);
|
||||||
|
|
||||||
|
setup_packet_handlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update()
|
void update()
|
||||||
@ -148,126 +157,152 @@ struct CState: public State
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup_packet_handlers();
|
||||||
|
|
||||||
void handle_packet(const ss_ &packet_name, const ss_ &data)
|
void handle_packet(const ss_ &packet_name, const ss_ &data)
|
||||||
{
|
{
|
||||||
if(packet_name.substr(0, 5) != "core:"){
|
auto it = m_packet_handlers.find(packet_name);
|
||||||
|
if(it == m_packet_handlers.end()){
|
||||||
|
// Pass forward
|
||||||
m_app->handle_packet(packet_name, data);
|
m_app->handle_packet(packet_name, data);
|
||||||
return;
|
} else {
|
||||||
}
|
// Use handler
|
||||||
|
auto &handler = it->second;
|
||||||
if(packet_name == "core:run_script"){
|
handler(packet_name, data);
|
||||||
log_i(MODULE, "Asked to run script:\n----\n%s\n----", cs(data));
|
|
||||||
if(m_app)
|
|
||||||
m_app->run_script(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(packet_name == "core:announce_file"){
|
|
||||||
ss_ file_name;
|
|
||||||
ss_ file_hash;
|
|
||||||
std::istringstream is(data, std::ios::binary);
|
|
||||||
{
|
|
||||||
cereal::PortableBinaryInputArchive ar(is);
|
|
||||||
ar(file_name);
|
|
||||||
ar(file_hash);
|
|
||||||
}
|
|
||||||
m_file_hashes[file_name] = file_hash;
|
|
||||||
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
|
||||||
log_v(MODULE, "Server announces file: %s %s",
|
|
||||||
cs(file_hash_hex), cs(file_name));
|
|
||||||
// Check if we already have this file
|
|
||||||
ss_ path = m_remote_cache_path+"/"+file_hash_hex;
|
|
||||||
std::ifstream ifs(path, std::ios::binary);
|
|
||||||
bool cached_is_ok = false;
|
|
||||||
if(ifs.good()){
|
|
||||||
std::string content((std::istreambuf_iterator<char>(ifs)),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
ss_ content_hash = interface::sha1::calculate(content);
|
|
||||||
if(content_hash == file_hash){
|
|
||||||
// We have it; no need to ask this file
|
|
||||||
log_i(MODULE, "%s %s: cached",
|
|
||||||
cs(file_hash_hex), cs(file_name));
|
|
||||||
cached_is_ok = true;
|
|
||||||
} else {
|
|
||||||
// Our copy is broken, re-request it
|
|
||||||
log_i(MODULE, "%s %s: Our copy is broken (has hash %s)",
|
|
||||||
cs(file_hash_hex), cs(file_name),
|
|
||||||
cs(interface::sha1::hex(content_hash)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(cached_is_ok){
|
|
||||||
// Let Lua resource wrapper know that this happened so it can update
|
|
||||||
// the copy made for Urho3D's resource cache
|
|
||||||
m_app->file_updated_in_cache(file_name, file_hash, path);
|
|
||||||
} else {
|
|
||||||
// We don't have it; request this file
|
|
||||||
log_i(MODULE, "%s %s: requesting",
|
|
||||||
cs(file_hash_hex), cs(file_name));
|
|
||||||
std::ostringstream os(std::ios::binary);
|
|
||||||
{
|
|
||||||
cereal::PortableBinaryOutputArchive ar(os);
|
|
||||||
ar(file_name);
|
|
||||||
ar(file_hash);
|
|
||||||
}
|
|
||||||
send_packet("core:request_file", os.str());
|
|
||||||
m_waiting_files.insert(file_name);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(packet_name == "core:tell_after_all_files_transferred"){
|
|
||||||
if(m_waiting_files.empty()){
|
|
||||||
send_packet("core:all_files_transferred", "");
|
|
||||||
} else {
|
|
||||||
m_tell_after_all_files_transferred_requested = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(packet_name == "core:file_content"){
|
|
||||||
ss_ file_name;
|
|
||||||
ss_ file_hash;
|
|
||||||
ss_ file_content;
|
|
||||||
std::istringstream is(data, std::ios::binary);
|
|
||||||
{
|
|
||||||
cereal::PortableBinaryInputArchive ar(is);
|
|
||||||
ar(file_name);
|
|
||||||
ar(file_hash);
|
|
||||||
ar(file_content);
|
|
||||||
}
|
|
||||||
if(m_waiting_files.count(file_name) == 0){
|
|
||||||
log_w(MODULE, "Received file was not requested: %s %s",
|
|
||||||
cs(interface::sha1::hex(file_hash)), cs(file_name));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_waiting_files.erase(file_name);
|
|
||||||
ss_ file_hash2 = interface::sha1::calculate(file_content);
|
|
||||||
if(file_hash != file_hash2){
|
|
||||||
log_w(MODULE, "Requested file differs in hash: \"%s\": "
|
|
||||||
"requested %s, actual %s", cs(file_name),
|
|
||||||
cs(interface::sha1::hex(file_hash)),
|
|
||||||
cs(interface::sha1::hex(file_hash2)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
|
||||||
ss_ path = g_client_config.cache_path+"/remote/"+file_hash_hex;
|
|
||||||
log_i(MODULE, "Saving %s to %s", cs(file_name), cs(path));
|
|
||||||
std::ofstream of(path, std::ios::binary);
|
|
||||||
of<<file_content;
|
|
||||||
if(m_tell_after_all_files_transferred_requested){
|
|
||||||
if(m_waiting_files.empty()){
|
|
||||||
send_packet("core:all_files_transferred", "");
|
|
||||||
m_tell_after_all_files_transferred_requested = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Let Lua resource wrapper know that this happened so it can update
|
|
||||||
// the copy made for Urho3D's resource cache
|
|
||||||
m_app->file_updated_in_cache(file_name, file_hash, path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(packet_name == "entitysync:new_node"){
|
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void CState::setup_packet_handlers()
|
||||||
|
{
|
||||||
|
m_packet_handlers["core:run_script"] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
log_i(MODULE, "Asked to run script:\n----\n%s\n----", cs(data));
|
||||||
|
if(m_app)
|
||||||
|
m_app->run_script(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_packet_handlers["core:announce_file"] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
ss_ file_name;
|
||||||
|
ss_ file_hash;
|
||||||
|
std::istringstream is(data, std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::PortableBinaryInputArchive ar(is);
|
||||||
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
|
}
|
||||||
|
m_file_hashes[file_name] = file_hash;
|
||||||
|
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
||||||
|
log_v(MODULE, "Server announces file: %s %s",
|
||||||
|
cs(file_hash_hex), cs(file_name));
|
||||||
|
// Check if we already have this file
|
||||||
|
ss_ path = m_remote_cache_path+"/"+file_hash_hex;
|
||||||
|
std::ifstream ifs(path, std::ios::binary);
|
||||||
|
bool cached_is_ok = false;
|
||||||
|
if(ifs.good()){
|
||||||
|
std::string content((std::istreambuf_iterator<char>(ifs)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
ss_ content_hash = interface::sha1::calculate(content);
|
||||||
|
if(content_hash == file_hash){
|
||||||
|
// We have it; no need to ask this file
|
||||||
|
log_i(MODULE, "%s %s: cached",
|
||||||
|
cs(file_hash_hex), cs(file_name));
|
||||||
|
cached_is_ok = true;
|
||||||
|
} else {
|
||||||
|
// Our copy is broken, re-request it
|
||||||
|
log_i(MODULE, "%s %s: Our copy is broken (has hash %s)",
|
||||||
|
cs(file_hash_hex), cs(file_name),
|
||||||
|
cs(interface::sha1::hex(content_hash)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cached_is_ok){
|
||||||
|
// Let Lua resource wrapper know that this happened so it can update
|
||||||
|
// the copy made for Urho3D's resource cache
|
||||||
|
m_app->file_updated_in_cache(file_name, file_hash, path);
|
||||||
|
} else {
|
||||||
|
// We don't have it; request this file
|
||||||
|
log_i(MODULE, "%s %s: requesting",
|
||||||
|
cs(file_hash_hex), cs(file_name));
|
||||||
|
std::ostringstream os(std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::PortableBinaryOutputArchive ar(os);
|
||||||
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
|
}
|
||||||
|
send_packet("core:request_file", os.str());
|
||||||
|
m_waiting_files.insert(file_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_packet_handlers["core:tell_after_all_files_transferred"] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
if(m_waiting_files.empty()){
|
||||||
|
send_packet("core:all_files_transferred", "");
|
||||||
|
} else {
|
||||||
|
m_tell_after_all_files_transferred_requested = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_packet_handlers["core:file_content"] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
ss_ file_name;
|
||||||
|
ss_ file_hash;
|
||||||
|
ss_ file_content;
|
||||||
|
std::istringstream is(data, std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::PortableBinaryInputArchive ar(is);
|
||||||
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
|
ar(file_content);
|
||||||
|
}
|
||||||
|
if(m_waiting_files.count(file_name) == 0){
|
||||||
|
log_w(MODULE, "Received file was not requested: %s %s",
|
||||||
|
cs(interface::sha1::hex(file_hash)), cs(file_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_waiting_files.erase(file_name);
|
||||||
|
ss_ file_hash2 = interface::sha1::calculate(file_content);
|
||||||
|
if(file_hash != file_hash2){
|
||||||
|
log_w(MODULE, "Requested file differs in hash: \"%s\": "
|
||||||
|
"requested %s, actual %s", cs(file_name),
|
||||||
|
cs(interface::sha1::hex(file_hash)),
|
||||||
|
cs(interface::sha1::hex(file_hash2)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
||||||
|
ss_ path = g_client_config.cache_path+"/remote/"+file_hash_hex;
|
||||||
|
log_i(MODULE, "Saving %s to %s", cs(file_name), cs(path));
|
||||||
|
std::ofstream of(path, std::ios::binary);
|
||||||
|
of<<file_content;
|
||||||
|
if(m_tell_after_all_files_transferred_requested){
|
||||||
|
if(m_waiting_files.empty()){
|
||||||
|
send_packet("core:all_files_transferred", "");
|
||||||
|
m_tell_after_all_files_transferred_requested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Let Lua resource wrapper know that this happened so it can update
|
||||||
|
// the copy made for Urho3D's resource cache
|
||||||
|
m_app->file_updated_in_cache(file_name, file_hash, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_packet_handlers["entitysync:new_node"] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
magic::Scene *scene = m_app->get_scene();
|
||||||
|
};
|
||||||
|
|
||||||
|
m_packet_handlers[""] =
|
||||||
|
[this](const ss_ &packet_name, const ss_ &data)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
State* createState(sp_<app::App> app)
|
State* createState(sp_<app::App> app)
|
||||||
{
|
{
|
||||||
return new CState(app);
|
return new CState(app);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user