forked from alterware/iw6-mod
init
This commit is contained in:
182
src/client/game/demonware/bit_buffer.cpp
Normal file
182
src/client/game/demonware/bit_buffer.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bit_buffer.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bool bit_buffer::read_bytes(const unsigned int bytes, unsigned char* output)
|
||||
{
|
||||
return this->read(bytes * 8, output);
|
||||
}
|
||||
|
||||
bool bit_buffer::read_bool(bool* output)
|
||||
{
|
||||
if (!this->read_data_type(1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->read(1, output);
|
||||
}
|
||||
|
||||
bool bit_buffer::read_uint32(unsigned int* output)
|
||||
{
|
||||
if (!this->read_data_type(8))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->read(32, output);
|
||||
}
|
||||
|
||||
bool bit_buffer::read_data_type(const char expected)
|
||||
{
|
||||
char data_type = 0;
|
||||
|
||||
if (!this->use_data_types_) return true;
|
||||
if (this->read(5, &data_type))
|
||||
{
|
||||
return (data_type == expected);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bit_buffer::write_bytes(const size_t bytes, const char* data)
|
||||
{
|
||||
return this->write_bytes(bytes, reinterpret_cast<const unsigned char*>(data));
|
||||
}
|
||||
|
||||
bool bit_buffer::write_bytes(const size_t bytes, const unsigned char* data)
|
||||
{
|
||||
return this->write(static_cast<unsigned>(bytes) * 8, data);
|
||||
}
|
||||
|
||||
bool bit_buffer::write_bool(bool data)
|
||||
{
|
||||
if (this->write_data_type(1))
|
||||
{
|
||||
return this->write(1, &data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bit_buffer::write_int32(int data)
|
||||
{
|
||||
if (this->write_data_type(7))
|
||||
{
|
||||
return this->write(32, &data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bit_buffer::write_uint32(unsigned int data)
|
||||
{
|
||||
if (this->write_data_type(8))
|
||||
{
|
||||
return this->write(32, &data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bit_buffer::write_data_type(char data)
|
||||
{
|
||||
if (!this->use_data_types_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this->write(5, &data);
|
||||
}
|
||||
|
||||
bool bit_buffer::read(unsigned int bits, void* output)
|
||||
{
|
||||
if (bits == 0) return false;
|
||||
if ((this->current_bit_ + bits) > (this->buffer_.size() * 8)) return false;
|
||||
|
||||
int cur_byte = this->current_bit_ >> 3;
|
||||
auto cur_out = 0;
|
||||
|
||||
const char* bytes = this->buffer_.data();
|
||||
const auto output_bytes = reinterpret_cast<unsigned char*>(output);
|
||||
|
||||
while (bits > 0)
|
||||
{
|
||||
const int min_bit = (bits < 8) ? bits : 8;
|
||||
const auto this_byte = bytes[cur_byte++] & 0xFF;
|
||||
const int remain = this->current_bit_ & 7;
|
||||
|
||||
if ((min_bit + remain) <= 8)
|
||||
{
|
||||
output_bytes[cur_out] = BYTE((0xFF >> (8 - min_bit)) & (this_byte >> remain));
|
||||
}
|
||||
else
|
||||
{
|
||||
output_bytes[cur_out] = BYTE(
|
||||
(0xFF >> (8 - min_bit)) & (bytes[cur_byte] << (8 - remain)) | (this_byte >> remain));
|
||||
}
|
||||
|
||||
cur_out++;
|
||||
this->current_bit_ += min_bit;
|
||||
bits -= min_bit;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bit_buffer::write(const unsigned int bits, const void* data)
|
||||
{
|
||||
if (bits == 0) return false;
|
||||
this->buffer_.resize(this->buffer_.size() + (bits >> 3) + 1);
|
||||
|
||||
int bit = bits;
|
||||
const auto bytes = const_cast<char*>(this->buffer_.data());
|
||||
const auto* input_bytes = reinterpret_cast<const unsigned char*>(data);
|
||||
|
||||
while (bit > 0)
|
||||
{
|
||||
const int bit_pos = this->current_bit_ & 7;
|
||||
auto rem_bit = 8 - bit_pos;
|
||||
const auto this_write = (bit < rem_bit) ? bit : rem_bit;
|
||||
|
||||
const BYTE mask = ((0xFF >> rem_bit) | (0xFF << (bit_pos + this_write)));
|
||||
const int byte_pos = this->current_bit_ >> 3;
|
||||
|
||||
const BYTE temp_byte = (mask & bytes[byte_pos]);
|
||||
const BYTE this_bit = ((bits - bit) & 7);
|
||||
const auto this_byte = (bits - bit) >> 3;
|
||||
|
||||
auto this_data = input_bytes[this_byte];
|
||||
|
||||
const auto next_byte = (((bits - 1) >> 3) > this_byte) ? input_bytes[this_byte + 1] : 0;
|
||||
|
||||
this_data = BYTE((next_byte << (8 - this_bit)) | (this_data >> this_bit));
|
||||
|
||||
const BYTE out_byte = (~mask & (this_data << bit_pos) | temp_byte);
|
||||
bytes[byte_pos] = out_byte;
|
||||
|
||||
this->current_bit_ += this_write;
|
||||
bit -= this_write;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bit_buffer::set_use_data_types(const bool use_data_types)
|
||||
{
|
||||
this->use_data_types_ = use_data_types;
|
||||
}
|
||||
|
||||
unsigned int bit_buffer::size() const
|
||||
{
|
||||
return this->current_bit_ / 8 + (this->current_bit_ % 8 ? 1 : 0);
|
||||
}
|
||||
|
||||
std::string& bit_buffer::get_buffer()
|
||||
{
|
||||
this->buffer_.resize(this->size());
|
||||
return this->buffer_;
|
||||
}
|
||||
}
|
40
src/client/game/demonware/bit_buffer.hpp
Normal file
40
src/client/game/demonware/bit_buffer.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bit_buffer final
|
||||
{
|
||||
public:
|
||||
bit_buffer() = default;
|
||||
|
||||
explicit bit_buffer(std::string buffer) : buffer_(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
bool read_bytes(unsigned int bytes, unsigned char* output);
|
||||
bool read_bool(bool* output);
|
||||
bool read_uint32(unsigned int* output);
|
||||
bool read_data_type(char expected);
|
||||
|
||||
bool write_bytes(size_t bytes, const char* data);
|
||||
bool write_bytes(size_t bytes, const unsigned char* data);
|
||||
bool write_bool(bool data);
|
||||
bool write_int32(int data);
|
||||
bool write_uint32(unsigned int data);
|
||||
bool write_data_type(char data);
|
||||
|
||||
bool read(unsigned int bits, void* output);
|
||||
bool write(unsigned int bits, const void* data);
|
||||
|
||||
void set_use_data_types(bool use_data_types);
|
||||
|
||||
unsigned int size() const;
|
||||
|
||||
std::string& get_buffer();
|
||||
|
||||
private:
|
||||
std::string buffer_{};
|
||||
unsigned int current_bit_ = 0;
|
||||
bool use_data_types_ = true;
|
||||
};
|
||||
}
|
313
src/client/game/demonware/byte_buffer.cpp
Normal file
313
src/client/game/demonware/byte_buffer.cpp
Normal file
@ -0,0 +1,313 @@
|
||||
#include <std_include.hpp>
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bool byte_buffer::read_byte(unsigned char* output)
|
||||
{
|
||||
if (!this->read_data_type(3)) return false;
|
||||
return this->read(1, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_bool(bool* output)
|
||||
{
|
||||
if (!this->read_data_type(1)) return false;
|
||||
return this->read(1, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_int16(short* output)
|
||||
{
|
||||
if (!this->read_data_type(5)) return false;
|
||||
return this->read(2, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_uint16(unsigned short* output)
|
||||
{
|
||||
if (!this->read_data_type(6)) return false;
|
||||
return this->read(2, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_int32(int* output)
|
||||
{
|
||||
if (!this->read_data_type(7)) return false;
|
||||
return this->read(4, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_uint32(unsigned int* output)
|
||||
{
|
||||
if (!this->read_data_type(8)) return false;
|
||||
return this->read(4, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_int64(__int64* output)
|
||||
{
|
||||
if (!this->read_data_type(9)) return false;
|
||||
return this->read(8, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_uint64(unsigned __int64* output)
|
||||
{
|
||||
if (!this->read_data_type(10)) return false;
|
||||
return this->read(8, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_float(float* output)
|
||||
{
|
||||
if (!this->read_data_type(13)) return false;
|
||||
return this->read(4, output);
|
||||
}
|
||||
|
||||
bool byte_buffer::read_string(std::string* output)
|
||||
{
|
||||
char* out_data;
|
||||
if (this->read_string(&out_data))
|
||||
{
|
||||
output->clear();
|
||||
output->append(out_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_string(char** output)
|
||||
{
|
||||
if (!this->read_data_type(16)) return false;
|
||||
|
||||
*output = const_cast<char*>(this->buffer_.data()) + this->current_byte_;
|
||||
this->current_byte_ += strlen(*output) + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_string(char* output, const int length)
|
||||
{
|
||||
if (!this->read_data_type(16)) return false;
|
||||
|
||||
strcpy_s(output, length, const_cast<char*>(this->buffer_.data()) + this->current_byte_);
|
||||
this->current_byte_ += strlen(output) + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_blob(std::string* output)
|
||||
{
|
||||
char* out_data;
|
||||
int length;
|
||||
if (this->read_blob(&out_data, &length))
|
||||
{
|
||||
output->clear();
|
||||
output->append(out_data, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_blob(char** output, int* length)
|
||||
{
|
||||
if (!this->read_data_type(0x13))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int size;
|
||||
this->read_uint32(&size);
|
||||
|
||||
*output = const_cast<char*>(this->buffer_.data()) + this->current_byte_;
|
||||
*length = static_cast<int>(size);
|
||||
|
||||
this->current_byte_ += size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_data_type(const char expected)
|
||||
{
|
||||
if (!this->use_data_types_) return true;
|
||||
|
||||
char type;
|
||||
this->read(1, &type);
|
||||
if (type != expected)
|
||||
{
|
||||
//throw std::runtime_error("Data type mismatch!");
|
||||
}
|
||||
|
||||
return type == expected;
|
||||
}
|
||||
|
||||
bool byte_buffer::read_array_header(const unsigned char expected, unsigned int* element_count,
|
||||
unsigned int* element_size)
|
||||
{
|
||||
if (element_count) *element_count = 0;
|
||||
if (element_size) *element_size = 0;
|
||||
|
||||
if (!this->read_data_type(expected + 100)) return false;
|
||||
|
||||
uint32_t array_size, el_count;
|
||||
if (!this->read_uint32(&array_size)) return false;
|
||||
|
||||
this->set_use_data_types(false);
|
||||
this->read_uint32(&el_count);
|
||||
this->set_use_data_types(true);
|
||||
|
||||
if (element_count) *element_count = el_count;
|
||||
if (element_size) *element_size = array_size / el_count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::write_byte(char data)
|
||||
{
|
||||
this->write_data_type(3);
|
||||
return this->write(1, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_bool(bool data)
|
||||
{
|
||||
this->write_data_type(1);
|
||||
return this->write(1, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_int16(short data)
|
||||
{
|
||||
this->write_data_type(5);
|
||||
return this->write(2, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_uint16(unsigned short data)
|
||||
{
|
||||
this->write_data_type(6);
|
||||
return this->write(2, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_int32(int data)
|
||||
{
|
||||
this->write_data_type(7);
|
||||
return this->write(4, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_uint32(unsigned int data)
|
||||
{
|
||||
this->write_data_type(8);
|
||||
return this->write(4, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_int64(__int64 data)
|
||||
{
|
||||
this->write_data_type(9);
|
||||
return this->write(8, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_uint64(unsigned __int64 data)
|
||||
{
|
||||
this->write_data_type(10);
|
||||
return this->write(8, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_data_type(char data)
|
||||
{
|
||||
if (!this->use_data_types_) return true;
|
||||
return this->write(1, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_float(float data)
|
||||
{
|
||||
this->write_data_type(13);
|
||||
return this->write(4, &data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_string(const std::string& data)
|
||||
{
|
||||
return this->write_string(data.data());
|
||||
}
|
||||
|
||||
bool byte_buffer::write_string(const char* data)
|
||||
{
|
||||
this->write_data_type(16);
|
||||
return this->write(static_cast<int>(strlen(data)) + 1, data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_blob(const std::string& data)
|
||||
{
|
||||
return this->write_blob(data.data(), INT(data.size()));
|
||||
}
|
||||
|
||||
bool byte_buffer::write_blob(const char* data, const int length)
|
||||
{
|
||||
this->write_data_type(0x13);
|
||||
this->write_uint32(length);
|
||||
|
||||
return this->write(length, data);
|
||||
}
|
||||
|
||||
bool byte_buffer::write_array_header(const unsigned char type, const unsigned int element_count,
|
||||
const unsigned int element_size)
|
||||
{
|
||||
const auto using_types = this->is_using_data_types();
|
||||
this->set_use_data_types(false);
|
||||
|
||||
auto result = this->write_byte(type + 100);
|
||||
|
||||
this->set_use_data_types(true);
|
||||
result &= this->write_uint32(element_count * element_size);
|
||||
this->set_use_data_types(false);
|
||||
|
||||
result &= this->write_uint32(element_count);
|
||||
|
||||
this->set_use_data_types(using_types);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool byte_buffer::read(const size_t bytes, void* output)
|
||||
{
|
||||
if (bytes + this->current_byte_ > this->buffer_.size()) return false;
|
||||
|
||||
std::memmove(output, this->buffer_.data() + this->current_byte_, bytes);
|
||||
this->current_byte_ += bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::write(const size_t bytes, const void* data)
|
||||
{
|
||||
this->buffer_.append(static_cast<const char*>(data), bytes);
|
||||
this->current_byte_ += bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool byte_buffer::write(const std::string& data)
|
||||
{
|
||||
return this->write(data.size(), data.data());
|
||||
}
|
||||
|
||||
void byte_buffer::set_use_data_types(const bool use_data_types)
|
||||
{
|
||||
this->use_data_types_ = use_data_types;
|
||||
}
|
||||
|
||||
size_t byte_buffer::size() const
|
||||
{
|
||||
return this->buffer_.size();
|
||||
}
|
||||
|
||||
bool byte_buffer::is_using_data_types() const
|
||||
{
|
||||
return use_data_types_;
|
||||
}
|
||||
|
||||
std::string& byte_buffer::get_buffer()
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::string byte_buffer::get_remaining()
|
||||
{
|
||||
return std::string(this->buffer_.begin() + this->current_byte_, this->buffer_.end());
|
||||
}
|
||||
|
||||
bool byte_buffer::has_more_data() const
|
||||
{
|
||||
return this->buffer_.size() > this->current_byte_;
|
||||
}
|
||||
}
|
69
src/client/game/demonware/byte_buffer.hpp
Normal file
69
src/client/game/demonware/byte_buffer.hpp
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class byte_buffer final
|
||||
{
|
||||
public:
|
||||
byte_buffer() = default;
|
||||
|
||||
explicit byte_buffer(std::string buffer) : buffer_(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
bool read_byte(unsigned char* output);
|
||||
bool read_bool(bool* output);
|
||||
bool read_int16(short* output);
|
||||
bool read_uint16(unsigned short* output);
|
||||
bool read_int32(int* output);
|
||||
bool read_uint32(unsigned int* output);
|
||||
bool read_int64(__int64* output);
|
||||
bool read_uint64(unsigned __int64* output);
|
||||
bool read_float(float* output);
|
||||
bool read_string(char** output);
|
||||
bool read_string(char* output, int length);
|
||||
bool read_string(std::string* output);
|
||||
bool read_blob(char** output, int* length);
|
||||
bool read_blob(std::string* output);
|
||||
bool read_data_type(char expected);
|
||||
|
||||
bool read_array_header(unsigned char expected, unsigned int* element_count,
|
||||
unsigned int* element_size = nullptr);
|
||||
|
||||
bool write_byte(char data);
|
||||
bool write_bool(bool data);
|
||||
bool write_int16(short data);
|
||||
bool write_uint16(unsigned short data);
|
||||
bool write_int32(int data);
|
||||
bool write_uint32(unsigned int data);
|
||||
bool write_int64(__int64 data);
|
||||
bool write_uint64(unsigned __int64 data);
|
||||
bool write_data_type(char data);
|
||||
bool write_float(float data);
|
||||
bool write_string(const char* data);
|
||||
bool write_string(const std::string& data);
|
||||
bool write_blob(const char* data, int length);
|
||||
bool write_blob(const std::string& data);
|
||||
|
||||
bool write_array_header(unsigned char type, unsigned int element_count, unsigned int element_size);
|
||||
|
||||
bool read(size_t bytes, void* output);
|
||||
bool write(size_t bytes, const void* data);
|
||||
bool write(const std::string& data);
|
||||
|
||||
void set_use_data_types(bool use_data_types);
|
||||
size_t size() const;
|
||||
|
||||
bool is_using_data_types() const;
|
||||
|
||||
std::string& get_buffer();
|
||||
std::string get_remaining();
|
||||
|
||||
bool has_more_data() const;
|
||||
|
||||
private:
|
||||
std::string buffer_;
|
||||
size_t current_byte_ = 0;
|
||||
bool use_data_types_ = true;
|
||||
};
|
||||
}
|
449
src/client/game/demonware/data_types.hpp
Normal file
449
src/client/game/demonware/data_types.hpp
Normal file
@ -0,0 +1,449 @@
|
||||
#pragma once
|
||||
#include "i_server.hpp"
|
||||
#include "game/structs.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdFileData final : public i_serializable
|
||||
{
|
||||
public:
|
||||
std::string file_data;
|
||||
|
||||
explicit bdFileData(std::string buffer) : file_data(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_blob(this->file_data);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_blob(&this->file_data);
|
||||
}
|
||||
};
|
||||
|
||||
class bdFileInfo final : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint64_t file_id;
|
||||
uint32_t create_time;
|
||||
uint32_t modified_time;
|
||||
bool priv;
|
||||
uint64_t owner_id;
|
||||
std::string filename;
|
||||
uint32_t file_size;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint32(this->file_size);
|
||||
buffer->write_uint64(this->file_id);
|
||||
buffer->write_uint32(this->create_time);
|
||||
buffer->write_uint32(this->modified_time);
|
||||
buffer->write_bool(this->priv);
|
||||
buffer->write_uint64(this->owner_id);
|
||||
buffer->write_string(this->filename);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint32(&this->file_size);
|
||||
buffer->read_uint64(&this->file_id);
|
||||
buffer->read_uint32(&this->create_time);
|
||||
buffer->read_uint32(&this->modified_time);
|
||||
buffer->read_bool(&this->priv);
|
||||
buffer->read_uint64(&this->owner_id);
|
||||
buffer->read_string(&this->filename);
|
||||
}
|
||||
};
|
||||
|
||||
class bdGroupCount final : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint32_t group_id;
|
||||
uint32_t group_count;
|
||||
|
||||
bdGroupCount()
|
||||
{
|
||||
this->group_id = 0;
|
||||
this->group_count = 0;
|
||||
}
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint32(this->group_id);
|
||||
buffer->write_uint32(this->group_count);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint32(&this->group_id);
|
||||
buffer->read_uint32(&this->group_count);
|
||||
}
|
||||
};
|
||||
|
||||
class bdTimeStamp final : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint32_t unix_time;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint32(this->unix_time);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint32(&this->unix_time);
|
||||
}
|
||||
};
|
||||
|
||||
class bdDMLInfo : public i_serializable
|
||||
{
|
||||
public:
|
||||
std::string country_code; // Char [3]
|
||||
std::string country; // Char [65]
|
||||
std::string region; // Char [65]
|
||||
std::string city; // Char [129]
|
||||
float latitude;
|
||||
float longitude;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_string(this->country_code);
|
||||
buffer->write_string(this->country);
|
||||
buffer->write_string(this->region);
|
||||
buffer->write_string(this->city);
|
||||
buffer->write_float(this->latitude);
|
||||
buffer->write_float(this->longitude);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_string(&this->country_code);
|
||||
buffer->read_string(&this->country);
|
||||
buffer->read_string(&this->region);
|
||||
buffer->read_string(&this->city);
|
||||
buffer->read_float(&this->latitude);
|
||||
buffer->read_float(&this->longitude);
|
||||
}
|
||||
};
|
||||
|
||||
class bdDMLRawData final : public bdDMLInfo
|
||||
{
|
||||
public:
|
||||
uint32_t asn; // Autonomous System Number.
|
||||
std::string timezone;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
bdDMLInfo::serialize(buffer);
|
||||
|
||||
buffer->write_uint32(this->asn);
|
||||
buffer->write_string(this->timezone);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
bdDMLInfo::deserialize(buffer);
|
||||
|
||||
buffer->read_uint32(&this->asn);
|
||||
buffer->read_string(&this->timezone);
|
||||
}
|
||||
};
|
||||
|
||||
class bdSessionID final : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint64_t session_id;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_blob(LPSTR(&this->session_id), sizeof this->session_id);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
int size{};
|
||||
char* data{};
|
||||
buffer->read_blob(&data, &size);
|
||||
|
||||
if (data && uint32_t(size) >= sizeof(this->session_id))
|
||||
{
|
||||
this->session_id = *reinterpret_cast<uint64_t*>(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class bdMatchmakingInfo : public i_serializable
|
||||
{
|
||||
public:
|
||||
bdSessionID session_id{};
|
||||
std::string host_addr{};
|
||||
uint32_t game_type{};
|
||||
uint32_t max_players{};
|
||||
uint32_t num_players{};
|
||||
|
||||
bool symmetric = false;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_blob(this->host_addr);
|
||||
this->session_id.serialize(buffer);
|
||||
buffer->write_uint32(this->game_type);
|
||||
buffer->write_uint32(this->max_players);
|
||||
buffer->write_uint32(this->num_players);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_blob(&this->host_addr);
|
||||
|
||||
if (this->symmetric) this->session_id.deserialize(buffer);
|
||||
|
||||
buffer->read_uint32(&this->game_type);
|
||||
buffer->read_uint32(&this->max_players);
|
||||
|
||||
if (this->symmetric) buffer->read_uint32(&this->num_players);
|
||||
}
|
||||
};
|
||||
|
||||
class MatchMakingInfo final : public bdMatchmakingInfo
|
||||
{
|
||||
public:
|
||||
int32_t playlist_number{};
|
||||
int32_t playlist_version{};
|
||||
int32_t netcode_version{};
|
||||
int32_t map_packs{};
|
||||
int32_t slots_needed_on_team{};
|
||||
int32_t skill{};
|
||||
uint32_t country_code{};
|
||||
uint32_t asn{};
|
||||
float latitude{};
|
||||
float longitude{};
|
||||
int32_t max_reserved_slots{};
|
||||
int32_t used_reserved_slots{};
|
||||
std::string game_security_key{}; // 16 bytes.
|
||||
std::string platform_session_id{}; // 16 bytes.
|
||||
uint32_t data_centres{};
|
||||
uint32_t coop_state{};
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
bdMatchmakingInfo::serialize(buffer);
|
||||
|
||||
buffer->write_int32(this->playlist_number);
|
||||
buffer->write_int32(this->playlist_version);
|
||||
buffer->write_int32(this->netcode_version);
|
||||
buffer->write_int32(this->map_packs);
|
||||
buffer->write_int32(this->slots_needed_on_team);
|
||||
buffer->write_int32(this->skill);
|
||||
buffer->write_uint32(this->country_code);
|
||||
buffer->write_uint32(this->asn);
|
||||
buffer->write_float(this->latitude);
|
||||
buffer->write_float(this->longitude);
|
||||
buffer->write_int32(this->max_reserved_slots);
|
||||
buffer->write_int32(this->used_reserved_slots);
|
||||
buffer->write_blob(this->game_security_key);
|
||||
buffer->write_blob(this->platform_session_id);
|
||||
buffer->write_uint32(this->data_centres);
|
||||
buffer->write_uint32(this->coop_state);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
bdMatchmakingInfo::deserialize(buffer);
|
||||
|
||||
buffer->read_int32(&this->playlist_number);
|
||||
buffer->read_int32(&this->playlist_version);
|
||||
buffer->read_int32(&this->netcode_version);
|
||||
buffer->read_int32(&this->map_packs);
|
||||
buffer->read_int32(&this->slots_needed_on_team);
|
||||
buffer->read_int32(&this->skill);
|
||||
buffer->read_uint32(&this->country_code);
|
||||
buffer->read_uint32(&this->asn);
|
||||
buffer->read_float(&this->latitude);
|
||||
buffer->read_float(&this->longitude);
|
||||
buffer->read_int32(&this->max_reserved_slots);
|
||||
buffer->read_int32(&this->used_reserved_slots);
|
||||
buffer->read_blob(&this->game_security_key);
|
||||
buffer->read_blob(&this->platform_session_id);
|
||||
buffer->read_uint32(&this->data_centres);
|
||||
buffer->read_uint32(&this->coop_state);
|
||||
}
|
||||
};
|
||||
|
||||
class bdPerformanceValue final : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint64_t user_id;
|
||||
int64_t performance;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint64(this->user_id);
|
||||
buffer->write_int64(this->performance);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint64(&this->user_id);
|
||||
buffer->read_int64(&this->performance);
|
||||
}
|
||||
};
|
||||
|
||||
struct bdSockAddr final
|
||||
{
|
||||
bdSockAddr() : in_un(), m_family(AF_INET)
|
||||
{
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
char m_b1;
|
||||
char m_b2;
|
||||
char m_b3;
|
||||
char m_b4;
|
||||
} m_caddr;
|
||||
|
||||
unsigned int m_iaddr;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned __int16 m_w1;
|
||||
unsigned __int16 m_w2;
|
||||
unsigned __int16 m_w3;
|
||||
unsigned __int16 m_w4;
|
||||
unsigned __int16 m_w5;
|
||||
unsigned __int16 m_w6;
|
||||
unsigned __int16 m_w7;
|
||||
unsigned __int16 m_w8;
|
||||
} m_caddr6;
|
||||
|
||||
char m_iaddr6[16];
|
||||
char m_sockaddr_storage[128];
|
||||
} in_un;
|
||||
|
||||
unsigned __int16 m_family;
|
||||
};
|
||||
|
||||
struct bdInetAddr final : i_serializable
|
||||
{
|
||||
bdSockAddr m_addr;
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return (this->m_addr.m_family == AF_INET /*|| this->m_addr.m_family == AF_INET6*/);
|
||||
}
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
const auto data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
if (this->m_addr.m_family == AF_INET)
|
||||
{
|
||||
buffer->write(4, &this->m_addr.in_un.m_caddr);
|
||||
}
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
const auto data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
if (this->m_addr.m_family == AF_INET)
|
||||
{
|
||||
buffer->read(4, &this->m_addr.in_un.m_caddr);
|
||||
}
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
};
|
||||
|
||||
struct bdAddr final : i_serializable
|
||||
{
|
||||
bdInetAddr m_address;
|
||||
unsigned __int16 m_port{};
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
const bool data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
this->m_address.serialize(buffer);
|
||||
buffer->write_uint16(this->m_port);
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
const auto data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
this->m_address.deserialize(buffer);
|
||||
buffer->read_uint16(&this->m_port);
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
};
|
||||
|
||||
struct bdCommonAddr : i_serializable
|
||||
{
|
||||
bdAddr m_local_addrs[5];
|
||||
bdAddr m_public_addr;
|
||||
game::bdNATType m_nat_type;
|
||||
unsigned int m_hash;
|
||||
bool m_is_loopback;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
const auto data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
auto valid = true;
|
||||
for (uint32_t i = 0; i < 5 && i < ARRAYSIZE(this->m_local_addrs) && valid; ++i)
|
||||
{
|
||||
this->m_local_addrs[i].serialize(buffer);
|
||||
valid = this->m_local_addrs[i].m_address.is_valid();
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
this->m_public_addr.serialize(buffer);
|
||||
buffer->write_byte(this->m_nat_type);
|
||||
}
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
const auto data_types = buffer->is_using_data_types();
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
auto valid = true;
|
||||
for (uint32_t i = 0; i < ARRAYSIZE(this->m_local_addrs) && valid; ++i)
|
||||
{
|
||||
bdAddr addr;
|
||||
addr.deserialize(buffer);
|
||||
this->m_local_addrs[i] = addr;
|
||||
valid = this->m_local_addrs[i].m_address.is_valid();
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
this->m_public_addr.deserialize(buffer);
|
||||
buffer->read_byte(reinterpret_cast<uint8_t*>(&this->m_nat_type));
|
||||
}
|
||||
|
||||
buffer->set_use_data_types(data_types);
|
||||
}
|
||||
};
|
||||
}
|
201
src/client/game/demonware/i_server.hpp
Normal file
201
src/client/game/demonware/i_server.hpp
Normal file
@ -0,0 +1,201 @@
|
||||
#pragma once
|
||||
#include "bit_buffer.hpp"
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class reply
|
||||
{
|
||||
public:
|
||||
virtual ~reply() = default;
|
||||
virtual std::string get_data() = 0;
|
||||
};
|
||||
|
||||
class raw_reply : public reply
|
||||
{
|
||||
public:
|
||||
raw_reply() = default;
|
||||
|
||||
explicit raw_reply(std::string data) : buffer_(std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string get_data() override
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string buffer_;
|
||||
};
|
||||
|
||||
class typed_reply : public raw_reply
|
||||
{
|
||||
public:
|
||||
typed_reply(uint8_t _type) : type_(_type)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t get_type() const { return this->type_; }
|
||||
|
||||
private:
|
||||
uint8_t type_;
|
||||
};
|
||||
|
||||
class encrypted_reply final : public typed_reply
|
||||
{
|
||||
public:
|
||||
encrypted_reply(const uint8_t type, bit_buffer* bbuffer) : typed_reply(type)
|
||||
{
|
||||
this->buffer_.append(bbuffer->get_buffer());
|
||||
}
|
||||
|
||||
encrypted_reply(const uint8_t type, byte_buffer* bbuffer) : typed_reply(type)
|
||||
{
|
||||
this->buffer_.append(bbuffer->get_buffer());
|
||||
}
|
||||
|
||||
virtual std::string get_data() override;
|
||||
};
|
||||
|
||||
class unencrypted_reply final : public typed_reply
|
||||
{
|
||||
public:
|
||||
unencrypted_reply(uint8_t _type, bit_buffer* bbuffer) : typed_reply(_type)
|
||||
{
|
||||
this->buffer_.append(bbuffer->get_buffer());
|
||||
}
|
||||
|
||||
unencrypted_reply(uint8_t _type, byte_buffer* bbuffer) : typed_reply(_type)
|
||||
{
|
||||
this->buffer_.append(bbuffer->get_buffer());
|
||||
}
|
||||
|
||||
virtual std::string get_data() override;
|
||||
};
|
||||
|
||||
class remote_reply;
|
||||
class service_reply;
|
||||
|
||||
class i_server
|
||||
{
|
||||
public:
|
||||
virtual ~i_server() = default;
|
||||
virtual int send(const char* buf, int len) = 0;
|
||||
virtual int recv(char* buf, int len) = 0;
|
||||
|
||||
virtual void send_reply(reply* reply) = 0;
|
||||
|
||||
virtual std::shared_ptr<remote_reply> create_message(uint8_t type)
|
||||
{
|
||||
auto reply = std::make_shared<remote_reply>(this, type);
|
||||
return reply;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<service_reply> create_reply(uint8_t type,
|
||||
uint32_t error = 0 /*Game::bdLobbyErrorCode::BD_NO_ERROR*/)
|
||||
{
|
||||
auto reply = std::make_shared<service_reply>(this, type, error);
|
||||
return reply;
|
||||
}
|
||||
};
|
||||
|
||||
class remote_reply final
|
||||
{
|
||||
public:
|
||||
remote_reply(i_server* server, uint8_t _type) : type_(_type), server_(server)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename BufferType>
|
||||
void send(BufferType* buffer, const bool encrypted)
|
||||
{
|
||||
std::unique_ptr<typed_reply> reply;
|
||||
|
||||
if (encrypted) reply = std::make_unique<encrypted_reply>(this->type_, buffer);
|
||||
else reply = std::make_unique<unencrypted_reply>(this->type_, buffer);
|
||||
this->server_->send_reply(reply.get());
|
||||
}
|
||||
|
||||
uint8_t get_type() const { return this->type_; }
|
||||
|
||||
private:
|
||||
uint8_t type_;
|
||||
i_server* server_;
|
||||
};
|
||||
|
||||
class i_serializable
|
||||
{
|
||||
public:
|
||||
virtual ~i_serializable() = default;
|
||||
|
||||
virtual void serialize(byte_buffer* /*buffer*/)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void deserialize(byte_buffer* /*buffer*/)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class service_reply final
|
||||
{
|
||||
public:
|
||||
service_reply(i_server* _server, uint8_t _type, uint32_t _error) : type_(_type), error_(_error),
|
||||
reply_(_server, 1)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t send()
|
||||
{
|
||||
static uint64_t id = 0x8000000000000001;
|
||||
const auto transaction_id = ++id;
|
||||
|
||||
byte_buffer buffer;
|
||||
buffer.write_uint64(transaction_id);
|
||||
buffer.write_uint32(this->error_);
|
||||
buffer.write_byte(this->type_);
|
||||
|
||||
if (!this->error_)
|
||||
{
|
||||
buffer.write_uint32(uint32_t(this->objects_.size()));
|
||||
if (!this->objects_.empty())
|
||||
{
|
||||
buffer.write_uint32(uint32_t(this->objects_.size()));
|
||||
|
||||
for (auto& object : this->objects_)
|
||||
{
|
||||
object->serialize(&buffer);
|
||||
}
|
||||
|
||||
this->objects_.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.write_uint64(transaction_id);
|
||||
}
|
||||
|
||||
this->reply_.send(&buffer, true);
|
||||
return transaction_id;
|
||||
}
|
||||
|
||||
void add(const std::shared_ptr<i_serializable>& object)
|
||||
{
|
||||
this->objects_.push_back(object);
|
||||
}
|
||||
|
||||
void add(i_serializable* object)
|
||||
{
|
||||
this->add(std::shared_ptr<i_serializable>(object));
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t type_;
|
||||
uint32_t error_;
|
||||
remote_reply reply_;
|
||||
|
||||
std::vector<std::shared_ptr<i_serializable>> objects_;
|
||||
};
|
||||
}
|
82
src/client/game/demonware/i_service.hpp
Normal file
82
src/client/game/demonware/i_service.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "i_server.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class i_service
|
||||
{
|
||||
public:
|
||||
virtual ~i_service() = default;
|
||||
i_service() = default;
|
||||
|
||||
// Copying or moving a service object won't work
|
||||
// as the callbacks are bound to the initial object pointer
|
||||
// Therefore, you should never declare copy/move
|
||||
// constructors when inheriting from IService!
|
||||
i_service(i_service&&) = delete;
|
||||
i_service(const i_service&) = delete;
|
||||
i_service& operator=(const i_service&) = delete;
|
||||
|
||||
typedef std::function<void(i_server*, byte_buffer*)> callback;
|
||||
|
||||
virtual uint16_t getType() = 0;
|
||||
|
||||
virtual void call_service(i_server* server, const std::string& data)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
byte_buffer buffer(data);
|
||||
buffer.read_byte(&this->sub_type_);
|
||||
#ifdef DEBUG
|
||||
printf("DW: Handling subservice of type %d\n", this->sub_type_);
|
||||
#endif
|
||||
|
||||
const auto callback = this->callbacks_.find(this->sub_type_);
|
||||
if (callback != this->callbacks_.end())
|
||||
{
|
||||
callback->second(server, &buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("DW: Missing subservice %d for type %d\n", this->sub_type_, this->getType());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::map<uint8_t, callback> callbacks_{};
|
||||
|
||||
template <typename Class, typename T, typename... Args>
|
||||
void register_service(const uint8_t type, T (Class::*callback)(Args ...) const)
|
||||
{
|
||||
this->callbacks_[type] = [this, callback](Args ... args) -> T
|
||||
{
|
||||
return (reinterpret_cast<Class*>(this)->*callback)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Class, typename T, typename... Args>
|
||||
void register_service(const uint8_t type, T (Class::*callback)(Args ...))
|
||||
{
|
||||
this->callbacks_[type] = [this, callback](Args ... args) -> T
|
||||
{
|
||||
return (reinterpret_cast<Class*>(this)->*callback)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
uint8_t get_sub_type() const { return this->sub_type_; }
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
|
||||
uint8_t sub_type_{};
|
||||
};
|
||||
|
||||
template <uint16_t Type>
|
||||
class i_generic_service : public i_service
|
||||
{
|
||||
public:
|
||||
uint16_t getType() override { return Type; }
|
||||
};
|
||||
}
|
206
src/client/game/demonware/service_server.cpp
Normal file
206
src/client/game/demonware/service_server.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
#include <std_include.hpp>
|
||||
#include "component/demonware.hpp"
|
||||
#include "game/demonware/service_server.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
std::string unencrypted_reply::get_data()
|
||||
{
|
||||
byte_buffer result;
|
||||
result.set_use_data_types(false);
|
||||
|
||||
result.write_int32(static_cast<int>(this->buffer_.size()) + 2);
|
||||
result.write_bool(false);
|
||||
result.write_byte(this->get_type());
|
||||
result.write(this->buffer_);
|
||||
|
||||
return result.get_buffer();
|
||||
}
|
||||
|
||||
std::string encrypted_reply::get_data()
|
||||
{
|
||||
byte_buffer result;
|
||||
result.set_use_data_types(false);
|
||||
|
||||
byte_buffer enc_buffer;
|
||||
enc_buffer.set_use_data_types(false);
|
||||
enc_buffer.write_int32(0xDEADBEEF);
|
||||
enc_buffer.write_byte(this->get_type());
|
||||
enc_buffer.write(this->buffer_);
|
||||
|
||||
auto data = enc_buffer.get_buffer();
|
||||
|
||||
auto size = enc_buffer.size();
|
||||
size = ~7 & (size + 7); // 8 byte align
|
||||
data.resize(size);
|
||||
|
||||
result.write_int32(static_cast<int>(size) + 5);
|
||||
result.write_byte(true);
|
||||
|
||||
auto seed = 0x13371337;
|
||||
result.write_int32(seed);
|
||||
|
||||
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
|
||||
|
||||
const std::string key(reinterpret_cast<char*>(get_key(true)), 24);
|
||||
result.write(utils::cryptography::des3::encrypt(data, iv, key));
|
||||
|
||||
return result.get_buffer();
|
||||
}
|
||||
|
||||
service_server::service_server(std::string _name) : name_(std::move(_name))
|
||||
{
|
||||
this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_);
|
||||
}
|
||||
|
||||
unsigned long service_server::get_address() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
int service_server::send(const char* buf, const int len)
|
||||
{
|
||||
if (len <= 3) return -1;
|
||||
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||
|
||||
this->incoming_queue_.push(std::string(buf, len));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int service_server::recv(char* buf, int len)
|
||||
{
|
||||
if (len > 0 && !this->outgoing_queue_.empty())
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||
|
||||
len = std::min(len, static_cast<int>(this->outgoing_queue_.size()));
|
||||
for (auto i = 0; i < len; ++i)
|
||||
{
|
||||
buf[i] = this->outgoing_queue_.front();
|
||||
this->outgoing_queue_.pop();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
void service_server::send_reply(reply* data)
|
||||
{
|
||||
if (!data) return;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||
|
||||
this->reply_sent_ = true;
|
||||
const auto buffer = data->get_data();
|
||||
for (const auto& byte : buffer)
|
||||
{
|
||||
this->outgoing_queue_.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void service_server::call_handler(const uint8_t type, const std::string& data)
|
||||
{
|
||||
if (this->services_.find(type) != this->services_.end())
|
||||
{
|
||||
this->services_[type]->call_service(this, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("DW: Missing handler of type %d\n", type);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void service_server::run_frame()
|
||||
{
|
||||
if (!this->incoming_queue_.empty())
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||
const auto packet = this->incoming_queue_.front();
|
||||
this->incoming_queue_.pop();
|
||||
|
||||
this->parse_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void service_server::parse_packet(const std::string& packet)
|
||||
{
|
||||
byte_buffer buffer(packet);
|
||||
buffer.set_use_data_types(false);
|
||||
|
||||
try
|
||||
{
|
||||
while (buffer.has_more_data())
|
||||
{
|
||||
int size;
|
||||
buffer.read_int32(&size);
|
||||
|
||||
if (size <= 0)
|
||||
{
|
||||
const std::string zero("\x00\x00\x00\x00", 4);
|
||||
|
||||
raw_reply reply(zero);
|
||||
this->send_reply(&reply);
|
||||
return;
|
||||
}
|
||||
else if (size == 200) // Connection id
|
||||
{
|
||||
byte_buffer bbufer;
|
||||
bbufer.write_uint64(0x00000000000000FD);
|
||||
|
||||
auto reply = this->create_message(4);
|
||||
reply->send(&bbufer, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.size() < size_t(size)) return;
|
||||
|
||||
byte_buffer p_buffer;
|
||||
p_buffer.set_use_data_types(false);
|
||||
p_buffer.get_buffer().resize(size);
|
||||
buffer.read(size, p_buffer.get_buffer().data());
|
||||
|
||||
bool enc;
|
||||
p_buffer.read_bool(&enc);
|
||||
|
||||
if (enc)
|
||||
{
|
||||
int iv;
|
||||
p_buffer.read_int32(&iv);
|
||||
|
||||
auto iv_hash = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&iv), 4));
|
||||
|
||||
const std::string key(reinterpret_cast<char*>(get_key(false)), 24);
|
||||
p_buffer = byte_buffer{utils::cryptography::des3::decrypt(p_buffer.get_remaining(), iv_hash, key)};
|
||||
p_buffer.set_use_data_types(false);
|
||||
|
||||
int checksum;
|
||||
p_buffer.read_int32(&checksum);
|
||||
}
|
||||
|
||||
uint8_t type;
|
||||
p_buffer.read_byte(&type);
|
||||
#ifdef DEBUG
|
||||
printf("DW: Handling message of type %d (encrypted: %d)\n", type, enc);
|
||||
#endif
|
||||
|
||||
this->reply_sent_ = false;
|
||||
this->call_handler(type, p_buffer.get_remaining());
|
||||
|
||||
if (!this->reply_sent_ && type != 7)
|
||||
{
|
||||
this->create_reply(type)->send();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
43
src/client/game/demonware/service_server.hpp
Normal file
43
src/client/game/demonware/service_server.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class service_server final : public i_server
|
||||
{
|
||||
public:
|
||||
explicit service_server(std::string name);
|
||||
|
||||
template <typename T>
|
||||
void register_service()
|
||||
{
|
||||
static_assert(std::is_base_of<i_service, T>::value, "Service must inherit from IService");
|
||||
|
||||
auto service = std::make_unique<T>();
|
||||
const uint16_t type = service->getType();
|
||||
|
||||
this->services_[type] = std::move(service);
|
||||
}
|
||||
|
||||
unsigned long get_address() const;
|
||||
|
||||
int send(const char* buf, int len) override;
|
||||
int recv(char* buf, int len) override;
|
||||
void send_reply(reply* data) override;
|
||||
|
||||
void call_handler(uint8_t type, const std::string& data);
|
||||
void run_frame();
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
|
||||
std::recursive_mutex mutex_;
|
||||
std::queue<char> outgoing_queue_;
|
||||
std::queue<std::string> incoming_queue_;
|
||||
std::map<uint16_t, std::unique_ptr<i_service>> services_;
|
||||
unsigned long address_ = 0;
|
||||
bool reply_sent_ = false;
|
||||
|
||||
void parse_packet(const std::string& packet);
|
||||
};
|
||||
}
|
18
src/client/game/demonware/services/bdAnticheat.cpp
Normal file
18
src/client/game/demonware/services/bdAnticheat.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdAnticheat.hpp"
|
||||
#include "../data_types.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bdAnticheat::bdAnticheat()
|
||||
{
|
||||
this->register_service(4, &bdAnticheat::report_console_details);
|
||||
}
|
||||
|
||||
void bdAnticheat::report_console_details(i_server* server, [[maybe_unused]] byte_buffer* buffer) const
|
||||
{
|
||||
// TODO: Read data as soon as needed
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->send();
|
||||
}
|
||||
}
|
14
src/client/game/demonware/services/bdAnticheat.hpp
Normal file
14
src/client/game/demonware/services/bdAnticheat.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdAnticheat final : public i_generic_service<38>
|
||||
{
|
||||
public:
|
||||
bdAnticheat();
|
||||
|
||||
private:
|
||||
void report_console_details(i_server* server, byte_buffer* buffer) const;
|
||||
};
|
||||
}
|
24
src/client/game/demonware/services/bdBandwidthTest.cpp
Normal file
24
src/client/game/demonware/services/bdBandwidthTest.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdBandwidthTest.hpp"
|
||||
#include "../data_types.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
static uint8_t bandwidth_iw6[51] =
|
||||
{
|
||||
0x0F, 0xC1, 0x1C, 0x37, 0xB8, 0xEF, 0x7C, 0xD6, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xD0, 0x07,
|
||||
0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0xF4, 0x01,
|
||||
0x00, 0x00, 0x02, 0x0C, 0x88, 0xB3, 0x04, 0x65, 0x89, 0xBF, 0xC3, 0x6A,
|
||||
0x27, 0x94, 0xD4, 0x8F
|
||||
};
|
||||
|
||||
void bdBandwidthTest::call_service(i_server* server, const std::string& /*data*/)
|
||||
{
|
||||
byte_buffer buffer;
|
||||
buffer.write(sizeof bandwidth_iw6, bandwidth_iw6);
|
||||
|
||||
auto reply = server->create_message(5);
|
||||
reply->send(&buffer, true);
|
||||
}
|
||||
}
|
11
src/client/game/demonware/services/bdBandwidthTest.hpp
Normal file
11
src/client/game/demonware/services/bdBandwidthTest.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdBandwidthTest final : public i_generic_service<18>
|
||||
{
|
||||
public:
|
||||
void call_service(i_server* server, const std::string& data) override;
|
||||
};
|
||||
}
|
29
src/client/game/demonware/services/bdDML.cpp
Normal file
29
src/client/game/demonware/services/bdDML.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdDML.hpp"
|
||||
#include "../data_types.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bdDML::bdDML()
|
||||
{
|
||||
this->register_service(2, &bdDML::get_user_raw_data);
|
||||
}
|
||||
|
||||
void bdDML::get_user_raw_data(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
auto result = new bdDMLRawData;
|
||||
result->country_code = "US";
|
||||
result->country_code = "'Murica";
|
||||
result->region = "New York";
|
||||
result->city = "New York";
|
||||
result->latitude = 0;
|
||||
result->longitude = 0;
|
||||
|
||||
result->asn = 0x2119;
|
||||
result->timezone = "+01:00";
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(result);
|
||||
reply->send();
|
||||
}
|
||||
}
|
14
src/client/game/demonware/services/bdDML.hpp
Normal file
14
src/client/game/demonware/services/bdDML.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdDML final : public i_generic_service<27>
|
||||
{
|
||||
public:
|
||||
bdDML();
|
||||
|
||||
private:
|
||||
void get_user_raw_data(i_server* server, byte_buffer* buffer) const;
|
||||
};
|
||||
}
|
63
src/client/game/demonware/services/bdDediAuth.cpp
Normal file
63
src/client/game/demonware/services/bdDediAuth.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdDediAuth.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
void bdDediAuth::call_service(i_server* server, const std::string& data)
|
||||
{
|
||||
bit_buffer buffer(data);
|
||||
|
||||
bool more_data;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.read_bool(&more_data);
|
||||
buffer.set_use_data_types(true);
|
||||
|
||||
uint32_t seed, title_id, ticket_size;
|
||||
buffer.read_uint32(&seed);
|
||||
buffer.read_uint32(&title_id);
|
||||
|
||||
uint8_t ticket[1024];
|
||||
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
|
||||
|
||||
game::bdAuthTicket auth_ticket{};
|
||||
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
|
||||
|
||||
auth_ticket.m_magicNumber = 0x0EFBDADDE;
|
||||
auth_ticket.m_type = 0;
|
||||
auth_ticket.m_titleID = title_id;
|
||||
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
|
||||
auth_ticket.m_licenseID = 4;
|
||||
|
||||
auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY);
|
||||
|
||||
strcpy_s(auth_ticket.m_username, "iw6-mod server");
|
||||
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
|
||||
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
|
||||
|
||||
uint8_t lsg_ticket[128];
|
||||
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
|
||||
std::memcpy(lsg_ticket, key.data(), 24);
|
||||
|
||||
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
|
||||
|
||||
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
|
||||
auto enc_ticket = utils::cryptography::des3::encrypt(
|
||||
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
|
||||
|
||||
bit_buffer response;
|
||||
response.set_use_data_types(false);
|
||||
response.write_bool(false);
|
||||
response.write_uint32(700);
|
||||
response.write_uint32(seed);
|
||||
response.write_bytes(enc_ticket.size(), enc_ticket.data());
|
||||
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
|
||||
|
||||
auto reply = server->create_message(29);
|
||||
reply->send(&response, false);
|
||||
}
|
||||
}
|
11
src/client/game/demonware/services/bdDediAuth.hpp
Normal file
11
src/client/game/demonware/services/bdDediAuth.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdDediAuth final : public i_generic_service<12>
|
||||
{
|
||||
public:
|
||||
void call_service(i_server* server, const std::string& data) override;
|
||||
};
|
||||
}
|
73
src/client/game/demonware/services/bdDediRSAAuth.cpp
Normal file
73
src/client/game/demonware/services/bdDediRSAAuth.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdDediRSAAuth.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
void bdDediRSAAuth::call_service(i_server* server, const std::string& data)
|
||||
{
|
||||
bit_buffer buffer(data);
|
||||
|
||||
bool more_data;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.read_bool(&more_data);
|
||||
buffer.set_use_data_types(true);
|
||||
|
||||
uint32_t seed, title_id, ticket_size;
|
||||
buffer.read_uint32(&seed);
|
||||
buffer.read_uint32(&title_id);
|
||||
|
||||
unsigned char rsa_key[140];
|
||||
buffer.read_bytes(sizeof(rsa_key), rsa_key);
|
||||
|
||||
uint8_t ticket[1024];
|
||||
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
|
||||
|
||||
game::bdAuthTicket auth_ticket{};
|
||||
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
|
||||
|
||||
auth_ticket.m_magicNumber = 0x0EFBDADDE;
|
||||
auth_ticket.m_type = 0;
|
||||
auth_ticket.m_titleID = title_id;
|
||||
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
|
||||
|
||||
auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY);
|
||||
|
||||
strcpy_s(auth_ticket.m_username, "iw6-mod server");
|
||||
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
|
||||
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
|
||||
|
||||
uint8_t lsg_ticket[128];
|
||||
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
|
||||
std::memcpy(lsg_ticket, key.data(), 24);
|
||||
|
||||
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
|
||||
|
||||
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
|
||||
auto enc_ticket = utils::cryptography::des3::encrypt(
|
||||
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
|
||||
|
||||
register_hash(&sha1_desc);
|
||||
register_prng(&yarrow_desc);
|
||||
|
||||
auto encrypted_key = utils::cryptography::rsa::encrypt(std::string(SERVER_CD_KEY, 24),
|
||||
std::string("DW-RSAENC", 10),
|
||||
std::string(PCHAR(rsa_key), sizeof(rsa_key)));
|
||||
|
||||
bit_buffer response;
|
||||
response.set_use_data_types(false);
|
||||
response.write_bool(false);
|
||||
response.write_uint32(700);
|
||||
response.write_uint32(seed);
|
||||
response.write_bytes(enc_ticket.size(), enc_ticket.data());
|
||||
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
|
||||
response.write_bytes(encrypted_key.size(), encrypted_key.data());
|
||||
|
||||
auto reply = server->create_message(29);
|
||||
reply->send(&response, false);
|
||||
}
|
||||
}
|
11
src/client/game/demonware/services/bdDediRSAAuth.hpp
Normal file
11
src/client/game/demonware/services/bdDediRSAAuth.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdDediRSAAuth final : public i_generic_service<26>
|
||||
{
|
||||
public:
|
||||
void call_service(i_server* server, const std::string& data) override;
|
||||
};
|
||||
}
|
49
src/client/game/demonware/services/bdGroup.cpp
Normal file
49
src/client/game/demonware/services/bdGroup.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdGroup.hpp"
|
||||
#include "../data_types.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bdGroup::bdGroup()
|
||||
{
|
||||
this->register_service(1, &bdGroup::set_groups);
|
||||
this->register_service(4, &bdGroup::get_groups);
|
||||
}
|
||||
|
||||
void bdGroup::set_groups(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
//uint32_t groupCount;
|
||||
// TODO: Implement array reading
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdGroup::get_groups(i_server* server, byte_buffer* buffer)
|
||||
{
|
||||
uint32_t group_count;
|
||||
buffer->read_array_header(8, &group_count);
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
|
||||
buffer->set_use_data_types(false);
|
||||
|
||||
for (uint32_t i = 0; i < group_count; ++i)
|
||||
{
|
||||
auto* count = new bdGroupCount;
|
||||
buffer->read_uint32(&count->group_id);
|
||||
|
||||
if (count->group_id < ARRAYSIZE(this->groups))
|
||||
{
|
||||
this->groups[count->group_id] = 999;
|
||||
count->group_count = this->groups[count->group_id];
|
||||
}
|
||||
|
||||
reply->add(count);
|
||||
}
|
||||
|
||||
buffer->set_use_data_types(true);
|
||||
|
||||
reply->send();
|
||||
}
|
||||
}
|
17
src/client/game/demonware/services/bdGroup.hpp
Normal file
17
src/client/game/demonware/services/bdGroup.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdGroup final : public i_generic_service<28>
|
||||
{
|
||||
public:
|
||||
bdGroup();
|
||||
|
||||
private:
|
||||
void set_groups(i_server* server, byte_buffer* buffer) const;
|
||||
void get_groups(i_server* server, byte_buffer* buffer);
|
||||
|
||||
uint32_t groups[512]{};
|
||||
};
|
||||
}
|
26
src/client/game/demonware/services/bdLSGHello.cpp
Normal file
26
src/client/game/demonware/services/bdLSGHello.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdLSGHello.hpp"
|
||||
#include "component/demonware.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
void bdLSGHello::call_service(i_server* server, const std::string& data)
|
||||
{
|
||||
bit_buffer buffer(data);
|
||||
|
||||
bool more_data;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.read_bool(&more_data);
|
||||
buffer.set_use_data_types(true);
|
||||
|
||||
uint32_t seed, title_id;
|
||||
buffer.read_uint32(&title_id);
|
||||
buffer.read_uint32(&seed);
|
||||
|
||||
uint8_t ticket[128];
|
||||
buffer.read_bytes(sizeof(ticket), ticket);
|
||||
|
||||
set_key(true, ticket);
|
||||
set_key(false, ticket);
|
||||
}
|
||||
}
|
11
src/client/game/demonware/services/bdLSGHello.hpp
Normal file
11
src/client/game/demonware/services/bdLSGHello.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdLSGHello final : public i_generic_service<7>
|
||||
{
|
||||
public:
|
||||
void call_service(i_server* server, const std::string& data) override;
|
||||
};
|
||||
}
|
107
src/client/game/demonware/services/bdMatchMaking.cpp
Normal file
107
src/client/game/demonware/services/bdMatchMaking.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdMatchMaking.hpp"
|
||||
#include "../data_types.hpp"
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
std::map<uint64_t, std::shared_ptr<MatchMakingInfo>> sessions;
|
||||
|
||||
void UpdateSession(const std::string& data)
|
||||
{
|
||||
byte_buffer buffer(data);
|
||||
|
||||
auto mmInfo = std::make_shared<MatchMakingInfo>();
|
||||
mmInfo->symmetric = true;
|
||||
mmInfo->deserialize(&buffer);
|
||||
mmInfo->symmetric = false;
|
||||
|
||||
sessions[mmInfo->session_id.session_id] = mmInfo;
|
||||
}
|
||||
|
||||
void DeleteSession(const std::string& data)
|
||||
{
|
||||
byte_buffer buffer(data);
|
||||
|
||||
bdSessionID id;
|
||||
id.deserialize(&buffer);
|
||||
|
||||
const auto session = sessions.find(id.session_id);
|
||||
if (session != sessions.end())
|
||||
{
|
||||
sessions.erase(session);
|
||||
}
|
||||
}
|
||||
|
||||
bdMatchMaking::bdMatchMaking()
|
||||
{
|
||||
this->register_service(1, &bdMatchMaking::create_session);
|
||||
this->register_service(2, &bdMatchMaking::update_session);
|
||||
this->register_service(3, &bdMatchMaking::delete_session);
|
||||
this->register_service(10, &bdMatchMaking::get_performance);
|
||||
this->register_service(16, &bdMatchMaking::find_sessions_two_pass);
|
||||
}
|
||||
|
||||
void bdMatchMaking::create_session(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
auto* id = new bdSessionID;
|
||||
id->session_id = steam::SteamUser()->GetSteamID().bits;
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(id);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdMatchMaking::update_session(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
MatchMakingInfo mmInfo;
|
||||
mmInfo.session_id.deserialize(buffer);
|
||||
mmInfo.deserialize(buffer);
|
||||
|
||||
byte_buffer out_data;
|
||||
mmInfo.symmetric = true;
|
||||
mmInfo.serialize(&out_data);
|
||||
|
||||
byte_buffer addr_buf(mmInfo.host_addr);
|
||||
bdCommonAddr addr;
|
||||
addr.deserialize(&addr_buf);
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdMatchMaking::delete_session(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
bdSessionID id;
|
||||
id.deserialize(buffer);
|
||||
|
||||
byte_buffer out_data;
|
||||
id.serialize(&out_data);
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdMatchMaking::get_performance(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
auto* result = new bdPerformanceValue;
|
||||
result->user_id = steam::SteamUser()->GetSteamID().bits;
|
||||
result->performance = 10;
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(result);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdMatchMaking::find_sessions_two_pass(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
|
||||
for (auto& session : sessions)
|
||||
{
|
||||
reply->add(session.second);
|
||||
}
|
||||
|
||||
reply->send();
|
||||
}
|
||||
}
|
18
src/client/game/demonware/services/bdMatchMaking.hpp
Normal file
18
src/client/game/demonware/services/bdMatchMaking.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdMatchMaking final : public i_generic_service<21>
|
||||
{
|
||||
public:
|
||||
bdMatchMaking();
|
||||
|
||||
private:
|
||||
void create_session(i_server* server, byte_buffer* buffer) const;
|
||||
void update_session(i_server* server, byte_buffer* buffer) const;
|
||||
void delete_session(i_server* server, byte_buffer* buffer) const;
|
||||
void get_performance(i_server* server, byte_buffer* buffer) const;
|
||||
void find_sessions_two_pass(i_server* server, byte_buffer* buffer) const;
|
||||
};
|
||||
}
|
136
src/client/game/demonware/services/bdRelayService.cpp
Normal file
136
src/client/game/demonware/services/bdRelayService.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdRelayService.hpp"
|
||||
#include "../data_types.hpp"
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class DebugObjectUCD : public i_serializable
|
||||
{
|
||||
public:
|
||||
uint64_t user_id;
|
||||
std::string platform;
|
||||
std::vector<uint64_t> user_ids;
|
||||
std::vector<std::string> user_files;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint64(this->user_id);
|
||||
buffer->write_string(this->platform);
|
||||
|
||||
buffer->write_array_header(10, INT(this->user_ids.size()), sizeof(uint64_t));
|
||||
buffer->set_use_data_types(false);
|
||||
for (size_t i = 0; i < this->user_ids.size(); i++)
|
||||
{
|
||||
buffer->write_uint64(this->user_ids[i]);
|
||||
}
|
||||
buffer->set_use_data_types(true);
|
||||
|
||||
auto stringSize = this->user_files.size();
|
||||
for (size_t i = 0; i < this->user_files.size(); i++)
|
||||
{
|
||||
stringSize += this->user_files[i].length();
|
||||
}
|
||||
|
||||
buffer->write_array_header(16, INT(this->user_files.size()), INT(stringSize / this->user_files.size()));
|
||||
|
||||
buffer->set_use_data_types(false);
|
||||
for (size_t i = 0; i < this->user_files.size(); i++)
|
||||
{
|
||||
buffer->write_string(this->user_files[i]);
|
||||
}
|
||||
buffer->set_use_data_types(true);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* /*buffer*/) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class DebugObjectUNO : public i_serializable
|
||||
{
|
||||
public:
|
||||
std::vector<uint64_t> user_ids;
|
||||
std::vector<std::string> user_files;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_array_header(10, INT(this->user_ids.size()), sizeof(uint64_t));
|
||||
|
||||
buffer->set_use_data_types(false);
|
||||
for (size_t i = 0; i < this->user_ids.size(); i++)
|
||||
{
|
||||
buffer->write_uint64(this->user_ids[i]);
|
||||
}
|
||||
buffer->set_use_data_types(true);
|
||||
|
||||
auto stringSize = this->user_files.size();
|
||||
for (size_t i = 0; i < this->user_files.size(); i++)
|
||||
{
|
||||
stringSize += this->user_files[i].length();
|
||||
}
|
||||
|
||||
buffer->write_array_header(16, INT(this->user_files.size()), INT(stringSize / this->user_files.size()));
|
||||
|
||||
buffer->set_use_data_types(false);
|
||||
for (size_t i = 0; i < this->user_files.size(); i++)
|
||||
{
|
||||
buffer->write_string(this->user_files[i]);
|
||||
}
|
||||
buffer->set_use_data_types(true);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* /*buffer*/) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bdRelayService::bdRelayService()
|
||||
{
|
||||
this->register_service(3, &bdRelayService::get_credentials);
|
||||
this->register_service(4, &bdRelayService::get_credentials_from_ticket);
|
||||
}
|
||||
|
||||
void bdRelayService::get_credentials(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint32_t unk1;
|
||||
uint64_t user_id;
|
||||
std::string platform;
|
||||
|
||||
// User info.
|
||||
buffer->read_uint32(&unk1);
|
||||
buffer->read_uint64(&user_id);
|
||||
buffer->read_string(&platform);
|
||||
|
||||
auto* result = new DebugObjectUCD;
|
||||
result->user_id = steam::SteamUser()->GetSteamID().bits;
|
||||
result->user_ids.push_back(steam::SteamUser()->GetSteamID().bits);
|
||||
result->user_ids.push_back(0x00659CD6);
|
||||
result->user_files.push_back("pc");
|
||||
result->user_files.push_back("ucd");
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(result);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdRelayService::get_credentials_from_ticket(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
std::string ticket;
|
||||
buffer->read_string(&ticket);
|
||||
|
||||
auto* result = new DebugObjectUNO;
|
||||
|
||||
// Create fake info.
|
||||
result->user_ids.push_back(0x1A586A45744DA396);
|
||||
result->user_ids.push_back(steam::SteamUser()->GetSteamID().bits);
|
||||
result->user_ids.push_back(16326195462233142067);
|
||||
result->user_files.push_back("youtube");
|
||||
result->user_files.push_back("steam");
|
||||
result->user_files.push_back("uno");
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(result);
|
||||
reply->send();
|
||||
}
|
||||
}
|
15
src/client/game/demonware/services/bdRelayService.hpp
Normal file
15
src/client/game/demonware/services/bdRelayService.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdRelayService final : public i_generic_service<86>
|
||||
{
|
||||
public:
|
||||
bdRelayService();
|
||||
|
||||
private:
|
||||
void get_credentials(i_server* server, byte_buffer* buffer) const;
|
||||
void get_credentials_from_ticket(i_server* server, byte_buffer* buffer) const;
|
||||
};
|
||||
}
|
63
src/client/game/demonware/services/bdSteamAuth.cpp
Normal file
63
src/client/game/demonware/services/bdSteamAuth.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdSteamAuth.hpp"
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
void bdSteamAuth::call_service(i_server* server, const std::string& data)
|
||||
{
|
||||
bit_buffer buffer(data);
|
||||
|
||||
bool more_data;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.read_bool(&more_data);
|
||||
buffer.set_use_data_types(true);
|
||||
|
||||
uint32_t seed, title_id, ticket_size;
|
||||
buffer.read_uint32(&seed);
|
||||
buffer.read_uint32(&title_id);
|
||||
buffer.read_uint32(&ticket_size);
|
||||
|
||||
uint8_t ticket[1024];
|
||||
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
|
||||
|
||||
game::bdAuthTicket auth_ticket{};
|
||||
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
|
||||
|
||||
auth_ticket.m_magicNumber = 0x0EFBDADDE;
|
||||
auth_ticket.m_type = 0;
|
||||
auth_ticket.m_titleID = title_id;
|
||||
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
|
||||
|
||||
auto key = utils::cryptography::tiger::compute("iw6-mod");
|
||||
|
||||
strcpy_s(auth_ticket.m_username, "iw6-mod user");
|
||||
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
|
||||
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
|
||||
|
||||
uint8_t lsg_ticket[128];
|
||||
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
|
||||
std::memcpy(lsg_ticket, key.data(), 24);
|
||||
|
||||
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
|
||||
|
||||
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
|
||||
auto enc_ticket = utils::cryptography::des3::encrypt(
|
||||
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
|
||||
|
||||
bit_buffer response;
|
||||
response.set_use_data_types(false);
|
||||
response.write_bool(false);
|
||||
response.write_uint32(700);
|
||||
response.write_uint32(seed);
|
||||
response.write_bytes(enc_ticket.size(), enc_ticket.data());
|
||||
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
|
||||
|
||||
auto reply = server->create_message(29);
|
||||
reply->send(&response, false);
|
||||
}
|
||||
}
|
11
src/client/game/demonware/services/bdSteamAuth.hpp
Normal file
11
src/client/game/demonware/services/bdSteamAuth.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdSteamAuth final : public i_generic_service<28>
|
||||
{
|
||||
public:
|
||||
void call_service(i_server* server, const std::string& data) override;
|
||||
};
|
||||
}
|
365
src/client/game/demonware/services/bdStorage.cpp
Normal file
365
src/client/game/demonware/services/bdStorage.cpp
Normal file
@ -0,0 +1,365 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdStorage.hpp"
|
||||
|
||||
#include "../data_types.hpp"
|
||||
|
||||
#include <utils/compression.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "component/filesystem.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string get_motd_text()
|
||||
{
|
||||
return "This is not a copy & pasted client";
|
||||
}
|
||||
}
|
||||
|
||||
bdStorage::bdStorage()
|
||||
{
|
||||
this->register_service(1, &bdStorage::set_legacy_user_file);
|
||||
this->register_service(3, &bdStorage::get_legacy_user_file);
|
||||
this->register_service(5, &bdStorage::list_legacy_user_files);
|
||||
this->register_service(6, &bdStorage::list_publisher_files);
|
||||
this->register_service(7, &bdStorage::get_publisher_file);
|
||||
this->register_service(8, &bdStorage::update_legacy_user_file);
|
||||
this->register_service(10, &bdStorage::set_user_file);
|
||||
this->register_service(11, &bdStorage::delete_user_file);
|
||||
this->register_service(12, &bdStorage::get_user_file);
|
||||
|
||||
this->map_publisher_resource_variant("motd-.*\\.txt", get_motd_text);
|
||||
this->map_publisher_resource("newsfeed-.*\\.txt", "dw/newsfeed.txt", DW_NEWSFEED);
|
||||
this->map_publisher_resource("mm\\.cfg", "dw/mm.cfg", DW_MM_CONFIG);
|
||||
this->map_publisher_resource("playlists(_.+)?\\.aggr", "dw/playlists_tu14.aggr", DW_PLAYLISTS);
|
||||
this->map_publisher_resource("social_[Tt][Uu][0-9]+\\.cfg", "dw/social_tu14.cfg", DW_SOCIAL_CONFIG);
|
||||
this->map_publisher_resource("entitlement_config_[Tt][Uu][0-9]+\\.info", "dw/entitlement_config_tu14.info", DW_ENTITLEMENT_CONFIG);
|
||||
|
||||
publisher_resources_.emplace_back(std::regex{"heatmap\\.raw"}, generate_heat_map());
|
||||
}
|
||||
|
||||
void bdStorage::map_publisher_resource(const std::string& expression, const std::string& path, const int id)
|
||||
{
|
||||
auto data = filesystem::exists(path)
|
||||
? filesystem::read_file(path)
|
||||
: utils::nt::load_resource(id);
|
||||
|
||||
this->map_publisher_resource_variant(expression, std::move(data));
|
||||
}
|
||||
|
||||
void bdStorage::map_publisher_resource_variant(const std::string& expression, resource_variant resource)
|
||||
{
|
||||
if (resource.valueless_by_exception())
|
||||
{
|
||||
throw std::runtime_error("Publisher resource variant is empty!");
|
||||
}
|
||||
|
||||
this->publisher_resources_.emplace_back(std::regex{expression}, std::move(resource));
|
||||
}
|
||||
|
||||
bool bdStorage::load_publisher_resource(const std::string& name, std::string& buffer) const
|
||||
{
|
||||
for (const auto& resource : this->publisher_resources_)
|
||||
{
|
||||
if (std::regex_match(name, resource.first))
|
||||
{
|
||||
if (std::holds_alternative<std::string>(resource.second))
|
||||
{
|
||||
buffer = std::get<std::string>(resource.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = std::get<callback>(resource.second)();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: missing publisher file: %s\n", name.data());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string bdStorage::get_user_file_path(const std::string& name)
|
||||
{
|
||||
return "players2/user/" + name;
|
||||
}
|
||||
|
||||
std::string bdStorage::generate_heat_map()
|
||||
{
|
||||
uint8_t map[256][256];
|
||||
|
||||
for (auto y = 0; y < 256; ++y)
|
||||
{
|
||||
for (auto x = 0; x < 256; ++x)
|
||||
{
|
||||
auto data = std::rand() % std::numeric_limits<std::uint8_t>::max();
|
||||
if (data % 15 == 0)
|
||||
{
|
||||
data = 0xF | ((data & 0x7) << 4) | 0x10;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0;
|
||||
}
|
||||
|
||||
map[y][x] = static_cast<std::uint8_t>(data);
|
||||
}
|
||||
}
|
||||
|
||||
return utils::compression::zlib::compress(std::string(LPSTR(map), sizeof(map)));
|
||||
}
|
||||
|
||||
void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
bool priv;
|
||||
std::string filename, data;
|
||||
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_bool(&priv);
|
||||
buffer->read_blob(&data);
|
||||
|
||||
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
std::string id_string = utils::string::va("%llX", id);
|
||||
#ifdef DEBUG
|
||||
printf("DW: Storing user file '%s' as %s\n", filename.data(), id_string.data());
|
||||
#endif
|
||||
|
||||
const auto path = get_user_file_path(id_string);
|
||||
utils::io::write_file(path, data);
|
||||
|
||||
auto* info = new bdFileInfo;
|
||||
|
||||
info->file_id = id;
|
||||
info->filename = filename;
|
||||
info->create_time = uint32_t(time(nullptr));
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = 0;
|
||||
info->priv = priv;
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(info);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::update_legacy_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint64_t id;
|
||||
std::string data;
|
||||
|
||||
buffer->read_uint64(&id);
|
||||
buffer->read_blob(&data);
|
||||
|
||||
std::string id_string = utils::string::va("%llX", id);
|
||||
#ifdef DEBUG
|
||||
printf("DW: Updating user file %s\n", id_string.data());
|
||||
#endif
|
||||
|
||||
const auto path = get_user_file_path(id_string);
|
||||
utils::io::write_file(path, data);
|
||||
|
||||
auto* info = new bdFileInfo;
|
||||
|
||||
info->file_id = id;
|
||||
info->filename = "<>";
|
||||
info->create_time = uint32_t(time(nullptr));
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = 0;
|
||||
info->priv = false;
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(info);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::get_legacy_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
std::string filename, data;
|
||||
buffer->read_string(&filename);
|
||||
|
||||
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
std::string id_string = utils::string::va("%llX", id);
|
||||
#ifdef DEBUG
|
||||
printf("DW: Loading user file: %s (%s)\n", filename.data(), id_string.data());
|
||||
#endif
|
||||
|
||||
const auto path = get_user_file_path(id_string);
|
||||
if (utils::io::read_file(path, &data))
|
||||
{
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(new bdFileData(data));
|
||||
reply->send();
|
||||
}
|
||||
else
|
||||
{
|
||||
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
|
||||
}
|
||||
}
|
||||
|
||||
void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint64_t unk;
|
||||
uint32_t date;
|
||||
uint16_t num_results, offset;
|
||||
std::string filename, data;
|
||||
|
||||
buffer->read_uint64(&unk);
|
||||
buffer->read_uint32(&date);
|
||||
buffer->read_uint16(&num_results);
|
||||
buffer->read_uint16(&offset);
|
||||
buffer->read_string(&filename);
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
if (utils::io::read_file(path, &data))
|
||||
{
|
||||
auto* info = new bdFileInfo;
|
||||
|
||||
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
info->filename = filename;
|
||||
info->create_time = 0;
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = 0;
|
||||
info->priv = false;
|
||||
|
||||
reply->add(info);
|
||||
}
|
||||
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::list_publisher_files(i_server* server, byte_buffer* buffer)
|
||||
{
|
||||
uint32_t date;
|
||||
uint16_t num_results, offset;
|
||||
std::string filename, data;
|
||||
|
||||
buffer->read_uint32(&date);
|
||||
buffer->read_uint16(&num_results);
|
||||
buffer->read_uint16(&offset);
|
||||
buffer->read_string(&filename);
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
|
||||
if (this->load_publisher_resource(filename, data))
|
||||
{
|
||||
auto* info = new bdFileInfo;
|
||||
|
||||
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
info->filename = filename;
|
||||
info->create_time = 0;
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = 0;
|
||||
info->priv = false;
|
||||
|
||||
reply->add(info);
|
||||
}
|
||||
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::get_publisher_file(i_server* server, byte_buffer* buffer)
|
||||
{
|
||||
std::string filename;
|
||||
buffer->read_string(&filename);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: loading publisher file: %s\n", filename.data());
|
||||
#endif
|
||||
|
||||
std::string data;
|
||||
if (this->load_publisher_resource(filename, data))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: sending publisher file: %s, size: %lld\n", filename.data(), data.size());
|
||||
#endif
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(new bdFileData(data));
|
||||
reply->send();
|
||||
}
|
||||
else
|
||||
{
|
||||
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
|
||||
}
|
||||
}
|
||||
|
||||
void bdStorage::delete_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint64_t owner;
|
||||
std::string game, filename;
|
||||
|
||||
buffer->read_string(&game);
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_uint64(&owner);
|
||||
|
||||
// Really remove the file?
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
bool priv;
|
||||
uint64_t owner;
|
||||
std::string game, filename, data;
|
||||
|
||||
buffer->read_string(&game);
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_bool(&priv);
|
||||
buffer->read_blob(&data);
|
||||
buffer->read_uint64(&owner);
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
utils::io::write_file(path, data);
|
||||
|
||||
auto* info = new bdFileInfo;
|
||||
|
||||
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
info->filename = filename;
|
||||
info->create_time = uint32_t(time(nullptr));
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = owner;
|
||||
info->priv = priv;
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(info);
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::get_user_file(i_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint64_t owner{};
|
||||
std::string game, filename, platform, data;
|
||||
|
||||
buffer->read_string(&game);
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_uint64(&owner);
|
||||
buffer->read_string(&platform);
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
if (utils::io::read_file(path, &data))
|
||||
{
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(new bdFileData(data));
|
||||
reply->send();
|
||||
}
|
||||
else
|
||||
{
|
||||
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
|
||||
}
|
||||
}
|
||||
}
|
33
src/client/game/demonware/services/bdStorage.hpp
Normal file
33
src/client/game/demonware/services/bdStorage.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdStorage final : public i_generic_service<10>
|
||||
{
|
||||
public:
|
||||
bdStorage();
|
||||
|
||||
private:
|
||||
using callback = std::function<std::string()>;
|
||||
using resource_variant = std::variant<std::string, callback>;
|
||||
std::vector<std::pair<std::regex, resource_variant>> publisher_resources_;
|
||||
|
||||
void set_legacy_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
void update_legacy_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
void get_legacy_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
void list_legacy_user_files(i_server* server, byte_buffer* buffer) const;
|
||||
void list_publisher_files(i_server* server, byte_buffer* buffer);
|
||||
void get_publisher_file(i_server* server, byte_buffer* buffer);
|
||||
void delete_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
void set_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
void get_user_file(i_server* server, byte_buffer* buffer) const;
|
||||
|
||||
void map_publisher_resource(const std::string& expression, const std::string& path, int id);
|
||||
void map_publisher_resource_variant(const std::string& expression, resource_variant resource);
|
||||
bool load_publisher_resource(const std::string& name, std::string& buffer) const;
|
||||
|
||||
static std::string get_user_file_path(const std::string& name);
|
||||
static std::string generate_heat_map();
|
||||
};
|
||||
}
|
21
src/client/game/demonware/services/bdTitleUtilities.cpp
Normal file
21
src/client/game/demonware/services/bdTitleUtilities.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <std_include.hpp>
|
||||
#include "bdTitleUtilities.hpp"
|
||||
#include "../data_types.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
bdTitleUtilities::bdTitleUtilities()
|
||||
{
|
||||
this->register_service(6, &bdTitleUtilities::get_server_time);
|
||||
}
|
||||
|
||||
void bdTitleUtilities::get_server_time(i_server* server, byte_buffer* /*buffer*/) const
|
||||
{
|
||||
const auto time_result = new bdTimeStamp;
|
||||
time_result->unix_time = uint32_t(time(nullptr));
|
||||
|
||||
auto reply = server->create_reply(this->get_sub_type());
|
||||
reply->add(time_result);
|
||||
reply->send();
|
||||
}
|
||||
}
|
14
src/client/game/demonware/services/bdTitleUtilities.hpp
Normal file
14
src/client/game/demonware/services/bdTitleUtilities.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../i_service.hpp"
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class bdTitleUtilities final : public i_generic_service<12>
|
||||
{
|
||||
public:
|
||||
bdTitleUtilities();
|
||||
|
||||
private:
|
||||
void get_server_time(i_server* server, byte_buffer* buffer) const;
|
||||
};
|
||||
}
|
78
src/client/game/demonware/stun_server.cpp
Normal file
78
src/client/game/demonware/stun_server.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include <std_include.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include "component/demonware.hpp"
|
||||
#include "game/demonware/stun_server.hpp"
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
stun_server::stun_server(std::string _name) : name_(std::move(_name))
|
||||
{
|
||||
this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_);
|
||||
}
|
||||
|
||||
unsigned long stun_server::get_address() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
void stun_server::ip_discovery(SOCKET s, const sockaddr* to, const int tolen) const
|
||||
{
|
||||
const uint32_t ip = 0x0100007f;
|
||||
|
||||
byte_buffer buffer;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.write_byte(31); // type
|
||||
buffer.write_byte(2); // version
|
||||
buffer.write_byte(0); // version
|
||||
buffer.write_uint32(ip); // external ip
|
||||
buffer.write_uint16(3074); // port
|
||||
|
||||
send_datagram_packet(s, buffer.get_buffer(), to, tolen);
|
||||
}
|
||||
|
||||
void stun_server::nat_discovery(SOCKET s, const sockaddr* to, const int tolen) const
|
||||
{
|
||||
const uint32_t ip = 0x0100007f;
|
||||
|
||||
byte_buffer buffer;
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.write_byte(21); // type
|
||||
buffer.write_byte(2); // version
|
||||
buffer.write_byte(0); // version
|
||||
buffer.write_uint32(ip); // external ip
|
||||
buffer.write_uint16(3074); // port
|
||||
buffer.write_uint32(this->get_address()); // server ip
|
||||
buffer.write_uint16(3074); // server port
|
||||
|
||||
send_datagram_packet(s, buffer.get_buffer(), to, tolen);
|
||||
}
|
||||
|
||||
int stun_server::send(const SOCKET s, const char* buf, const int len, const sockaddr* to, const int tolen) const
|
||||
{
|
||||
uint8_t type, version, padding;
|
||||
|
||||
byte_buffer buffer(std::string(buf, len));
|
||||
buffer.set_use_data_types(false);
|
||||
buffer.read_byte(&type);
|
||||
buffer.read_byte(&version);
|
||||
buffer.read_byte(&padding);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 30:
|
||||
this->ip_discovery(s, to, tolen);
|
||||
break;
|
||||
case 20:
|
||||
this->nat_discovery(s, to, tolen);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
}
|
21
src/client/game/demonware/stun_server.hpp
Normal file
21
src/client/game/demonware/stun_server.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace demonware
|
||||
{
|
||||
class stun_server final
|
||||
{
|
||||
public:
|
||||
explicit stun_server(std::string name);
|
||||
|
||||
unsigned long get_address() const;
|
||||
|
||||
int send(SOCKET s, const char* buf, int len, const sockaddr* to, int tolen) const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
unsigned long address_;
|
||||
|
||||
void ip_discovery(SOCKET s, const sockaddr* to, int tolen) const;
|
||||
void nat_discovery(SOCKET s, const sockaddr* to, int tolen) const;
|
||||
};
|
||||
}
|
149
src/client/game/dvars.cpp
Normal file
149
src/client/game/dvars.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include <std_include.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "game.hpp"
|
||||
|
||||
namespace dvars
|
||||
{
|
||||
game::dvar_t* con_inputBoxColor = nullptr;
|
||||
game::dvar_t* con_inputHintBoxColor = nullptr;
|
||||
game::dvar_t* con_outputBarColor = nullptr;
|
||||
game::dvar_t* con_outputSliderColor = nullptr;
|
||||
game::dvar_t* con_outputWindowColor = nullptr;
|
||||
game::dvar_t* con_inputDvarMatchColor = nullptr;
|
||||
game::dvar_t* con_inputDvarValueColor = nullptr;
|
||||
game::dvar_t* con_inputDvarInactiveValueColor = nullptr;
|
||||
game::dvar_t* con_inputCmdMatchColor = nullptr;
|
||||
|
||||
game::dvar_t* sv_cheats = nullptr;
|
||||
|
||||
game::dvar_t* g_playerEjection = nullptr;
|
||||
game::dvar_t* g_playerCollision = nullptr;
|
||||
game::dvar_t* g_gravity = nullptr;
|
||||
game::dvar_t* g_speed = nullptr;
|
||||
game::dvar_t* g_rocketPushbackScale = nullptr;
|
||||
game::dvar_t* g_enableElevators = nullptr;
|
||||
game::dvar_t* g_dump_scripts = nullptr;
|
||||
game::dvar_t* g_log = nullptr;
|
||||
|
||||
game::dvar_t* bg_surfacePenetration = nullptr;
|
||||
|
||||
game::dvar_t* pm_bouncing = nullptr;
|
||||
|
||||
game::dvar_t* player_sustainAmmo = nullptr;
|
||||
|
||||
game::dvar_t* jump_slowdownEnable = nullptr;
|
||||
game::dvar_t* jump_enableFallDamage = nullptr;
|
||||
game::dvar_t* jump_height = nullptr;
|
||||
game::dvar_t* jump_ladderPushVel = nullptr;
|
||||
game::dvar_t* jump_spreadAdd = nullptr;
|
||||
|
||||
game::dvar_t* r_aspectRatioCustom = nullptr;
|
||||
game::dvar_t* r_fullbright = nullptr;
|
||||
|
||||
game::dvar_t* aimassist_enabled = nullptr;
|
||||
|
||||
game::dvar_t* cg_legacyCrashHandling = nullptr;
|
||||
|
||||
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
|
||||
{
|
||||
if (domain.vector.min == -FLT_MAX)
|
||||
{
|
||||
if (domain.vector.max == FLT_MAX)
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector", components);
|
||||
}
|
||||
else
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components %g or smaller", components,
|
||||
domain.vector.max);
|
||||
}
|
||||
}
|
||||
else if (domain.vector.max == FLT_MAX)
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components %g or bigger", components,
|
||||
domain.vector.min);
|
||||
}
|
||||
else
|
||||
{
|
||||
return utils::string::va("Domain is any %iD vector with components from %g to %g", components,
|
||||
domain.vector.min, domain.vector.max);
|
||||
}
|
||||
}
|
||||
|
||||
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case game::dvar_type::boolean:
|
||||
return "Domain is 0 or 1"s;
|
||||
|
||||
case game::dvar_type::value:
|
||||
if (domain.value.min == -FLT_MAX)
|
||||
{
|
||||
if (domain.value.max == FLT_MAX)
|
||||
{
|
||||
return "Domain is any number"s;
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any number %g or smaller", domain.value.max);
|
||||
}
|
||||
|
||||
if (domain.value.max == FLT_MAX)
|
||||
{
|
||||
return utils::string::va("Domain is any number %g or bigger", domain.value.min);
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any number from %g to %g", domain.value.min, domain.value.max);
|
||||
|
||||
case game::dvar_type::vec2:
|
||||
return dvar_get_vector_domain(2, domain);
|
||||
|
||||
case game::dvar_type::rgb:
|
||||
case game::dvar_type::vec3:
|
||||
return dvar_get_vector_domain(3, domain);
|
||||
|
||||
case game::dvar_type::vec4:
|
||||
return dvar_get_vector_domain(4, domain);
|
||||
|
||||
case game::dvar_type::integer:
|
||||
if (domain.enumeration.stringCount == INT_MIN)
|
||||
{
|
||||
if (domain.integer.max == INT_MAX)
|
||||
{
|
||||
return "Domain is any integer"s;
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any integer %i or smaller", domain.integer.max);
|
||||
}
|
||||
|
||||
if (domain.integer.max == INT_MAX)
|
||||
{
|
||||
return utils::string::va("Domain is any integer %i or bigger", domain.integer.min);
|
||||
}
|
||||
|
||||
return utils::string::va("Domain is any integer from %i to %i", domain.integer.min, domain.integer.max);
|
||||
|
||||
case game::dvar_type::color:
|
||||
return "Domain is any 4-component color, in RGBA format"s;
|
||||
|
||||
case game::dvar_type::enumeration:
|
||||
str = "Domain is one of the following:"s;
|
||||
|
||||
for (auto string_index = 0; string_index < domain.enumeration.stringCount; ++string_index)
|
||||
{
|
||||
str += utils::string::va("\n %2i: %s", string_index, domain.enumeration.strings[string_index]);
|
||||
}
|
||||
|
||||
return str;
|
||||
|
||||
case game::dvar_type::string:
|
||||
return "Domain is any text"s;
|
||||
|
||||
default:
|
||||
return utils::string::va("unhandled dvar type '%i'", type);
|
||||
}
|
||||
}
|
||||
}
|
49
src/client/game/dvars.hpp
Normal file
49
src/client/game/dvars.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "structs.hpp"
|
||||
|
||||
namespace dvars
|
||||
{
|
||||
extern game::dvar_t* con_inputBoxColor;
|
||||
extern game::dvar_t* con_inputHintBoxColor;
|
||||
extern game::dvar_t* con_outputBarColor;
|
||||
extern game::dvar_t* con_outputSliderColor;
|
||||
extern game::dvar_t* con_outputWindowColor;
|
||||
extern game::dvar_t* con_inputDvarMatchColor;
|
||||
extern game::dvar_t* con_inputDvarValueColor;
|
||||
extern game::dvar_t* con_inputDvarInactiveValueColor;
|
||||
extern game::dvar_t* con_inputCmdMatchColor;
|
||||
|
||||
extern game::dvar_t* sv_cheats;
|
||||
|
||||
extern game::dvar_t* g_playerCollision;
|
||||
extern game::dvar_t* g_playerEjection;
|
||||
extern game::dvar_t* g_gravity;
|
||||
extern game::dvar_t* g_speed;
|
||||
extern game::dvar_t* g_rocketPushbackScale;
|
||||
extern game::dvar_t* g_enableElevators;
|
||||
extern game::dvar_t* g_dump_scripts;
|
||||
extern game::dvar_t* g_log;
|
||||
|
||||
extern game::dvar_t* bg_surfacePenetration;
|
||||
|
||||
extern game::dvar_t* pm_bouncing;
|
||||
|
||||
extern game::dvar_t* player_sustainAmmo;
|
||||
|
||||
extern game::dvar_t* r_aspectRatioCustom;
|
||||
extern game::dvar_t* jump_slowdownEnable;
|
||||
extern game::dvar_t* jump_enableFallDamage;
|
||||
extern game::dvar_t* jump_height;
|
||||
extern game::dvar_t* jump_ladderPushVel;
|
||||
extern game::dvar_t* jump_spreadAdd;
|
||||
|
||||
extern game::dvar_t* r_fullbright;
|
||||
|
||||
extern game::dvar_t* aimassist_enabled;
|
||||
|
||||
extern game::dvar_t* cg_legacyCrashHandling;
|
||||
|
||||
std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain);
|
||||
std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain);
|
||||
}
|
102
src/client/game/game.cpp
Normal file
102
src/client/game/game.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include <std_include.hpp>
|
||||
#include "game.hpp"
|
||||
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
namespace game
|
||||
{
|
||||
int Cmd_Argc()
|
||||
{
|
||||
return cmd_args->argc[cmd_args->nesting];
|
||||
}
|
||||
|
||||
const char* Cmd_Argv(const int index)
|
||||
{
|
||||
return cmd_args->argv[cmd_args->nesting][index];
|
||||
}
|
||||
|
||||
int SV_Cmd_Argc()
|
||||
{
|
||||
return sv_cmd_args->argc[sv_cmd_args->nesting];
|
||||
}
|
||||
|
||||
const char* SV_Cmd_Argv(const int index)
|
||||
{
|
||||
return sv_cmd_args->argv[sv_cmd_args->nesting][index];
|
||||
}
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
|
||||
launcher::mode get_mode()
|
||||
{
|
||||
if (mode == launcher::mode::none)
|
||||
{
|
||||
throw std::runtime_error("Launcher mode not valid. Something must be wrong.");
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool is_dedi()
|
||||
{
|
||||
return get_mode() == launcher::mode::server;
|
||||
}
|
||||
|
||||
bool is_mp()
|
||||
{
|
||||
return get_mode() == launcher::mode::multiplayer;
|
||||
}
|
||||
|
||||
bool is_sp()
|
||||
{
|
||||
return get_mode() == launcher::mode::singleplayer;
|
||||
}
|
||||
|
||||
void set_mode(const launcher::mode _mode)
|
||||
{
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
std::string get_string()
|
||||
{
|
||||
const auto current_mode = get_mode();
|
||||
switch (current_mode)
|
||||
{
|
||||
case launcher::mode::server:
|
||||
return "Dedicated Server";
|
||||
|
||||
case launcher::mode::multiplayer:
|
||||
return "Multiplayer";
|
||||
|
||||
case launcher::mode::singleplayer:
|
||||
return "Multiplayer";
|
||||
|
||||
case launcher::mode::none:
|
||||
return "None";
|
||||
|
||||
default:
|
||||
return "Unknown (" + std::to_string(static_cast<int>(mode)) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_headless()
|
||||
{
|
||||
static const auto headless = utils::flags::has_flag("headless");
|
||||
return headless;
|
||||
}
|
||||
|
||||
void show_error(const std::string& text, const std::string& title)
|
||||
{
|
||||
if (is_headless())
|
||||
{
|
||||
puts(text.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxA(nullptr, text.data(), title.data(), MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
|
||||
}
|
||||
}
|
||||
}
|
75
src/client/game/game.hpp
Normal file
75
src/client/game/game.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "structs.hpp"
|
||||
#include "launcher/launcher.hpp"
|
||||
|
||||
#define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
|
||||
|
||||
#define SERVER_CD_KEY "iw6-mod-CD-Key"
|
||||
|
||||
namespace game
|
||||
{
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode get_mode();
|
||||
|
||||
bool is_mp();
|
||||
bool is_sp();
|
||||
bool is_dedi();
|
||||
|
||||
void set_mode(launcher::mode mode);
|
||||
|
||||
std::string get_string();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class symbol
|
||||
{
|
||||
public:
|
||||
symbol(const size_t sp_address, const size_t mp_address)
|
||||
: sp_object_(reinterpret_cast<T*>(sp_address))
|
||||
, mp_object_(reinterpret_cast<T*>(mp_address))
|
||||
{
|
||||
}
|
||||
|
||||
T* get() const
|
||||
{
|
||||
if (environment::is_sp())
|
||||
{
|
||||
return sp_object_;
|
||||
}
|
||||
|
||||
return mp_object_;
|
||||
}
|
||||
|
||||
operator T*() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
private:
|
||||
T* sp_object_;
|
||||
T* mp_object_;
|
||||
};
|
||||
|
||||
[[nodiscard]] int Cmd_Argc();
|
||||
[[nodiscard]] const char* Cmd_Argv(int index);
|
||||
|
||||
[[nodiscard]] int SV_Cmd_Argc();
|
||||
[[nodiscard]] const char* SV_Cmd_Argv(int index);
|
||||
|
||||
[[nodiscard]] bool is_headless();
|
||||
void show_error(const std::string& text, const std::string& title = "Error");
|
||||
|
||||
constexpr auto MAX_GENTITIES = 2048;
|
||||
constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1;
|
||||
|
||||
constexpr auto JUMP_LAND_SLOWDOWN_TIME = 1800;
|
||||
}
|
||||
|
||||
#include "symbols.hpp"
|
90
src/client/game/scripting/functions.cpp
Normal file
90
src/client/game/scripting/functions.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include <std_include.hpp>
|
||||
#include "functions.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "component/gsc/script_extension.hpp"
|
||||
#include "component/gsc/script_loading.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
int find_function_index(const std::string& name, [[maybe_unused]] const bool prefer_global)
|
||||
{
|
||||
const auto target = utils::string::to_lower(name);
|
||||
auto const& first = gsc::gsc_ctx->func_map();
|
||||
auto const& second = gsc::gsc_ctx->meth_map();
|
||||
|
||||
if (const auto itr = first.find(name); itr != first.end())
|
||||
{
|
||||
return static_cast<int>(itr->second);
|
||||
}
|
||||
|
||||
if (const auto itr = second.find(name); itr != second.end())
|
||||
{
|
||||
return static_cast<int>(itr->second);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::uint32_t parse_token_id(const std::string& name)
|
||||
{
|
||||
if (name.starts_with("_ID"))
|
||||
{
|
||||
return static_cast<std::uint32_t>(std::strtol(name.substr(3).data(), nullptr, 10));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string find_token(std::uint32_t id)
|
||||
{
|
||||
return gsc::gsc_ctx->token_name(id);
|
||||
}
|
||||
|
||||
std::string find_token_single(std::uint32_t id)
|
||||
{
|
||||
return gsc::gsc_ctx->token_name(id);
|
||||
}
|
||||
|
||||
unsigned int find_token_id(const std::string& name)
|
||||
{
|
||||
const auto id = gsc::gsc_ctx->token_id(name);
|
||||
if (id)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const auto parsed_id = parse_token_id(name);
|
||||
if (parsed_id)
|
||||
{
|
||||
return parsed_id;
|
||||
}
|
||||
|
||||
return game::SL_GetCanonicalString(name.data());
|
||||
}
|
||||
|
||||
script_function get_function_by_index(const std::uint32_t index)
|
||||
{
|
||||
static const auto function_table = &gsc::func_table;
|
||||
static const auto method_table = SELECT_VALUE(0x144E1F9E0, 0x1446B8A90);
|
||||
|
||||
if (index <= 0x1000)
|
||||
{
|
||||
return reinterpret_cast<script_function*>(function_table)[index - 1];
|
||||
}
|
||||
|
||||
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
|
||||
}
|
||||
|
||||
script_function find_function(const std::string& name, const bool prefer_global)
|
||||
{
|
||||
const auto index = find_function_index(name, prefer_global);
|
||||
if (index < 0) return nullptr;
|
||||
|
||||
return get_function_by_index(index);
|
||||
}
|
||||
}
|
14
src/client/game/scripting/functions.hpp
Normal file
14
src/client/game/scripting/functions.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
using script_function = void(*)(game::scr_entref_t);
|
||||
|
||||
std::string find_token(std::uint32_t id);
|
||||
std::string find_token_single(std::uint32_t id);
|
||||
unsigned int find_token_id(const std::string& name);
|
||||
|
||||
script_function get_function_by_index(std::uint32_t index);
|
||||
script_function find_function(const std::string& name, bool prefer_global);
|
||||
}
|
3009
src/client/game/structs.hpp
Normal file
3009
src/client/game/structs.hpp
Normal file
File diff suppressed because it is too large
Load Diff
350
src/client/game/symbols.hpp
Normal file
350
src/client/game/symbols.hpp
Normal file
@ -0,0 +1,350 @@
|
||||
#pragma once
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game
|
||||
{
|
||||
/***************************************************************
|
||||
* Functions
|
||||
**************************************************************/
|
||||
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0, 0x1404326D0};
|
||||
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x1403D7740, 0x1404326E0};
|
||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0, 0x1404329B0};
|
||||
WEAK symbol<ObjectVariableValue*(unsigned int* index)> AllocVariable{0x1403D7A70, 0x140432A10};
|
||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403D90F0, 0x1404340C0};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0, 0x140433FB0};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x1403D91C0, 0x140434190};
|
||||
|
||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x140139D80};
|
||||
|
||||
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0, 0x140239370};
|
||||
|
||||
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1403BC980, 0x1404131A0};
|
||||
WEAK symbol<const char*(char const**)> Com_Parse{0x1404313E0, 0x1404F50E0};
|
||||
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1403BBFF0, 0x140412740};
|
||||
WEAK symbol<void()> Com_Quit{0x1403BDDD0, 0x140414920};
|
||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1404f6140};
|
||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1404158C0};
|
||||
WEAK symbol<void(const char* text_in)> Com_TokenizeString{0x1403B4150, 0x1403F7CC0};
|
||||
WEAK symbol<void()> Com_EndTokenizeString{0x1403B37C0, 0x1403F7330};
|
||||
|
||||
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x14043DDE0, 0x1405028C0};
|
||||
|
||||
WEAK symbol<char*(int start)> ConcatArgs{0x14030AF10, 0x140392880};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1403B3050, 0x1403F6B50};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer, void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x1403B3160, 0x1403F6C60};
|
||||
|
||||
WEAK symbol<bool()> CL_IsCgameInitialized{0x140234DA0, 0x1402B9A70};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1401F2E20, 0x140271320};
|
||||
WEAK symbol<void(int localClientNum, mp::cg_s* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0x0, 0x14028A2C0};
|
||||
|
||||
WEAK symbol<void(const char* cmdName, void (), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1403B3570, 0x1403F7070};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x1403B3B10, 0x1403F7680};
|
||||
|
||||
WEAK symbol<void (XAssetType type, void (__cdecl *func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x140271F50, 0x14031EF90};
|
||||
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)> DB_EnumXAssets_Internal{0x140271FC0, 0x14031F000};
|
||||
WEAK symbol<XAssetEntry*(XAssetType type, const char* name)> DB_FindXAssetEntry{0x140272230, 0x14031F2D0};
|
||||
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x14024FB10, 0x1402FB160};
|
||||
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x14024FB30, 0x1402FB180};
|
||||
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x140273FD0, 0x140320F20};
|
||||
WEAK symbol<void(void* buffer, int size)> DB_ReadXFileUncompressed{0x140250FB0, 0x1402FC9C0};
|
||||
WEAK symbol<XAssetHeader(XAssetType type, const char* name, int allowCreateDefault)> DB_FindXAssetHeader{0x140272300, 0x14031F3A0};
|
||||
WEAK symbol<int(XAssetType type, const char* name)> DB_XAssetExists{0x140276200, 0x1403245E0};
|
||||
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{0x140273480 , 0x1403204D0};
|
||||
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x0140272E80, 0x14031FF80};
|
||||
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x140272D50, 0x14031FE50};
|
||||
WEAK symbol<int(const char* zoneName)> DB_IsLocalized{0x140273210, 0x140320360};
|
||||
|
||||
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source)> PMem_AllocFromSource_NoDebug{0x0140430B80, 0x001404F46C0};
|
||||
WEAK symbol<void(const char* name, PMem_Direction allocDir)> PMem_Free{0x140430EC0 , 0x1404F4A30};
|
||||
|
||||
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140423C70, 0x1404E4E20};
|
||||
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140429E70, 0x1404ECB60};
|
||||
WEAK symbol<void (char* buffer, int index)> Dvar_GetCombinedString{0x1403BFD80, 0x140416B30};
|
||||
WEAK symbol<bool(const char* dvarName)> Dvar_GetBool{0x140429FC0, 0x1404ECCB0};
|
||||
WEAK symbol<int(const char* dvarName)> Dvar_GetInt{0x14042A0A0, 0x1404ECD90};
|
||||
WEAK symbol<const char*(const char* dvarName, const char* defaultValue)> Dvar_GetVariantStringWithDefault{0x14042A240, 0x1404ECF90};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, bool value, unsigned int flags, const char* description)> Dvar_RegisterBool{0x14042AF10, 0x1404EDD60};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char** valueList, int defaultIndex, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterEnum{0x14042B220, 0x1404EE070};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float value, float min, float max, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterFloat{0x14042B330, 0x1404EE180};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max, unsigned int flags, const char* desc)> Dvar_RegisterInt{0x14042B420, 0x1404EE270};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, const char* value, unsigned int flags, const char* description)> Dvar_RegisterString{0x14042B7A0, 0x1404EE660};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float min, float max, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterVec2{0x14042B880, 0x1404EE740};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, float x, float y, float z, float w, float min, float max,
|
||||
unsigned int flags, const char* description)> Dvar_RegisterVec4{0x14042BC10, 0x1404EEA50};
|
||||
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x14042C150, 0x1404EF020};
|
||||
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x14042C370, 0x1404EF1A0};
|
||||
WEAK symbol<void(const char* dvar, const char* buffer)> Dvar_SetCommand{0x14042C8E0, 0x1404EF790};
|
||||
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x14042D6E0, 0x1404F08E0};
|
||||
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x14042D000, 0x1404F00B0};
|
||||
WEAK symbol<void()> Dvar_Sort{0x14042DEF0, 0x1404F1210};
|
||||
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x14042E710, 0x1404F1A30};
|
||||
|
||||
WEAK symbol<long long (const char* qpath, char** buffer)> FS_ReadFile{0x14041D0B0, 0x1404DE900};
|
||||
WEAK symbol<void(void* buffer)> FS_FreeFile{0x14041D0A0, 0x1404DE8F0};
|
||||
WEAK symbol<void(const char *gameName)> FS_Startup{0x14041C660, 0x1404DDEB0};
|
||||
WEAK symbol<void(const char *path, const char *dir, int bLanguageDirectory, int iLanguage)> FS_AddGameDirectory{0x14041A120, 0x1404DC570};
|
||||
WEAK symbol<void(const char *path, const char *dir)> FS_AddLocalizedGameDirectory{0x14041A2F0, 0x1404DC760};
|
||||
|
||||
WEAK symbol<Weapon(const char* pickupName, int model)> G_FindItem{0x140462490, 0x14021B7E0};
|
||||
WEAK symbol<int(playerState_s* ps, Weapon weapon, int dualWield, int startInAltMode, int usedBefore)> G_GivePlayerWeapon{0x140359E20, 0x1403DA5E0};
|
||||
WEAK symbol<Weapon(const char* name)> G_GetWeaponForName{0x140359890, 0x1403DA060};
|
||||
WEAK symbol<void()> G_Glass_Update{0x14030E680, 0x140397450};
|
||||
WEAK symbol<void (playerState_s* ps, Weapon weapon, int hadWeapon)> G_InitializeAmmo{0x140311F00, 0x14039AEA0};
|
||||
WEAK symbol<void(int clientNum, Weapon weapon)> G_SelectWeapon{0x14035A200, 0x1403DA880};
|
||||
WEAK symbol<int(playerState_s* ps, Weapon weapon)> G_TakePlayerWeapon{0x14035A350, 0x1403DA9C0};
|
||||
WEAK symbol<unsigned int (const char* name, /*ConfigString*/ unsigned int start, unsigned int max, int create,
|
||||
const char* errormsg)> G_FindConfigstringIndex{0x0, 0x140161F90};
|
||||
WEAK symbol<int(int server_time)> G_RunFrame{0x0, 0x1403A05E0};
|
||||
|
||||
WEAK symbol<game_hudelem_s*(int clientNum, int teamNum)> HudElem_Alloc{0x0, 0x1403997E0};
|
||||
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x140432460, 0x1404F63C0};
|
||||
|
||||
WEAK symbol<char*(GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
|
||||
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const void* initData)>
|
||||
Image_Setup{0x140517910, 0x1405E4380};
|
||||
|
||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x14023D9A0, 0x1402C40E0};
|
||||
|
||||
WEAK symbol<unsigned int (int)> Live_SyncOnlineDataFlags{0, 0x1405ABF70};
|
||||
|
||||
WEAK symbol<bool(int controllerIndex, unsigned int name, int value, StatsGroup statsGroup)> LiveStorage_PlayerDataSetIntByName{0x1403B8C20, 0x140404730};
|
||||
WEAK symbol<bool(std::uint8_t* persistentData, const char* lookupString, int value, std::uint8_t* modifiedFlags, StatsGroup statsGroup)> LiveStorage_PlayerDataSetReservedInt{0x1403B8D00, 0x140404820};
|
||||
WEAK symbol<std::int64_t(std::uint8_t* persistentData, const char* lookupString, const StatsGroup statsGroup)> LiveStorage_PlayerDataGetReservedInt{0x1403B84F0, 0x140403CF0};
|
||||
WEAK symbol<bool(int controllerIndex, const unsigned int* navStrings, int navStringCount, int value, StatsGroup statsGroup)> LiveStorage_PlayerDataSetIntByNameArray{0, 0x140404640};
|
||||
WEAK symbol<int(int controllerIndex, const unsigned int* navStrings, int navStringCount, StatsGroup statsGroup)> LiveStorage_PlayerDataGetIntByNameArray{0, 0x140403B80};
|
||||
WEAK symbol<int(int controllerIndex, unsigned int name, StatsGroup statsGroup)> LiveStorage_PlayerDataGetIntByName{0x1403B8460, 0x140403C40};
|
||||
WEAK symbol<std::uint8_t*(int controllerIndex)>LiveStorage_GetPersistentDataBuffer{0x1403B6F80, 0x140400170};
|
||||
WEAK symbol<void(int controllerIndex)> LiveStorage_StatsWriteNeeded{0x1403BA400, 0x1404090E0};
|
||||
|
||||
WEAK symbol<BOOL(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)> MainWndProc{0x14043E6A0, 0x140504A00};
|
||||
WEAK symbol<void()> IN_MouseMove{0x140438840, 0x1404FC7B0};
|
||||
WEAK symbol<void()> IN_RecenterMouse{0x1404389E0, 0x1404FC950};
|
||||
WEAK symbol<int(int x, int y, int dx, int dy)> CL_MouseEvent{0x14023B8E0, 0x1402C12A0};
|
||||
|
||||
WEAK symbol<StructuredDataDef*(const char* filename, unsigned int maxSize)>StructuredDataDef_GetAsset{0, 0x1404E6560};
|
||||
WEAK symbol<StringTable*(const char* fileName, const StringTable** tablePtr)>StringTable_GetAsset{0, 0x1404E6170};
|
||||
WEAK symbol<const char*(const StringTable* table, const int row, const int column)> StringTable_GetColumnValueForRow{0, 0x1404E61A0};
|
||||
WEAK symbol<int(const StringTable* table, const int comparisonColumn, const char* value)> StringTable_LookupRowNumForValue{0, 0x1404E6260};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)> LUI_OpenMenu{0x1403FD460, 0x1404B3610};
|
||||
// Made up name, replaced by ScopedCriticalSection on Black Ops 3
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0x1401AE940, 0x1401CD040};
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0x1401B0AA0, 0x1401CF1A0};
|
||||
|
||||
WEAK symbol<bool(int localClientNum, const char* menuName)> Menu_IsMenuOpenAndVisible{0x0, 0x1404B38A0};
|
||||
|
||||
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x140523D90, 0x1405F0E20};
|
||||
|
||||
WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0, 0x14041D5C0};
|
||||
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0, 0x14041D780};
|
||||
WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0, 0x14041D870};
|
||||
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0, 0x1404E53D0};
|
||||
|
||||
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
|
||||
float* color, Material* material)> R_AddCmdDrawStretchPic{0x140234460, 0x140600BE0};
|
||||
WEAK symbol<void(const char*, int, Font_s*, float, float, float, float, float, float*, int)> R_AddCmdDrawText{0x140533E40, 0x140601070};
|
||||
WEAK symbol<void(const char*, int, Font_s*, float, float, float, float, float, const float*, int, int, char)> R_AddCmdDrawTextWithCursor{0x140534170, 0x1406013A0};
|
||||
WEAK symbol<Font_s*(const char* font)> R_RegisterFont{0x1405130B0, 0x1405DFAC0};
|
||||
WEAK symbol<void()> R_SyncRenderThread{0x140535AF0, 0x140602D30};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x140513390, 0x1405DFDB0};
|
||||
|
||||
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x14024D150, 0x1402F6D40};
|
||||
|
||||
WEAK symbol<void()> GScr_LoadConsts{0x140367AA0, 0x1403E0420};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x1403D84F0, 0x1404334A0};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0, 0x1404333A0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int id)> GetVariableName{0x1403D8E90, 0x140433E60};
|
||||
WEAK symbol<void (VariableValue* result, unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x1403DC810, 0x140437860};
|
||||
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x1403D9AF0, 0x140434A10};
|
||||
WEAK symbol<const char*(unsigned int index)> Scr_GetString{0, 0x140439160};
|
||||
WEAK symbol<void(int value)> Scr_AddInt{0x0, 0x140437E70};
|
||||
WEAK symbol<void(const char* value)> Scr_AddString{0x0, 0x1404381D0};
|
||||
WEAK symbol<int(unsigned int index)> Scr_GetInt{0x0, 0x140438E10};
|
||||
WEAK symbol<float(unsigned int index)> Scr_GetFloat{0, 0x140438D60};
|
||||
WEAK symbol<unsigned int()> Scr_GetNumParam{0x1403DDF60, 0x140438EC0};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x1403DD500, 0x140438600};
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x1403DBDC0, 0x140436E10};
|
||||
WEAK symbol<void(int entnum, unsigned int classnum)> Scr_AddEntityNum{0x0, 0x140437F60};
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x140350E70, 0x1403D3FE0};
|
||||
WEAK symbol<void(unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x1403DE730, 0x140439700};
|
||||
WEAK symbol<void(unsigned int stringValue, unsigned int paramcount)> Scr_NotifyLevel{0x0, 0x1404397D0};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x0, 0x140436D60};
|
||||
WEAK symbol<bool(VariableValue* value)> Scr_CastString{0x0, 0x140434AC0};
|
||||
|
||||
WEAK symbol<unsigned __int16(int handle, unsigned int paramcount)> Scr_ExecThread{0x1403DD600, 0x1404386E0};
|
||||
WEAK symbol<unsigned int(const char* name)> Scr_LoadScript{0x1403D3C50, 0x14042EAA0};
|
||||
WEAK symbol<unsigned int(const char* script, unsigned int name)> Scr_GetFunctionHandle{0x1403D3AD0 , 0x14042E920};
|
||||
WEAK symbol<unsigned int(void* func, int type, unsigned int name)> Scr_RegisterFunction{0x1403D3530, 0x14042E330};
|
||||
WEAK symbol<void()> Scr_ErrorInternal{0x0, 0x140438660};
|
||||
|
||||
WEAK symbol<int(unsigned int)> GetObjectType{0x0, 0x140433CF0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0, 0x14043A280};
|
||||
|
||||
WEAK symbol<const char*(const char*)> SEH_StringEd_GetString{0, 0x1404A5F60};
|
||||
|
||||
WEAK symbol<const char*(unsigned int stringValue)> SL_ConvertToString{0x1403D6870, 0x1404317F0};
|
||||
WEAK symbol<unsigned int(const char* str, unsigned int user)> SL_GetString{0x1403D6CD0, 0x140431C70};
|
||||
WEAK symbol<unsigned int(int)> SL_GetStringForInt{0x1403D6D50, 0x140431CF0};
|
||||
WEAK symbol<unsigned int(const char* str)> SL_FindString{0x1403D6AF0, 0x140431A90};
|
||||
WEAK symbol<unsigned int(char const* str)> SL_GetCanonicalString{0x1403D36F0, 0x14042E4F0};
|
||||
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x1403B4560, 0x1403F80D0};
|
||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0, 0x1403F8150};
|
||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0, 0x1403F8110};
|
||||
WEAK symbol<void()> SV_MatchEnd{0x0, 0x14047A090};
|
||||
|
||||
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0, 0x140471390};
|
||||
WEAK symbol<void(int, int, const char*)> SV_GameSendServerCommand{0x140490F40, 0x1404758C0};
|
||||
WEAK symbol<bool()> SV_Loaded{0x140491820, 0x1404770C0};
|
||||
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded)> SV_StartMap{0, 0x140470170};
|
||||
WEAK symbol<void(int localClientNum, const char* map, bool mapIsPreloaded, bool migrate)> SV_StartMapForParty{0, 0x1404702F0};
|
||||
|
||||
WEAK symbol<mp::gentity_s*(const char*, unsigned int, unsigned int, unsigned int)> SV_AddBot{0, 0x140470920};
|
||||
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0, 0x140461340};
|
||||
WEAK symbol<const char*()> SV_BotGetRandomName{0, 0x140460B80};
|
||||
WEAK symbol<void(mp::gentity_s*)> SV_SpawnTestClient{0, 0x1404740A0};
|
||||
|
||||
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x140472430};
|
||||
WEAK symbol<void()> SV_FastRestart{0x14048B890, 0x14046F440};
|
||||
WEAK symbol<playerState_s*(int num)> SV_GetPlayerstateForClientNum{0x140490F80, 0x140475A10};
|
||||
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0, 0x140475990};
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14046F730};
|
||||
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140477450};
|
||||
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x14043AC20, 0x1404FF510};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403C2D40, 0x140423920};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x14043D2A0, 0x140501CA0};
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x14043E650, 0x140503130};
|
||||
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x14043D000, 0x140501A00};
|
||||
WEAK symbol<void*(int valueIndex)> Sys_GetValue{0x1403C2C30, 0x1404237D0};
|
||||
WEAK symbol<bool()> Sys_IsMainThread{0x1478FC470, 0x140423950};
|
||||
WEAK symbol<HANDLE(Sys_Folder folder, const char* baseFilename)> Sys_CreateFile{0x140434B10, 0x1404F8FD0};
|
||||
|
||||
WEAK symbol<void()> SwitchToCoreMode{0, 0x1401FA4A0};
|
||||
WEAK symbol<void()> SwitchToAliensMode{0, 0x1401FA4D0};
|
||||
WEAK symbol<void()> SwitchToSquadVsSquadMode{0, 0x1401FA500};
|
||||
|
||||
WEAK symbol<const char*(const char*)> UI_LocalizeMapname{0, 0x1404B96D0};
|
||||
WEAK symbol<const char*(const char*)> UI_LocalizeGametype{0, 0x1404B90F0};
|
||||
|
||||
WEAK symbol<DWOnlineStatus(int)> dwGetLogOnStatus{0, 0x140589490};
|
||||
|
||||
WEAK symbol<void(pmove_t* move, trace_t*, const float*, const float*,
|
||||
const Bounds*, int, int)> PM_playerTrace{0x14046C910, 0x140225C20};
|
||||
WEAK symbol<void(const pmove_t* move, trace_t* trace, const float*,
|
||||
const float*, const Bounds*, int, int)> PM_trace{0, 0x140225DB0};
|
||||
|
||||
WEAK symbol<void(void* ps)> Jump_ClearState{0x0, 0x140213120};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14062E030, 0x140738060};
|
||||
WEAK symbol<int (jmp_buf* Buf)> _setjmp{0x14062F030, 0x140739060};
|
||||
|
||||
/***************************************************************
|
||||
* Variables
|
||||
**************************************************************/
|
||||
|
||||
WEAK symbol<int> keyCatchers{0x1417CF6E0, 0x1419E1ADC};
|
||||
|
||||
WEAK symbol<CmdArgs> cmd_args{0x144CE7F70, 0x144518480};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x144CE8020, 0x144518530};
|
||||
WEAK symbol<cmd_function_s*> cmd_functions{0x144CE80C8, 0x1445185D8};
|
||||
|
||||
WEAK symbol<int> dvarCount{0x1458CBA3C, 0x1478EADF4};
|
||||
WEAK symbol<dvar_t*> sortedDvars{0x1458CBA60, 0x1478EAE10};
|
||||
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0x14164138C, 0x1419DEABC};
|
||||
|
||||
WEAK symbol<SOCKET> query_socket{0, 0x147AD1A78};
|
||||
|
||||
WEAK symbol<const char*> command_whitelist{0x14086AA70, 0x1409E3AB0};
|
||||
|
||||
WEAK symbol<unsigned int> levelEntityId{0x1452A9F30, 0x144A43020};
|
||||
WEAK symbol<int> g_script_error_level{0x1455B1F98, 0x144D535C4};
|
||||
WEAK symbol<jmp_buf> g_script_error{0x1455BA5E0, 0x144D536E0};
|
||||
WEAK symbol<scr_classStruct_t> g_classMap{0x140873E20, 0x1409EBFC0};
|
||||
|
||||
WEAK symbol<void*> DB_XAssetPool{0x14086DCB0, 0x1409E4F20};
|
||||
// db_hashTable
|
||||
WEAK symbol<int> g_poolSize{0x14086DBB0, 0x1409E4E20};
|
||||
// g_assetEntryPool
|
||||
WEAK symbol<const char*> g_assetNames{0x14086CA40, 0x1409E40C0};
|
||||
|
||||
WEAK symbol<DB_LoadData> g_load{0x1418CF4A0, 0x141EEFBA0};
|
||||
|
||||
WEAK symbol<WinVars_t> g_wv{0x145A7BAD0, 0x147AD2630};
|
||||
WEAK symbol<WinMouseVars_t> s_wmv{0x145A73750, 0x147AC9D78};
|
||||
|
||||
WEAK symbol<int> window_center_x{0x145A73760, 0x147AC9D74};
|
||||
WEAK symbol<int> window_center_y{0x145A73764, 0x147AC9D88};
|
||||
|
||||
WEAK symbol<vidConfig_t> vidConfigOut{0x141644464, 0x141D1ACE0};
|
||||
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x1452CDF80, 0x144A67080};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0x1455B1FA0, 0x144D4B090};
|
||||
WEAK symbol<function_stack_t> scr_function_stack{0x1455BE708, 0x144D57808};
|
||||
WEAK symbol<unsigned int> scr_levelEntityId{0x1452A9F30, 0x144A43020};
|
||||
|
||||
WEAK symbol<int> level_time{0x0, 0x1443F4B6C};
|
||||
WEAK symbol<int> level_finished{0x0, 0x1443F6FAC};
|
||||
WEAK symbol<int> level_savepersist{0x0, 0x1443F5710};
|
||||
|
||||
WEAK symbol<DWORD> threadIds{0x144DE6640, 0x1446B4960};
|
||||
|
||||
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x145F525A8, 0x1480350D8};
|
||||
|
||||
namespace sp
|
||||
{
|
||||
WEAK symbol<gentity_s> g_entities{0x143C91600, 0};
|
||||
|
||||
WEAK symbol<XZone> g_zones_0{0x1434892D8, 0};
|
||||
}
|
||||
|
||||
namespace mp
|
||||
{
|
||||
WEAK symbol<cg_s> cgArray{0, 0x14176EC00};
|
||||
|
||||
WEAK symbol<gentity_s> g_entities{0, 0x14427A0E0};
|
||||
|
||||
WEAK symbol<int> svs_clientCount{0, 0x14647B28C};
|
||||
WEAK symbol<client_t> svs_clients{0, 0x14647B290};
|
||||
|
||||
WEAK symbol<std::uint32_t> sv_serverId_value{0, 0x144DF9478};
|
||||
|
||||
WEAK symbol<int> gameTime{0, 0x1443F4B6C};
|
||||
WEAK symbol<int> serverTime{0, 0x14647B280};
|
||||
|
||||
WEAK symbol<XZone> g_zones_0{0, 0x143A46498};
|
||||
}
|
||||
|
||||
namespace hks
|
||||
{
|
||||
WEAK symbol<lua_State*> lua_state{0, 0x141640DA0};
|
||||
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0, 0x14019DEC0};
|
||||
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0, 0x1401994B0};
|
||||
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0, 0x14019A570};
|
||||
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0, 0x1401998F0};
|
||||
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0, 0x1401C6420};
|
||||
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0, 0x140186BD0};
|
||||
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues, int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0, 0x140186DF0};
|
||||
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0, 0x14019C5C0};
|
||||
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0, 0x14019C750};
|
||||
WEAK symbol<int(lua_State* s, int what, int data)> hks_lua_gc{0, 0x1401A4790};
|
||||
WEAK symbol<int(lua_State* s, const char* filename)> hksi_hks_memorystats{0, 0x14019B580};
|
||||
|
||||
WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff, unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{0, 0x14019AD90};
|
||||
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0, 0x14019CCF0};
|
||||
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0, 0x14019CFB0};
|
||||
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0, 0x14019C4C0};
|
||||
WEAK symbol<const char*> s_compilerTypeName{0, 0x1409DE040};
|
||||
}
|
||||
}
|
172
src/client/game/ui_scripting/execution.cpp
Normal file
172
src/client/game/ui_scripting/execution.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
|
||||
#include "component/ui_scripting.hpp"
|
||||
#include "component/console.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
script_value get_field(void* ptr, game::hks::HksObjectType type, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(key);
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = type;
|
||||
obj.v.ptr = ptr;
|
||||
|
||||
game::hks::hks_obj_gettable(&value, state, &obj, &state->m_apistack.top[-1]);
|
||||
state->m_apistack.top = top;
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_field(void* ptr, game::hks::HksObjectType type, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = type;
|
||||
obj.v.ptr = ptr;
|
||||
|
||||
game::hks::hks_obj_settable(state, &obj, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
}
|
||||
|
||||
void push_value(const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
*state->m_apistack.top = value.get_raw();
|
||||
state->m_apistack.top++;
|
||||
}
|
||||
|
||||
void push_value(const game::hks::HksObject& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
*state->m_apistack.top = value;
|
||||
state->m_apistack.top++;
|
||||
}
|
||||
|
||||
script_value get_return_value(std::int64_t offset)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
return state->m_apistack.top[-1 - offset];
|
||||
}
|
||||
|
||||
arguments get_return_values()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto count = state->m_apistack.top - state->m_apistack.base;
|
||||
arguments values;
|
||||
|
||||
for (auto i = count - 1; i >= 0; i--)
|
||||
{
|
||||
values.push_back(get_return_value(i));
|
||||
}
|
||||
|
||||
if (values.empty())
|
||||
{
|
||||
values.push_back({});
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
arguments get_return_values(game::hks::HksObject* base)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto count = state->m_apistack.top - base;
|
||||
arguments values;
|
||||
|
||||
for (auto i = count - 1; i >= 0; i--)
|
||||
{
|
||||
values.push_back(get_return_value(i));
|
||||
}
|
||||
|
||||
if (values.empty())
|
||||
{
|
||||
values.push_back({});
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
bool notify(const std::string& name, const event_arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (state == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _0 = gsl::finally(game::LUI_LeaveCriticalSection);
|
||||
game::LUI_EnterCriticalSection();
|
||||
|
||||
try
|
||||
{
|
||||
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
|
||||
const auto engine = globals.get("Engine").as<table>();
|
||||
const auto root = engine.get("GetLuiRoot")()[0].as<userdata>();
|
||||
const auto process_event = root.get("processEvent");
|
||||
|
||||
table event{};
|
||||
event.set("name", name);
|
||||
event.set("dispatchChildren", true);
|
||||
|
||||
for (const auto& arg : arguments)
|
||||
{
|
||||
event.set(arg.first, arg.second);
|
||||
}
|
||||
|
||||
process_event(root, event);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
console::error("Error processing event '%s' %s\n", name.data(), ex.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(function);
|
||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, nullptr);
|
||||
const auto args = get_return_values(top);
|
||||
state->m_apistack.top = top;
|
||||
return args;
|
||||
}
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key)
|
||||
{
|
||||
return get_field(self.ptr, game::hks::TUSERDATA, key);
|
||||
}
|
||||
|
||||
script_value get_field(const table& self, const script_value& key)
|
||||
{
|
||||
return get_field(self.ptr, game::hks::TTABLE, key);
|
||||
}
|
||||
|
||||
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
set_field(self.ptr, game::hks::TUSERDATA, key, value);
|
||||
}
|
||||
|
||||
void set_field(const table& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
set_field(self.ptr, game::hks::TTABLE, key, value);
|
||||
}
|
||||
}
|
23
src/client/game/ui_scripting/execution.hpp
Normal file
23
src/client/game/ui_scripting/execution.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
void push_value(const script_value& value);
|
||||
void push_value(const game::hks::HksObject& value);
|
||||
|
||||
script_value get_return_value(std::int64_t offset);
|
||||
arguments get_return_values();
|
||||
arguments get_return_values(game::hks::HksObject* base);
|
||||
|
||||
bool notify(const std::string& name, const event_arguments& arguments);
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments);
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key);
|
||||
script_value get_field(const table& self, const script_value& key);
|
||||
void set_field(const userdata& self, const script_value& key, const script_value& value);
|
||||
void set_field(const table& self, const script_value& key, const script_value& value);
|
||||
}
|
402
src/client/game/ui_scripting/script_value.cpp
Normal file
402
src/client/game/ui_scripting/script_value.cpp
Normal file
@ -0,0 +1,402 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
#include "component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
hks_object::hks_object(const game::hks::HksObject& value)
|
||||
{
|
||||
this->assign(value);
|
||||
}
|
||||
|
||||
hks_object::hks_object(const hks_object& other) noexcept
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
hks_object::hks_object(hks_object&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
hks_object& hks_object::operator=(const hks_object& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->assign(other.value_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
hks_object& hks_object::operator=(hks_object&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->value_ = other.value_;
|
||||
other.value_.t = game::hks::TNONE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
hks_object::~hks_object()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
const game::hks::HksObject& hks_object::get() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
void hks_object::assign(const game::hks::HksObject& value)
|
||||
{
|
||||
this->value_ = value;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(this->value_);
|
||||
this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void hks_object::release()
|
||||
{
|
||||
if (this->ref_)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_);
|
||||
this->value_.t = game::hks::TNONE;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Constructors
|
||||
**************************************************************/
|
||||
|
||||
script_value::script_value(const game::hks::HksObject& value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const int value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const unsigned int value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const bool value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TBOOLEAN;
|
||||
obj.v.boolean = value;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const float value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const double value)
|
||||
: script_value(static_cast<float>(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value, const std::size_t len)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (state == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto top = state->m_apistack.top;
|
||||
game::hks::hksi_lua_pushlstring(state, value, static_cast<std::uint32_t>(len));
|
||||
obj = state->m_apistack.top[-1];
|
||||
state->m_apistack.top = top;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value)
|
||||
: script_value(value, std::strlen(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const std::string& value)
|
||||
: script_value(value.data(), value.size())
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const lightuserdata& value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TLIGHTUSERDATA;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const userdata& value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TUSERDATA;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const table& value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TTABLE;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const function& value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = value.type;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Integer
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<int>() const
|
||||
{
|
||||
const auto number = this->get_raw().v.number;
|
||||
return this->get_raw().t == game::hks::TNUMBER && static_cast<int>(number) == number;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<unsigned int>() const
|
||||
{
|
||||
return this->is<int>();
|
||||
}
|
||||
|
||||
template <>
|
||||
int script_value::get() const
|
||||
{
|
||||
return static_cast<int>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned int script_value::get() const
|
||||
{
|
||||
return static_cast<unsigned int>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Boolean
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<bool>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TBOOLEAN;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.boolean;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Float
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<float>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TNUMBER;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<double>() const
|
||||
{
|
||||
return this->is<float>();
|
||||
}
|
||||
|
||||
template <>
|
||||
float script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.number;
|
||||
}
|
||||
|
||||
template <>
|
||||
double script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.number;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* String
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<const char*>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TSTRING;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<std::string>() const
|
||||
{
|
||||
return this->is<const char*>();
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.str->m_data;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string script_value::get() const
|
||||
{
|
||||
return this->get<const char*>();
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<lightuserdata>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
|
||||
}
|
||||
|
||||
template <>
|
||||
lightuserdata script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<userdata>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TUSERDATA;
|
||||
}
|
||||
|
||||
template <>
|
||||
userdata script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<table>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TTABLE;
|
||||
}
|
||||
|
||||
template <>
|
||||
table script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.table;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<function>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TIFUNCTION
|
||||
|| this->get_raw().t == game::hks::TCFUNCTION;
|
||||
}
|
||||
|
||||
template <>
|
||||
function script_value::get() const
|
||||
{
|
||||
return {this->get_raw().v.cClosure, this->get_raw().t};
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
**************************************************************/
|
||||
|
||||
const game::hks::HksObject& script_value::get_raw() const
|
||||
{
|
||||
return this->value_.get();
|
||||
}
|
||||
|
||||
bool script_value::operator==(const script_value& other) const
|
||||
{
|
||||
if (this->get_raw().t != other.get_raw().t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->get_raw().t == game::hks::TSTRING)
|
||||
{
|
||||
return this->get<std::string>() == other.get<std::string>();
|
||||
}
|
||||
|
||||
return this->get_raw().v.native == other.get_raw().v.native;
|
||||
}
|
||||
|
||||
arguments script_value::operator()() const
|
||||
{
|
||||
return this->as<function>()();
|
||||
}
|
||||
|
||||
arguments script_value::operator()(const arguments& arguments) const
|
||||
{
|
||||
return this->as<function>()(arguments);
|
||||
}
|
||||
|
||||
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
|
||||
: values_(args), value_(value), index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
function_arguments::function_arguments(const arguments& values)
|
||||
: values_(values)
|
||||
{
|
||||
}
|
||||
}
|
251
src/client/game/ui_scripting/script_value.hpp
Normal file
251
src/client/game/ui_scripting/script_value.hpp
Normal file
@ -0,0 +1,251 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata;
|
||||
class userdata_value;
|
||||
class userdata;
|
||||
class table_value;
|
||||
class table;
|
||||
class function;
|
||||
class script_value;
|
||||
|
||||
template <typename T>
|
||||
std::string get_typename()
|
||||
{
|
||||
auto& info = typeid(T);
|
||||
|
||||
if (info == typeid(std::string) ||
|
||||
info == typeid(const char*))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (info == typeid(lightuserdata))
|
||||
{
|
||||
return "lightuserdata";
|
||||
}
|
||||
|
||||
if (info == typeid(userdata))
|
||||
{
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
if (info == typeid(table))
|
||||
{
|
||||
return "table";
|
||||
}
|
||||
|
||||
if (info == typeid(function))
|
||||
{
|
||||
return "function";
|
||||
}
|
||||
|
||||
if (info == typeid(int) ||
|
||||
info == typeid(float) ||
|
||||
info == typeid(unsigned int))
|
||||
{
|
||||
return "number";
|
||||
}
|
||||
|
||||
if (info == typeid(bool))
|
||||
{
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
return info.name();
|
||||
}
|
||||
|
||||
class hks_object
|
||||
{
|
||||
public:
|
||||
hks_object() = default;
|
||||
hks_object(const game::hks::HksObject& value);
|
||||
hks_object(const hks_object& other) noexcept;
|
||||
hks_object(hks_object&& other) noexcept;
|
||||
|
||||
hks_object& operator=(const hks_object& other) noexcept;
|
||||
hks_object& operator=(hks_object&& other) noexcept;
|
||||
|
||||
~hks_object();
|
||||
|
||||
const game::hks::HksObject& get() const;
|
||||
|
||||
private:
|
||||
void assign(const game::hks::HksObject& value);
|
||||
void release();
|
||||
|
||||
game::hks::HksObject value_{game::hks::TNONE, {}};
|
||||
int ref_{};
|
||||
};
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
using event_arguments = std::unordered_map<std::string, script_value>;
|
||||
|
||||
class script_value
|
||||
{
|
||||
public:
|
||||
script_value() = default;
|
||||
script_value(const game::hks::HksObject& value);
|
||||
|
||||
script_value(int value);
|
||||
script_value(unsigned int value);
|
||||
script_value(bool value);
|
||||
|
||||
script_value(float value);
|
||||
script_value(double value);
|
||||
|
||||
script_value(const char* value, std::size_t len);
|
||||
script_value(const char* value);
|
||||
script_value(const std::string& value);
|
||||
|
||||
script_value(const lightuserdata& value);
|
||||
script_value(const userdata& value);
|
||||
script_value(const table& value);
|
||||
script_value(const function& value);
|
||||
|
||||
template <template<class, class> class C, class T, typename TableType = table>
|
||||
script_value(const C<T, std::allocator<T>>& container)
|
||||
{
|
||||
TableType table_{};
|
||||
int index = 1;
|
||||
|
||||
for (const auto& value : container)
|
||||
{
|
||||
table_.set(index++, value);
|
||||
}
|
||||
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TTABLE;
|
||||
obj.v.ptr = table_.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
script_value(F f)
|
||||
: script_value(function(f))
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const script_value& other) const;
|
||||
|
||||
arguments operator()() const;
|
||||
arguments operator()(const arguments& arguments) const;
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->as<function>().call({ arguments... });
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
table_value operator[](const char(&key)[Size]) const
|
||||
{
|
||||
return {this->as<table>(), key};
|
||||
}
|
||||
|
||||
template <typename T = script_value>
|
||||
table_value operator[](const T& key) const
|
||||
{
|
||||
return {this->as<table>(), key};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] bool is() const;
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
if (!this->is<T>())
|
||||
{
|
||||
const auto hks_typename = game::hks::s_compilerTypeName[this->get_raw().t + 2];
|
||||
const auto typename_ = get_typename<T>();
|
||||
|
||||
throw std::runtime_error(utils::string::va("%s expected, got %s", typename_.data(), hks_typename));
|
||||
}
|
||||
|
||||
return get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T() const
|
||||
{
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] const game::hks::HksObject& get_raw() const;
|
||||
|
||||
hks_object value_{};
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T get() const;
|
||||
};
|
||||
|
||||
class variadic_args : public arguments
|
||||
{
|
||||
};
|
||||
|
||||
class function_argument
|
||||
{
|
||||
public:
|
||||
function_argument(const arguments& args, const script_value& value, const int index);
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
try
|
||||
{
|
||||
return this->value_.as<T>();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(utils::string::va("bad argument #%d (%s)", this->index_ + 1, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
variadic_args as() const
|
||||
{
|
||||
variadic_args args{};
|
||||
for (auto i = this->index_; i < this->values_.size(); i++)
|
||||
{
|
||||
args.push_back(this->values_[i]);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T() const
|
||||
{
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
arguments values_{};
|
||||
script_value value_{};
|
||||
int index_{};
|
||||
};
|
||||
|
||||
class function_arguments
|
||||
{
|
||||
public:
|
||||
function_arguments(const arguments& values);
|
||||
|
||||
function_argument operator[](const int index) const
|
||||
{
|
||||
if (static_cast<std::size_t>(index) >= values_.size())
|
||||
{
|
||||
return {values_, {}, index};
|
||||
}
|
||||
|
||||
return {values_, values_[index], index};
|
||||
}
|
||||
private:
|
||||
arguments values_{};
|
||||
};
|
||||
}
|
351
src/client/game/ui_scripting/types.cpp
Normal file
351
src/client/game/ui_scripting/types.cpp
Normal file
@ -0,0 +1,351 @@
|
||||
#include <std_include.hpp>
|
||||
#include "types.hpp"
|
||||
#include "execution.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
lightuserdata::lightuserdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
userdata::userdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
userdata::userdata(const userdata& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
userdata::userdata(userdata&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
userdata::~userdata()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(const userdata& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(userdata&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void userdata::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.ptr = this->ptr;
|
||||
value.t = game::hks::TUSERDATA;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void userdata::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value userdata::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
userdata_value userdata::operator[](const script_value& key) const
|
||||
{
|
||||
return {*this, key};
|
||||
}
|
||||
|
||||
userdata_value::userdata_value(const userdata& table, const script_value& key)
|
||||
: userdata_(table), key_(key)
|
||||
{
|
||||
this->value_ = this->userdata_.get(key).get_raw();
|
||||
}
|
||||
|
||||
void userdata_value::operator=(const script_value& value)
|
||||
{
|
||||
this->userdata_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
bool userdata_value::operator==(const script_value& value)
|
||||
{
|
||||
return this->userdata_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
table::table()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(game::hks::HashTable* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(const table& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
table::table(table&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
table::~table()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
table& table::operator=(const table& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
table& table::operator=(table&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void table::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.table = this->ptr;
|
||||
value.t = game::hks::TTABLE;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void table::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void table::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
table_value table::operator[](const script_value& key) const
|
||||
{
|
||||
return {*this, key};
|
||||
}
|
||||
|
||||
script_value table::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
table_value::table_value(const table& table, const script_value& key)
|
||||
: table_(table), key_(key)
|
||||
{
|
||||
this->value_ = this->table_.get(key).get_raw();
|
||||
}
|
||||
|
||||
void table_value::operator=(const script_value& value)
|
||||
{
|
||||
this->table_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
void table_value::operator=(const table_value& value)
|
||||
{
|
||||
this->table_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
bool table_value::operator==(const script_value& value)
|
||||
{
|
||||
return this->table_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
bool table_value::operator==(const table_value& value)
|
||||
{
|
||||
return this->table_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
function::function(game::hks::lua_function func)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0);
|
||||
this->type = game::hks::HksObjectType::TCFUNCTION;
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||
: ptr(ptr_), type(type_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(const function& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
function::function(function&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
function::~function()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
function& function::operator=(const function& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
function& function::operator=(function&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void function::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.cClosure = this->ptr;
|
||||
value.t = this->type;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void function::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
arguments function::call(const arguments& arguments) const
|
||||
{
|
||||
return call_script_function(*this, arguments);
|
||||
}
|
||||
|
||||
arguments function::operator()(const arguments& arguments) const
|
||||
{
|
||||
return this->call(arguments);
|
||||
}
|
||||
|
||||
arguments function::operator()() const
|
||||
{
|
||||
return this->call({});
|
||||
}
|
||||
}
|
141
src/client/game/ui_scripting/types.hpp
Normal file
141
src/client/game/ui_scripting/types.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "script_value.hpp"
|
||||
#include "component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata
|
||||
{
|
||||
public:
|
||||
lightuserdata(void*);
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
class userdata_value;
|
||||
|
||||
class userdata
|
||||
{
|
||||
public:
|
||||
userdata(void*);
|
||||
|
||||
userdata(const userdata& other);
|
||||
userdata(userdata&& other) noexcept;
|
||||
|
||||
~userdata();
|
||||
|
||||
userdata& operator=(const userdata& other);
|
||||
userdata& operator=(userdata&& other) noexcept;
|
||||
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
userdata_value operator[](const script_value& key) const;
|
||||
|
||||
void* ptr;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
|
||||
class userdata_value : public script_value
|
||||
{
|
||||
public:
|
||||
userdata_value(const userdata& table, const script_value& key);
|
||||
void operator=(const script_value& value);
|
||||
bool operator==(const script_value& value);
|
||||
private:
|
||||
userdata userdata_;
|
||||
script_value key_;
|
||||
};
|
||||
|
||||
class table_value;
|
||||
|
||||
class table
|
||||
{
|
||||
public:
|
||||
table();
|
||||
table(game::hks::HashTable* ptr_);
|
||||
|
||||
table(const table& other);
|
||||
table(table&& other) noexcept;
|
||||
|
||||
~table();
|
||||
|
||||
table& operator=(const table& other);
|
||||
table& operator=(table&& other) noexcept;
|
||||
|
||||
[[nodiscard]] script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
table_value operator[](const script_value& key) const;
|
||||
|
||||
game::hks::HashTable* ptr;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
|
||||
class table_value : public script_value
|
||||
{
|
||||
public:
|
||||
table_value(const table& table, const script_value& key);
|
||||
void operator=(const script_value& value);
|
||||
void operator=(const table_value& value);
|
||||
bool operator==(const script_value& value);
|
||||
bool operator==(const table_value& value);
|
||||
private:
|
||||
table table_;
|
||||
script_value key_;
|
||||
};
|
||||
|
||||
|
||||
class function
|
||||
{
|
||||
public:
|
||||
function(game::hks::lua_function);
|
||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||
|
||||
template <typename F>
|
||||
function(F f)
|
||||
{
|
||||
this->ptr = ui_scripting::convert_function(f);
|
||||
this->type = game::hks::TCFUNCTION;
|
||||
}
|
||||
|
||||
function(const function& other);
|
||||
function(function&& other) noexcept;
|
||||
|
||||
~function();
|
||||
|
||||
function& operator=(const function& other);
|
||||
function& operator=(function&& other) noexcept;
|
||||
|
||||
arguments call(const arguments& arguments) const;
|
||||
|
||||
arguments operator()(const arguments& arguments) const;
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->call({arguments...});
|
||||
}
|
||||
|
||||
arguments operator()() const;
|
||||
|
||||
game::hks::cclosure* ptr;
|
||||
game::hks::HksObjectType type;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user