2023-12-25 20:27:28 +01:00
/*************************************************************************/
/* net_action.cpp */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2023-12-25 19:27:07 +01:00
# include "net_action.h"
2023-12-25 20:00:07 +01:00
# include "core/error/error_macros.h"
2023-12-25 19:27:07 +01:00
# include "core/os/os.h"
# include "scene/main/node.h"
# include "scene_synchronizer.h"
# include "scene_synchronizer_debugger.h"
void SenderNetAction : : prepare_processor (
NetUtility : : NodeData * p_nd ,
NetActionId p_action_id ,
const Array & p_vars ) {
action_processor . action_id = p_action_id ;
action_processor . nd = p_nd ;
// Compress the vars so that locally we use the compressed version like remotely.
const NetActionInfo & info = p_nd - > net_actions [ p_action_id ] ;
LocalVector < Variant > raw_vars ;
raw_vars . resize ( p_vars . size ( ) ) ;
for ( int i = 0 ; i < p_vars . size ( ) ; i + = 1 ) {
raw_vars [ i ] = p_vars [ i ] ;
}
DataBuffer db ;
db . begin_write ( 0 ) ;
info . network_encoder - > encode ( raw_vars , db ) ;
db . begin_read ( ) ;
info . network_encoder - > decode ( db , raw_vars ) ;
action_processor . vars . resize ( raw_vars . size ( ) ) ;
for ( int i = 0 ; i < p_vars . size ( ) ; i + = 1 ) {
action_processor . vars [ i ] = raw_vars [ i ] ;
}
}
const NetActionInfo & SenderNetAction : : get_action_info ( ) const {
return action_processor . nd - > net_actions [ action_processor . action_id ] ;
}
void SenderNetAction : : client_set_executed_input_id ( uint32_t p_input_id ) {
peers_executed_input_id [ 1 ] = p_input_id ;
}
uint32_t SenderNetAction : : client_get_executed_input_id ( ) const {
const uint32_t * input_id = peers_executed_input_id . getptr ( 1 ) ;
return input_id = = nullptr ? UINT32_MAX : * input_id ;
}
uint32_t SenderNetAction : : peer_get_executed_input_id ( int p_peer ) const {
const uint32_t * input_id = peers_executed_input_id . getptr ( p_peer ) ;
return input_id = = nullptr ? UINT32_MAX : * input_id ;
}
void net_action : : encode_net_action (
const LocalVector < SenderNetAction * > & p_actions ,
int p_peer ,
DataBuffer & r_data_buffer ) {
for ( uint32_t i = 0 ; i < p_actions . size ( ) ; i + + ) {
// ---------------------------------------------------------- Add a boolean to note a new action
const bool has_anotherone = true ;
r_data_buffer . add_bool ( has_anotherone ) ;
// ----------------------------------------------------------------- Add the sender action token
r_data_buffer . add_uint ( p_actions [ i ] - > action_token , DataBuffer : : COMPRESSION_LEVEL_1 ) ;
// ----------------------------------------------------------------------------- Add the node id
const bool uses_node_id = p_actions [ i ] - > action_processor . nd - > id ! = UINT32_MAX ;
r_data_buffer . add_bool ( uses_node_id ) ;
if ( uses_node_id ) {
r_data_buffer . add_uint ( p_actions [ i ] - > action_processor . nd - > id , DataBuffer : : COMPRESSION_LEVEL_2 ) ;
} else {
r_data_buffer . add_variant ( p_actions [ i ] - > action_processor . nd - > node - > get_path ( ) ) ;
}
// --------------------------------------------------------------------------- Add the action_id
const NetActionId action_id = p_actions [ i ] - > action_processor . action_id ;
r_data_buffer . add_uint ( action_id , DataBuffer : : COMPRESSION_LEVEL_2 ) ;
// -------------------------------------------------------------------------- Add executed frame
const uint32_t * executed_frame = p_actions [ i ] - > peers_executed_input_id . getptr ( p_peer ) ;
const bool has_executed_frame = executed_frame ! = nullptr ;
r_data_buffer . add_bool ( has_executed_frame ) ;
if ( has_executed_frame ) {
r_data_buffer . add_uint ( * executed_frame , DataBuffer : : COMPRESSION_LEVEL_1 ) ;
}
// --------------------------------------------------------------- Add the executed time changed
const bool sender_executed_time_changed =
p_actions [ i ] - > sender_executed_time_changed & &
p_peer = = p_actions [ i ] - > sender_peer ;
r_data_buffer . add_bool ( sender_executed_time_changed ) ;
if ( sender_executed_time_changed ) {
r_data_buffer . add_uint ( p_actions [ i ] - > triggerer_action_token , DataBuffer : : COMPRESSION_LEVEL_1 ) ;
}
// --------------------------------------------------------------------------- Add the variables
LocalVector < Variant > inputs ;
inputs . resize ( p_actions [ i ] - > action_processor . vars . size ( ) ) ;
for ( uint32_t u = 0 ; u < inputs . size ( ) ; u + + ) {
inputs [ u ] = p_actions [ i ] - > action_processor . vars [ u ] ;
}
p_actions [ i ] - > action_processor . nd - > net_actions [ action_id ] . network_encoder - > encode ( inputs , r_data_buffer ) ;
}
const bool has_anotherone = false ;
r_data_buffer . add_bool ( has_anotherone ) ;
}
void net_action : : decode_net_action (
SceneSynchronizer * synchronizer ,
DataBuffer & p_data_buffer ,
int p_peer ,
LocalVector < SenderNetAction > & r_actions ) {
const int sender_peer = synchronizer - > get_tree ( ) - > get_multiplayer ( ) - > get_rpc_sender_id ( ) ;
LocalVector < Variant > variables ;
while ( p_data_buffer . get_bit_offset ( ) < p_data_buffer . total_size ( ) ) {
// ---------------------------------------------------------- Fetch the boolean `has_anotherone`
const bool has_anotherone = p_data_buffer . read_bool ( ) ;
if ( ! has_anotherone ) {
break ;
}
// --------------------------------------------------------------- Fetch the sender action token
const uint32_t action_token = p_data_buffer . read_uint ( DataBuffer : : COMPRESSION_LEVEL_1 ) ;
// --------------------------------------------------------------------------- Fetch the node id
const bool uses_node_id = p_data_buffer . read_bool ( ) ;
// Fetch the node_data.
NetUtility : : NodeData * node_data ;
if ( uses_node_id ) {
const uint32_t node_data_id = p_data_buffer . read_uint ( DataBuffer : : COMPRESSION_LEVEL_2 ) ;
node_data = synchronizer - > get_node_data ( node_data_id ) ;
if ( node_data = = nullptr ) {
SceneSynchronizerDebugger : : singleton ( ) - > debug_error ( synchronizer , " The received action data contains a node which is not registered on this peer. NodeDataId: ` " + itos ( node_data_id ) + " ` " ) ;
continue ;
}
} else {
Variant node_path = p_data_buffer . read_variant ( ) ;
ERR_FAIL_COND_MSG ( node_path . get_type ( ) ! = Variant : : NODE_PATH , " The received acts data is malformed, expected NodePath at this point. " ) ;
Node * node = synchronizer - > get_node ( node_path ) ;
if ( node = = nullptr ) {
SceneSynchronizerDebugger : : singleton ( ) - > debug_error ( synchronizer , String ( " The received action data contains a node path which is unknown: ` " ) + node_path + " ` " ) ;
continue ;
}
node_data = synchronizer - > find_node_data ( node ) ;
if ( node_data = = nullptr ) {
SceneSynchronizerDebugger : : singleton ( ) - > debug_error ( synchronizer , String ( " The received action data contains a node which is not registered on this peer. NodePath: ` " ) + node_path + " ` " ) ;
continue ;
}
}
// ------------------------------------------------------------------------- Fetch the action_id
const NetActionId action_id = p_data_buffer . read_uint ( DataBuffer : : COMPRESSION_LEVEL_2 ) ;
if ( node_data - > net_actions . size ( ) < = action_id ) {
SceneSynchronizerDebugger : : singleton ( ) - > debug_error ( synchronizer , " The received action data is malformed. This peer doesn't have the action_id (` " + itos ( action_id ) + " `) for the node ` " + node_data - > node - > get_path ( ) + " ` " ) ;
continue ;
}
// ------------------------------------------------------------------------ Fetch executed frame
uint32_t executed_frame = UINT32_MAX ;
const bool has_executed_frame = p_data_buffer . read_bool ( ) ;
if ( has_executed_frame ) {
executed_frame = p_data_buffer . read_uint ( DataBuffer : : COMPRESSION_LEVEL_1 ) ;
}
// ------------------------------------------------------------- Fetch the executed time changed
const bool sender_executed_time_changed = p_data_buffer . read_bool ( ) ;
uint32_t triggerer_action_token = action_token ;
if ( sender_executed_time_changed ) {
triggerer_action_token = p_data_buffer . read_uint ( DataBuffer : : COMPRESSION_LEVEL_1 ) ;
}
// ------------------------------------------------------------------------- Fetch the variables
variables . clear ( ) ;
node_data - > net_actions [ action_id ] . network_encoder - > decode ( p_data_buffer , variables ) ;
// This should never be triggered because the `has_anotherone` is meant to be false and stop the loop.
ERR_FAIL_COND_MSG ( p_data_buffer . get_bit_offset ( ) > = p_data_buffer . total_size ( ) , " The received action data is malformed. " ) ;
Array arguments ;
arguments . resize ( variables . size ( ) ) ;
for ( uint32_t i = 0 ; i < variables . size ( ) ; i + = 1 ) {
arguments [ i ] = variables [ i ] ;
}
const uint32_t index = r_actions . size ( ) ;
r_actions . resize ( index + 1 ) ;
r_actions [ index ] . action_token = action_token ;
r_actions [ index ] . triggerer_action_token = triggerer_action_token ;
r_actions [ index ] . sender_executed_time_changed = sender_executed_time_changed ;
r_actions [ index ] . sender_peer = sender_peer ;
r_actions [ index ] . peers_executed_input_id [ p_peer ] = executed_frame ;
r_actions [ index ] . action_processor . nd = node_data ;
r_actions [ index ] . action_processor . action_id = action_id ;
r_actions [ index ] . action_processor . vars = arguments ;
}
}
bool NetActionSenderInfo : : process_received_action ( uint32_t p_action_index ) {
const uint64_t now = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
bool already_received = true ;
if ( last_received_action_id ! = UINT32_MAX ) {
if ( last_received_action_id < p_action_index ) {
// Add all the in between ids as missing.
for ( uint32_t missing_action_id = last_received_action_id + 1 ; missing_action_id < p_action_index ; missing_action_id + = 1 ) {
missing_actions . push_back ( { missing_action_id , now } ) ;
}
last_received_action_id = p_action_index ;
already_received = false ;
} else if ( last_received_action_id = = p_action_index ) {
// Already known, drop it.
already_received = true ;
} else {
// Old act, check if it's a missing act.
const int64_t index = missing_actions . find ( { p_action_index , 0 } ) ;
const bool known = index = = - 1 ;
if ( known ) {
already_received = true ;
} else {
already_received = false ;
missing_actions . remove_unordered ( index ) ;
}
}
} else {
last_received_action_id = p_action_index ;
already_received = false ;
}
return already_received ;
}
void NetActionSenderInfo : : check_missing_actions_and_clean_up ( Node * p_owner ) {
const uint64_t now = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
const uint64_t one_second = 1000 ;
for ( int64_t i = int64_t ( missing_actions . size ( ) ) - 1 ; i > = 0 ; i - = 1 ) {
if ( ( missing_actions [ i ] . timestamp + one_second ) < = now ) {
// After more than 1 second the action is still missing.
SceneSynchronizerDebugger : : singleton ( ) - > debug_warning ( p_owner , " The action with ID: ` " + itos ( missing_actions [ i ] . id ) + " ` was never received. " ) ;
// Remove it from missing actions, this will:
// 1. From now on this action will be discarded if received.
// 2. Reduce the `missing_actions` array size.
missing_actions . remove_unordered ( i ) ;
}
}
}