pandemonium_engine/modules/network_synchronizer/data_buffer.h

364 lines
14 KiB
C++

#ifndef INPUT_BUFFER_H
#define INPUT_BUFFER_H
/*************************************************************************/
/* data_buffer.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
/**
@author AndreaCatania
*/
#include "core/object/class_db.h"
#include "bit_array.h"
class DataBuffer : public Object {
GDCLASS(DataBuffer, Object);
public:
enum DataType {
DATA_TYPE_BOOL,
DATA_TYPE_INT,
DATA_TYPE_UINT,
DATA_TYPE_REAL,
DATA_TYPE_POSITIVE_UNIT_REAL,
DATA_TYPE_UNIT_REAL,
DATA_TYPE_VECTOR2,
DATA_TYPE_NORMALIZED_VECTOR2,
DATA_TYPE_VECTOR3,
DATA_TYPE_NORMALIZED_VECTOR3,
// The only dynamic sized value.
DATA_TYPE_VARIANT
};
/// Compression level for the stored input data.
///
/// Depending on the data type and the compression level used the amount of
/// bits used and loss change.
///
///
/// ## Bool
/// Always use 1 bit
///
///
/// ## Int
/// COMPRESSION_LEVEL_0: 64 bits are used - Stores integers -9223372036854775808 / 9223372036854775807
/// COMPRESSION_LEVEL_1: 32 bits are used - Stores integers -2147483648 / 2147483647
/// COMPRESSION_LEVEL_2: 16 bits are used - Stores integers -32768 / 32767
/// COMPRESSION_LEVEL_3: 8 bits are used - Stores integers -128 / 127
///
///
/// ## Real
/// Precision depends on an integer range
/// COMPRESSION_LEVEL_0: 64 bits are used - Double precision. Up to 16 precision is 0.00000000000000177636 in worst case. Up to 512 precision is 0.00000000000005684342 in worst case. Up to 1024 precision is 0.00000000000011368684 in worst case.
/// COMPRESSION_LEVEL_1: 32 bits are used - Single precision (float). Up to 16 precision is 0.00000095367431640625 in worst case. Up to 512 precision is 0.000030517578125 in worst case. Up to 1024 precision is 0.00006103515625 in worst case.
/// COMPRESSION_LEVEL_2: 16 bits are used - Half precision. Up to 16 precision is 0.0078125 in worst case. Up to 512 precision is 0.25 in worst case. Up to 1024 precision is 0.5.
/// COMPRESSION_LEVEL_3: 8 bits are used - Minifloat: Up to 2 precision is 0.125. Up to 4 precision is 0.25. Up to 8 precision is 0.5.
///
/// To get the exact precision for the stored number, you need to find the lower power of two relative to the number and divide it by 2^mantissa_bits.
/// To get the mantissa or exponent bits for a specific compression level, you can use the get_mantissa_bits and get_exponent_bits functions.
///
///
/// ## Positive unit real
/// COMPRESSION_LEVEL_0: 10 bits are used - Max loss ~0.005%
/// COMPRESSION_LEVEL_1: 8 bits are used - Max loss ~0.020%
/// COMPRESSION_LEVEL_2: 6 bits are used - Max loss ~0.793%
/// COMPRESSION_LEVEL_3: 4 bits are used - Max loss ~3.333%
///
///
/// ## Unit real (uses one extra bit for the sign)
/// COMPRESSION_LEVEL_0: 11 bits are used - Max loss ~0.005%
/// COMPRESSION_LEVEL_1: 9 bits are used - Max loss ~0.020%
/// COMPRESSION_LEVEL_2: 7 bits are used - Max loss ~0.793%
/// COMPRESSION_LEVEL_3: 5 bits are used - Max loss ~3.333%
///
///
/// ## Vector2
/// COMPRESSION_LEVEL_0: 2 * 64 bits are used - Double precision (will fallback to level 1 if REAL_T_IS_DOUBLE is not defined)
/// COMPRESSION_LEVEL_1: 2 * 32 bits are used - Single precision
/// COMPRESSION_LEVEL_2: 2 * 16 bits are used - Half precision
/// COMPRESSION_LEVEL_3: 2 * 8 bits are used - Minifloat
///
/// For floating point precision, check the Real compression section.
///
///
/// ## Normalized Vector2
/// COMPRESSION_LEVEL_0: 12 bits are used - Max loss 0.17°
/// COMPRESSION_LEVEL_1: 11 bits are used - Max loss 0.35°
/// COMPRESSION_LEVEL_2: 10 bits are used - Max loss 0.7°
/// COMPRESSION_LEVEL_3: 9 bits are used - Max loss 1.1°
///
///
/// ## Vector3
/// COMPRESSION_LEVEL_0: 3 * 64 bits are used - Double precision (will fallback to level 1 if REAL_T_IS_DOUBLE is not defined)
/// COMPRESSION_LEVEL_1: 3 * 32 bits are used - Single precision
/// COMPRESSION_LEVEL_2: 3 * 16 bits are used - Half precision
/// COMPRESSION_LEVEL_3: 3 * 8 bits are used - Minifloat
///
/// For floating point precision, check the Real compression section.
///
///
/// ## Normalized Vector3
/// COMPRESSION_LEVEL_0: 11 * 3 bits are used - Max loss ~0.005% per axis
/// COMPRESSION_LEVEL_1: 9 * 3 bits are used - Max loss ~0.020% per axis
/// COMPRESSION_LEVEL_2: 7 * 3 bits are used - Max loss ~0.793% per axis
/// COMPRESSION_LEVEL_3: 5 * 3 bits are used - Max loss ~3.333% per axis
///
/// ## Variant
/// It's dynamic sized. It's not possible to compress it.
enum CompressionLevel {
COMPRESSION_LEVEL_0,
COMPRESSION_LEVEL_1,
COMPRESSION_LEVEL_2,
COMPRESSION_LEVEL_3
};
private:
int metadata_size = 0;
int bit_offset = 0;
int bit_size = 0;
bool is_reading = false;
BitArray buffer;
#if DEBUG_ENABLED
bool debug_enabled = true;
#endif
public:
static void _bind_methods();
DataBuffer() = default;
DataBuffer(const DataBuffer &p_other);
DataBuffer(const BitArray &p_buffer);
void copy(const DataBuffer &p_other);
void copy(const BitArray &p_buffer);
const BitArray &get_buffer() const {
return buffer;
}
BitArray &get_buffer_mut() {
return buffer;
}
/// Begin write.
void begin_write(int p_metadata_size);
/// Make sure the buffer takes least space possible.
void dry();
/// Seek the offset to a specific bit. Seek to a bit greater than the actual
/// size is not allowed.
void seek(int p_bits);
/// Set the bit size and the metadata size.
void shrink_to(int p_metadata_bit_size, int p_bit_size);
/// Returns the metadata size in bits.
int get_metadata_size() const;
/// Returns the buffer size in bits
int size() const;
/// Total size in bits.
int total_size() const;
/// Returns the bit offset.
int get_bit_offset() const;
/// Skip n bits.
void skip(int p_bits);
/// Begin read.
void begin_read();
/// Add a boolean to the buffer.
/// Returns the same data.
bool add_bool(bool p_input);
/// Parse the next data as boolean.
bool read_bool();
/// Add the next data as int.
int64_t add_int(int64_t p_input, CompressionLevel p_compression_level);
/// Parse the next data as int.
int64_t read_int(CompressionLevel p_compression_level);
/// Add the next data as uint
uint64_t add_uint(uint64_t p_input, CompressionLevel p_compression_level);
/// Parse the next data as uint.
uint64_t read_uint(CompressionLevel p_compression_level);
/// Add a real into the buffer. Depending on the compression level is possible
/// to store different range level.
/// The fractional part has a precision of ~0.3%
///
/// Returns the compressed value so both the client and the peers can use
/// the same data.
double add_real(double p_input, CompressionLevel p_compression_level);
/// Parse the following data as a real.
double read_real(CompressionLevel p_compression_level);
/// Add a positive unit real into the buffer.
///
/// **Note:** Not unitary values lead to unexpected behaviour.
///
/// Returns the compressed value so both the client and the peers can use
/// the same data.
real_t add_positive_unit_real(real_t p_input, CompressionLevel p_compression_level);
/// Parse the following data as a positive unit real.
real_t read_positive_unit_real(CompressionLevel p_compression_level);
/// Add a unit real into the buffer.
///
/// **Note:** Not unitary values lead to unexpected behaviour.
///
/// Returns the compressed value so both the client and the peers can use
/// the same data.
real_t add_unit_real(real_t p_input, CompressionLevel p_compression_level);
/// Parse the following data as an unit real.
real_t read_unit_real(CompressionLevel p_compression_level);
/// Add a vector2 into the buffer.
/// Note: This kind of vector occupies more space than the normalized verison.
/// Consider use a normalized vector to save bandwidth if possible.
///
/// Returns the decompressed vector so both the client and the peers can use
/// the same data.
Vector2 add_vector2(Vector2 p_input, CompressionLevel p_compression_level);
/// Parse next data as vector from the input buffer.
Vector2 read_vector2(CompressionLevel p_compression_level);
/// Add a normalized vector2 into the buffer.
/// Note: The compression algorithm rely on the fact that this is a
/// normalized vector. The behaviour is unexpected for not normalized vectors.
///
/// Returns the decompressed vector so both the client and the peers can use
/// the same data.
Vector2 add_normalized_vector2(Vector2 p_input, CompressionLevel p_compression_level);
/// Parse next data as normalized vector from the input buffer.
Vector2 read_normalized_vector2(CompressionLevel p_compression_level);
/// Add a vector3 into the buffer.
/// Note: This kind of vector occupies more space than the normalized verison.
/// Consider use a normalized vector to save bandwidth if possible.
///
/// Returns the decompressed vector so both the client and the peers can use
/// the same data.
Vector3 add_vector3(Vector3 p_input, CompressionLevel p_compression_level);
/// Parse next data as vector3 from the input buffer.
Vector3 read_vector3(CompressionLevel p_compression_level);
/// Add a normalized vector3 into the buffer.
/// Note: The compression algorithm rely on the fact that this is a
/// normalized vector. The behaviour is unexpected for not normalized vectors.
///
/// Returns the decompressed vector so both the client and the peers can use
/// the same data.
Vector3 add_normalized_vector3(Vector3 p_input, CompressionLevel p_compression_level);
/// Parse next data as normalized vector3 from the input buffer.
Vector3 read_normalized_vector3(CompressionLevel p_compression_level);
/// Add a variant. This is the only supported dynamic sized value.
Variant add_variant(const Variant &p_input);
/// Parse the next data as Variant and returns it.
Variant read_variant();
/// Puts all the bytes to 0.
void zero();
/** Skips the amount of bits a type takes. */
void skip_bool();
void skip_int(CompressionLevel p_compression);
void skip_uint(CompressionLevel p_compression);
void skip_real(CompressionLevel p_compression);
void skip_positive_unit_real(CompressionLevel p_compression);
void skip_unit_real(CompressionLevel p_compression);
void skip_vector2(CompressionLevel p_compression);
void skip_normalized_vector2(CompressionLevel p_compression);
void skip_vector3(CompressionLevel p_compression);
void skip_normalized_vector3(CompressionLevel p_compression);
/** Just returns the size of a specific type. */
int get_bool_size() const;
int get_int_size(CompressionLevel p_compression) const;
int get_uint_size(CompressionLevel p_compression) const;
int get_real_size(CompressionLevel p_compression) const;
int get_positive_unit_real_size(CompressionLevel p_compression) const;
int get_unit_real_size(CompressionLevel p_compression) const;
int get_vector2_size(CompressionLevel p_compression) const;
int get_normalized_vector2_size(CompressionLevel p_compression) const;
int get_vector3_size(CompressionLevel p_compression) const;
int get_normalized_vector3_size(CompressionLevel p_compression) const;
/** Read the size and pass to the next parameter. */
int read_bool_size();
int read_int_size(CompressionLevel p_compression);
int read_uint_size(CompressionLevel p_compression);
int read_real_size(CompressionLevel p_compression);
int read_positive_unit_real_size(CompressionLevel p_compression);
int read_unit_real_size(CompressionLevel p_compression);
int read_vector2_size(CompressionLevel p_compression);
int read_normalized_vector2_size(CompressionLevel p_compression);
int read_vector3_size(CompressionLevel p_compression);
int read_normalized_vector3_size(CompressionLevel p_compression);
int read_variant_size();
static int get_bit_taken(DataType p_data_type, CompressionLevel p_compression);
static int get_mantissa_bits(CompressionLevel p_compression);
static int get_exponent_bits(CompressionLevel p_compression);
private:
static uint64_t compress_unit_float(double p_value, double p_scale_factor);
static double decompress_unit_float(uint64_t p_value, double p_scale_factor);
void make_room_in_bits(int p_dim);
void make_room_pad_to_next_byte();
bool pad_to_next_byte();
};
VARIANT_ENUM_CAST(DataBuffer::DataType)
VARIANT_ENUM_CAST(DataBuffer::CompressionLevel)
#endif