savepoint, changelog later..

This commit is contained in:
Dmitry 2019-07-27 17:36:24 +03:00 committed by jeefo
commit 1bc1fd1913
45 changed files with 12866 additions and 10981 deletions

View file

@ -1,4 +1,4 @@
Copyright (c) 2003-2018, YaPB Dev Team
Copyright (c) 2003-2019, YaPB Development Team
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

File diff suppressed because it is too large Load diff

78
include/crlib/cr-alloc.h Normal file
View file

@ -0,0 +1,78 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <new>
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-platform.h>
CR_NAMESPACE_BEGIN
// default allocator for cr-objects
class Allocator : public Singleton <Allocator> {
public:
Allocator () = default;
~Allocator () = default;
public:
template <typename T> T *allocate (const size_t length = 1) {
auto ptr = reinterpret_cast <T *> (calloc (length, sizeof (T)));
if (!ptr) {
plat.abort ();
}
// calloc on linux with debug enabled doesn't always zero out memory
#if defined (CR_DEBUG) && !defined (CR_WINDOWS)
auto *zeroing = reinterpret_cast <uint8 *> (ptr);
for (size_t i = 0; i < length; ++i) {
zeroing[i] = 0;
}
#endif
return ptr;
}
template <typename T> void deallocate (T *ptr) {
if (ptr) {
free (reinterpret_cast <T *> (ptr));
}
}
public:
template <typename T, typename ...Args> void construct (T *d, Args &&...args) {
new (d) T (cr::forward <Args> (args)...);
}
template <typename T> void destruct (T *d) {
d->~T ();
}
public:
template <typename T, typename ...Args> T *create (Args &&...args) {
auto d = allocate <T> ();
new (d) T (cr::forward <Args> (args)...);
return d;
}
template <typename T> void destroy (T *ptr) {
if (ptr) {
destruct (ptr);
deallocate (ptr);
}
}
};
static auto &alloc = Allocator::get ();
CR_NAMESPACE_END

390
include/crlib/cr-array.h Normal file
View file

@ -0,0 +1,390 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-random.h>
// policy to reserve memory
CR_DECLARE_SCOPED_ENUM (ReservePolicy,
MultipleByTwo,
PlusOne
);
CR_NAMESPACE_BEGIN
// simple array class like std::vector
template <typename T, ReservePolicy R = ReservePolicy::MultipleByTwo> class Array : public DenyCopying {
public:
T *m_data = nullptr;
size_t m_capacity = 0;
size_t m_length = 0;
public:
explicit Array () = default;
Array (const size_t amount) {
reserve (amount);
}
Array (Array &&rhs) noexcept {
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
~Array () {
destroy ();
}
private:
void destructElements () noexcept {
for (size_t i = 0; i < m_length; ++i) {
alloc.destruct (&m_data[i]);
}
}
void transferElements (T *dest, T *src, size_t length) noexcept {
for (size_t i = 0; i < length; ++i) {
alloc.construct (&dest[i], cr::move (src[i]));
alloc.destruct (&src[i]);
}
}
void destroy () {
destructElements ();
alloc.deallocate (m_data);
}
void reset () {
m_data = nullptr;
m_capacity = 0;
m_length = 0;
}
public:
bool reserve (const size_t amount) {
if (m_length + amount < m_capacity) {
return true;
}
auto capacity = m_capacity ? m_capacity : 8;
if (cr::fix (R == ReservePolicy::MultipleByTwo)) {
while (m_length + amount > capacity) {
capacity *= 2;
}
}
else {
capacity = amount + m_capacity + 1;
}
auto data = alloc.allocate <T> (capacity);
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
m_data = data;
m_capacity = capacity;
return true;
}
bool resize (const size_t amount) {
if (amount < m_length) {
while (amount < m_length) {
discard ();
}
}
else if (amount > m_length) {
if (!ensure (amount)) {
return false;
}
size_t pushLength = amount - m_length;
for (size_t i = 0; i < pushLength; ++i) {
push (T ());
}
}
return true;
}
bool ensure (const size_t amount) {
if (amount <= m_length) {
return true;
}
return reserve (amount - m_length);
}
size_t length () const {
return m_length;
}
size_t capacity () const {
return m_capacity;
}
template <typename U> bool set (size_t index, U &&object) {
if (index >= m_capacity) {
if (!reserve (index + 1)) {
return false;
}
}
alloc.construct (&m_data[index], cr::forward <U> (object));
if (index >= m_length) {
m_length = index + 1;
}
return true;
}
template <typename U> bool insert (size_t index, U &&object) {
return insert (index, &object, 1);
}
template <typename U> bool insert (size_t index, U *objects, size_t count = 1) {
if (!objects || !count) {
return false;
}
const size_t capacity = (m_length > index ? m_length : index) + count;
if (capacity >= m_capacity && !reserve (capacity)) {
return false;
}
if (index >= m_length) {
for (size_t i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length = capacity;
}
else {
size_t i = 0;
for (i = m_length; i > index; --i) {
m_data[i + count - 1] = cr::move (m_data[i - 1]);
}
for (i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length += count;
}
return true;
}
bool insert (size_t at, const Array &rhs) {
if (&rhs == this) {
return false;
}
return insert (at, &rhs.m_data[0], rhs.m_length);
}
bool erase (const size_t index, const size_t count) {
if (index + count > m_capacity) {
return false;
}
m_length -= count;
for (size_t i = index; i < m_length; ++i) {
m_data[i] = cr::move (m_data[i + count]);
}
return true;
}
bool shift () {
return erase (0, 1);
}
template <typename U> bool unshift (U &&object) {
return insert (0, &object);
}
bool remove (const T &object) {
return erase (index (object), 1);
}
template <typename U> bool push (U &&object) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <U> (object));
m_length++;
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <Args> (args)...);
m_length++;
return true;
}
T pop () {
auto object = cr::move (m_data[m_length - 1]);
discard ();
return object;
}
void discard () {
erase (m_length - 1, 1);
}
size_t index (const T &object) const {
return &object - &m_data[0];
}
void shuffle () {
for (size_t i = m_length; i >= 1; --i) {
cr::swap (m_data[i - 1], m_data[rg.int_ (i, m_length - 2)]);
}
}
void reverse () {
for (size_t i = 0; i < m_length / 2; ++i) {
cr::swap (m_data[i], m_data[m_length - 1 - i]);
}
}
template <typename U> bool extend (U &&rhs) {
if (m_length == 0) {
*this = cr::move (rhs);
}
else {
for (size_t i = 0; i < rhs.length (); ++i) {
if (!push (cr::move (rhs[i]))) {
return false;
}
}
}
return true;
}
template <typename U> bool assign (U &&rhs) {
clear ();
return extend (cr::move (rhs));
}
void clear () {
destructElements ();
m_length = 0;
}
bool empty () const {
return m_length == 0;
}
bool shrink () {
if (m_length == m_capacity || !m_length) {
return false;
}
auto data = alloc.allocate <T> (m_length);
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
m_data = data;
m_capacity = m_length;
return true;
}
const T &at (size_t index) const {
return m_data[index];
}
T &at (size_t index) {
return m_data[index];
}
const T &first () const {
return m_data[0];
}
T &first () {
return m_data[0];
}
T &last () {
return m_data[m_length - 1];
}
const T &last () const {
return m_data[m_length - 1];
}
const T &random () const {
return m_data[rg.int_ <size_t> (0, m_length - 1)];
}
T &random () {
return m_data[rg.int_ <size_t> (0u, m_length - 1u)];
}
T *data () {
return m_data;
}
T *data () const {
return m_data;
}
public:
Array &operator = (Array &&rhs) noexcept {
if (this != &rhs) {
destroy ();
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
return *this;
}
public:
const T &operator [] (size_t index) const {
return at (index);
}
T &operator [] (size_t index) {
return at (index);
}
// for range-based loops
public:
T *begin () {
return m_data;
}
T *begin () const {
return m_data;
}
T *end () {
return m_data + m_length;
}
T *end () const {
return m_data + m_length;
}
};
CR_NAMESPACE_END

128
include/crlib/cr-basic.h Normal file
View file

@ -0,0 +1,128 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// our global namaespace
#define CR_NAMESPACE_BEGIN namespace cr {
#define CR_NAMESPACE_END }
// undef base max
#if defined (max)
# undef max
#endif
// undef base min
#if defined (min)
# undef min
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
CR_NAMESPACE_BEGIN
//
// some useful type definitions
//
namespace types {
using int8 = signed char;
using int16 = signed short;
using int32 = signed int;
using uint8 = unsigned char;
using uint16 = unsigned short;
using uint32 = unsigned int;
using uint64 = unsigned long long;
};
// make types available for our own use
using namespace cr::types;
//
// global helper stuff
//
template <typename T, size_t N> constexpr size_t bufsize (const T (&)[N]) {
return N - 1;
}
template <typename T> constexpr T abs (const T &a) {
return a > 0 ? a : -a;
}
template <typename T> constexpr T bit (const T &a) {
return static_cast <T> (1ULL << a);
}
template <typename T> constexpr T min (const T &a, const T &b) {
return a < b ? a : b;
}
template <typename T> constexpr T max (const T &a, const T &b) {
return a > b ? a : b;
}
template <typename T> constexpr T square (const T &value) {
return value * value;
}
template <typename T> constexpr T clamp (const T &x, const T &a, const T &b) {
return min (max (x, a), b);
}
template <typename T> const T &fix (const T &type) {
return type;
}
//
// base classes
//
// simple non-copying base class
class DenyCopying {
protected:
explicit DenyCopying () = default;
~DenyCopying () = default;
public:
DenyCopying (const DenyCopying &) = delete;
DenyCopying &operator = (const DenyCopying &) = delete;
};
// singleton for objects
template <typename T> struct Singleton : private DenyCopying {
public:
static inline T &get () {
static T ref_;
return ref_;
};
};
// simple scoped enum, instaed of enum class
#define CR_DECLARE_SCOPED_ENUM_TYPE(enumName, enumType, ...) \
CR_NAMESPACE_BEGIN \
namespace enums { \
struct _##enumName : protected DenyCopying { \
enum Type : enumType { \
__VA_ARGS__ \
}; \
}; \
}; \
CR_NAMESPACE_END \
using enumName = ::cr::enums::_##enumName::Type; \
// same as above, but with int32 type
#define CR_DECLARE_SCOPED_ENUM(enumName, ...) \
CR_DECLARE_SCOPED_ENUM_TYPE(enumName, int32, __VA_ARGS__); \
CR_NAMESPACE_END
// platform-dependant-stuff
#include <crlib/cr-platform.h>

141
include/crlib/cr-binheap.h Normal file
View file

@ -0,0 +1,141 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// simple priority queue
template <typename T> class BinaryHeap final : public DenyCopying {
private:
Array <T> m_data;
public:
explicit BinaryHeap () = default;
BinaryHeap (BinaryHeap &&rhs) noexcept : m_data (cr::move (rhs.m_data))
{ }
~BinaryHeap () = default;
public:
template <typename U> bool push (U &&item) {
if (!m_data.push (cr::move (item))) {
return false;
}
size_t length = m_data.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!m_data.emplace (cr::forward <Args> (args)...)) {
return false;
}
size_t length = m_data.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
const T &top () const {
return m_data[0];
}
T pop () {
if (m_data.length () == 1) {
return m_data.pop ();
}
auto key (cr::move (m_data[0]));
m_data[0] = cr::move (m_data.last ());
m_data.discard ();
percolateDown (0);
return key;
}
public:
size_t length () const {
return m_data.length ();
}
bool empty () const {
return !m_data.length ();
}
void clear () {
m_data.clear ();
}
private:
void percolateUp (size_t index) {
while (index != 0) {
size_t parent = this->parent (index);
if (m_data[parent] <= m_data[index]) {
break;
}
cr::swap (m_data[index], m_data[parent]);
index = parent;
}
}
void percolateDown (size_t index) {
while (this->left (index) < m_data.length ()) {
size_t best = this->left (index);
if (this->right (index) < m_data.length ()) {
size_t right_index = this->right (index);
if (m_data[right_index] < m_data[best]) {
best = right_index;
}
}
if (m_data[index] <= m_data[best]) {
break;
}
cr::swap (m_data[index], m_data[best]);
index = best;
best = this->left (index);
}
}
private:
BinaryHeap &operator = (BinaryHeap &&rhs) noexcept {
if (this != &rhs) {
m_data = cr::move (rhs.m_data);
}
return *this;
}
private:
static constexpr size_t left (size_t index) {
return index << 1 | 1;
}
static constexpr size_t right (size_t index) {
return ++index << 1;
}
static constexpr size_t parent (size_t index) {
return --index >> 1;
}
};
CR_NAMESPACE_END

41
include/crlib/cr-color.h Normal file
View file

@ -0,0 +1,41 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// simple color holder
class Color final {
public:
int32 red = 0, green = 0, blue = 0;
public:
explicit Color (int32 r, int32 g, int32 b) : red (r), green (g), blue (b) { }
Color () = default;
~Color () = default;
public:
void reset () {
red = green = blue = 0;
}
int32 avg () const {
return sum () / (sizeof (Color) / sizeof (int32));
}
int32 sum () const {
return red + green + blue;
}
};
CR_NAMESPACE_END

View file

@ -0,0 +1,40 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-platform.h>
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-array.h>
#include <crlib/cr-binheap.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
#include <crlib/cr-http.h>
#include <crlib/cr-library.h>
#include <crlib/cr-dict.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-math.h>
#include <crlib/cr-vector.h>
#include <crlib/cr-random.h>
#include <crlib/cr-ulz.h>
#include <crlib/cr-color.h>
CR_NAMESPACE_BEGIN
namespace types {
using StringArray = Array <String>;
using IntArray = Array <int>;
};
using namespace cr::types;
CR_NAMESPACE_END

237
include/crlib/cr-dict.h Normal file
View file

@ -0,0 +1,237 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-array.h>
#include <crlib/cr-string.h>
#include <crlib/cr-twin.h>
CR_NAMESPACE_BEGIN
// template for hashing our string
template <typename K> struct StringHash {
uint32 operator () (const K &key) const {
char *str = const_cast <char *> (key.chars ());
uint32 hash = 0;
while (*str++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
};
// template for hashing integers
template <typename K> struct IntHash {
uint32 operator () (K key) const {
key = ((key >> 16) ^ key) * 0x119de1f3;
key = ((key >> 16) ^ key) * 0x119de1f3;
key = (key >> 16) ^ key;
return key;
}
};
namespace detail {
struct DictionaryList {
uint32 index;
DictionaryList *next;
};
template <typename K, typename V> struct DictionaryBucket {
uint32 hash = static_cast <size_t> (-1);
K key = K ();
V value = V ();
public:
DictionaryBucket () = default;
~DictionaryBucket () = default;
};
}
// basic dictionary
template <class K, class V, class H = StringHash <K>, size_t HashSize = 36> class Dictionary final : public DenyCopying {
public:
static constexpr size_t kInvalidIndex = static_cast <size_t> (-1);
private:
Array <detail::DictionaryList *> m_table;
Array <detail::DictionaryBucket <K, V>> m_buckets;
H m_hasher;
private:
uint32 hash (const K &key) const {
return m_hasher (key);
}
size_t findIndexOrAllocate (const K &key, bool allocate) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
for (auto bucket = m_table[pos]; bucket != nullptr; bucket = bucket->next) {
if (m_buckets[bucket->index].hash == hashed) {
return bucket->index;
}
}
if (allocate) {
size_t created = m_buckets.length ();
m_buckets.resize (created + 1);
auto allocated = alloc.allocate <detail::DictionaryList> ();
allocated->index = created;
allocated->next = m_table[pos];
m_table[pos] = allocated;
m_buckets[created].key = key;
m_buckets[created].hash = hashed;
return created;
}
return kInvalidIndex;
}
size_t findIndex (const K &key) const {
return const_cast <Dictionary *> (this)->findIndexOrAllocate (key, false);
}
public:
explicit Dictionary () {
reset ();
}
Dictionary (Dictionary &&rhs) noexcept : m_table (cr::move (rhs.m_table)), m_buckets (cr::move (rhs.m_buckets)), m_hasher (cr::move (rhs.m_hasher))
{ }
~Dictionary () {
clear ();
}
public:
bool exists (const K &key) const {
return findIndex (key) != kInvalidIndex;
}
bool empty () const {
return m_buckets.empty ();
}
size_t length () const {
return m_buckets.length ();
}
bool find (const K &key, V &value) const {
size_t index = findIndex (key);
if (index == kInvalidIndex) {
return false;
}
value = m_buckets[index].value;
return true;
}
template <typename U> bool push (const K &key, U &&value) {
operator [] (key) = cr::forward <U> (value);
return true;
}
bool remove (const K &key) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
auto *bucket = m_table[pos];
detail::DictionaryList *next = nullptr;
while (bucket != nullptr) {
if (m_buckets[bucket->index].hash == hashed) {
if (!next) {
m_table[pos] = bucket->next;
}
else {
next->next = bucket->next;
}
m_buckets.erase (bucket->index, 1);
alloc.deallocate (bucket);
bucket = nullptr;
return true;
}
next = bucket;
bucket = bucket->next;
}
return false;
}
void clear () {
for (auto object : m_table) {
while (object != nullptr) {
auto next = object->next;
alloc.deallocate (object);
object = next;
}
}
m_table.clear ();
m_buckets.clear ();
reset ();
}
void reset () {
m_table.resize (HashSize);
for (size_t i = 0; i < HashSize; ++i) {
m_table[i] = nullptr;
}
}
public:
V &operator [] (const K &key) {
return m_buckets[findIndexOrAllocate (key, true)].value;
}
const V &operator [] (const K &key) const {
return m_buckets[findIndex (key)].value;
}
Dictionary &operator = (Dictionary &&rhs) noexcept {
if (this != &rhs) {
m_table = cr::move (rhs.m_table);
m_buckets = cr::move (rhs.m_buckets);
m_hasher = cr::move (rhs.m_hasher);
}
return *this;
}
// for range-based loops
public:
detail::DictionaryBucket <K, V> *begin () {
return m_buckets.begin ();
}
detail::DictionaryBucket <K, V> *begin () const {
return m_buckets.begin ();
}
detail::DictionaryBucket <K, V> *end () {
return m_buckets.end ();
}
detail::DictionaryBucket <K, V> *end () const {
return m_buckets.end ();
}
};
CR_NAMESPACE_END

334
include/crlib/cr-files.h Normal file
View file

@ -0,0 +1,334 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-lambda.h>
CR_NAMESPACE_BEGIN
// simple stdio file wrapper
class File final : private DenyCopying {
private:
FILE *m_handle = nullptr;
size_t m_length = 0;
public:
explicit File () = default;
File (const String &file, const String &mode = "rt") {
open (file, mode);
}
~File () {
close ();
}
public:
bool open (const String &file, const String &mode) {
if ((m_handle = fopen (file.chars (), mode.chars ())) == nullptr) {
return false;
}
fseek (m_handle, 0L, SEEK_END);
m_length = ftell (m_handle);
fseek (m_handle, 0L, SEEK_SET);
return true;
}
void close () {
if (m_handle != nullptr) {
fclose (m_handle);
m_handle = nullptr;
}
m_length = 0;
}
bool eof () const {
return !!feof (m_handle);
}
bool flush () const {
return !!fflush (m_handle);
}
int getChar () const {
return fgetc (m_handle);
}
char *getString (char *buffer, int count) const {
return fgets (buffer, count, m_handle);
}
bool getLine (String &line) {
line.clear ();
int ch;
while ((ch = getChar ()) != EOF && !eof ()) {
line += static_cast <char> (ch);
if (ch == '\n') {
break;
}
}
return !eof ();
}
template <typename ...Args> size_t puts (const char *fmt, Args ...args) {
if (!*this) {
return false;
}
return fprintf (m_handle, fmt, cr::forward <Args> (args)...);
}
bool puts (const String &buffer) {
if (!*this) {
return false;
}
if (fputs (buffer.chars (), m_handle) < 0) {
return false;
}
return true;
}
int putChar (int ch) {
return fputc (ch, m_handle);
}
size_t read (void *buffer, size_t size, size_t count = 1) {
return fread (buffer, size, count, m_handle);
}
size_t write (void *buffer, size_t size, size_t count = 1) {
return fwrite (buffer, size, count, m_handle);
}
bool seek (long offset, int origin) {
if (fseek (m_handle, offset, origin) != 0) {
return false;
}
return true;
}
void rewind () {
::rewind (m_handle);
}
size_t length () const {
return m_length;
}
public:
explicit operator bool () const {
return m_handle != nullptr;
}
public:
static inline bool exists (const String &file) {
File fp;
if (fp.open (file, "rb")) {
fp.close ();
return true;
}
return false;
}
static inline void createPath (const char *path) {
for (auto str = const_cast <char *> (path) + 1; *str; ++str) {
if (*str == '/') {
*str = 0;
plat.createDirectory (path);
*str = '/';
}
}
plat.createDirectory (path);
}
};
// wrapper for memory file for loading data into the memory
class MemFileStorage : public Singleton <MemFileStorage> {
private:
using LoadFunction = Lambda <uint8 * (const char *, int *)>;
using FreeFunction = Lambda <void (void *)>;
private:
LoadFunction m_load = nullptr;
FreeFunction m_free = nullptr;
public:
inline MemFileStorage () = default;
inline ~MemFileStorage () = default;
public:
void initizalize (LoadFunction loader, FreeFunction unloader) {
m_load = cr::move (loader);
m_free = cr::move (unloader);
}
public:
uint8 *load (const String &file, int *size) {
if (m_load) {
return m_load (file.chars (), size);
}
return nullptr;
}
void unload (void *buffer) {
if (m_free) {
m_free (buffer);
}
}
};
class MemFile final : public DenyCopying {
private:
static constexpr char kEOF = static_cast <char> (-1);
private:
uint8 *m_data = nullptr;
size_t m_length = 0, m_pos = 0;
public:
explicit MemFile () = default;
MemFile (const String &file) {
open (file);
}
~MemFile () {
close ();
}
public:
bool open (const String &file) {
m_length = 0;
m_pos = 0;
m_data = MemFileStorage::get ().load (file.chars (), reinterpret_cast <int *> (&m_length));
if (!m_data) {
return false;
}
return true;
}
void close () {
MemFileStorage::get ().unload (m_data);
m_length = 0;
m_pos = 0;
m_data = nullptr;
}
char getChar () {
if (!m_data || m_pos >= m_length) {
return kEOF;
}
auto ch = m_data[m_pos];
m_pos++;
return static_cast <char> (ch);
}
char *getString (char *buffer, size_t count) {
if (!m_data || m_pos >= m_length) {
return nullptr;
}
size_t index = 0;
buffer[0] = 0;
for (; index < count - 1;) {
if (m_pos < m_length) {
buffer[index] = m_data[m_pos++];
if (buffer[index++] == '\n') {
break;
}
}
else {
break;
}
}
buffer[index] = 0;
return index ? buffer : nullptr;
}
bool getLine (String &line) {
line.clear ();
char ch;
while ((ch = getChar ()) != kEOF) {
line += ch;
if (ch == '\n') {
break;
}
}
return !eof ();
}
size_t read (void *buffer, size_t size, size_t count = 1) {
if (!m_data || m_length <= m_pos || !buffer || !size || !count) {
return 0;
}
size_t blocks_read = size * count <= m_length - m_pos ? size * count : m_length - m_pos;
memcpy (buffer, &m_data[m_pos], blocks_read);
m_pos += blocks_read;
return blocks_read / size;
}
bool seek (size_t offset, int origin) {
if (!m_data || m_pos >= m_length) {
return false;
}
if (origin == SEEK_SET) {
if (offset >= m_length) {
return false;
}
m_pos = offset;
}
else if (origin == SEEK_END) {
if (offset >= m_length) {
return false;
}
m_pos = m_length - offset;
}
else {
if (m_pos + offset >= m_length) {
return false;
}
m_pos += offset;
}
return true;
}
size_t length () const {
return m_length;
}
bool eof () const {
return m_pos >= m_length;
}
void rewind () {
m_pos = 0;
}
public:
explicit operator bool () const {
return !!m_data && m_length > 0;
}
};
CR_NAMESPACE_END

425
include/crlib/cr-http.h Normal file
View file

@ -0,0 +1,425 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-files.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-platform.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <netinet/in.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <errno.h>
# include <netdb.h>
# include <fcntl.h>
#elif defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h>
#endif
// status codes for http client
CR_DECLARE_SCOPED_ENUM (HttpClientResult,
OK = 0,
NotFound,
Forbidden,
SocketError,
ConnectError,
HttpOnly,
Undefined,
NoLocalFile = -1,
LocalFileExists = -2
);
CR_NAMESPACE_BEGIN
class Socket final : public DenyCopying {
private:
int32 m_socket;
uint32 m_timeout;
public:
Socket () : m_socket (-1), m_timeout (2) {
#if defined(CR_WINDOWS)
WSADATA wsa;
if (WSAStartup (MAKEWORD (1, 1), &wsa) != 0) {
logger.error ("Unable to inialize sockets.");
}
#endif
}
~Socket () {
disconnect ();
#if defined (CR_WINDOWS)
WSACleanup ();
#endif
}
public:
bool connect (const String &hostname) {
auto host = gethostbyname (hostname.chars ());
if (!host) {
return false;
}
m_socket = static_cast <int> (socket (AF_INET, SOCK_STREAM, 0));
if (m_socket < 0) {
return false;
}
auto getTimeouts = [&] () -> Twin <char *, size_t> {
#if defined (CR_WINDOWS)
DWORD tv = m_timeout * 1000;
#else
timeval tv { static_cast <time_t> (m_timeout), 0 };
#endif
return { reinterpret_cast <char *> (&tv), sizeof (tv) };
};
auto timeouts = getTimeouts ();
setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, timeouts.first, timeouts.second);
setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, timeouts.first, timeouts.second);
sockaddr_in dest;
memset (&dest, 0, sizeof (dest));
dest.sin_family = AF_INET;
dest.sin_port = htons (80);
dest.sin_addr.s_addr = inet_addr (inet_ntoa (*(reinterpret_cast <in_addr *> (host->h_addr))));
if (::connect (m_socket, reinterpret_cast <sockaddr *> (&dest), static_cast <int> (sizeof (dest))) == -1) {
disconnect ();
return false;
}
return true;
}
void setTimeout (uint32 timeout) {
m_timeout = timeout;
}
void disconnect () {
#if defined(CR_WINDOWS)
if (m_socket != -1) {
closesocket (m_socket);
}
#else
if (m_socket != -1)
close (m_socket);
#endif
}
public:
template <typename U> int32 send (const U *buffer, int32 length) const {
return ::send (m_socket, reinterpret_cast <const char *> (buffer), length, 0);
}
template <typename U> int32 recv (U *buffer, int32 length) {
return ::recv (m_socket, reinterpret_cast <char *> (buffer), length, 0);
}
};
namespace detail {
// simple http uri omitting query-string and port
struct HttpUri {
String path, protocol, host;
public:
static HttpUri parse (const String &uri) {
HttpUri result;
if (uri.empty ()) {
return result;
}
size_t protocol = uri.find ("://");
if (protocol != String::kInvalidIndex) {
result.protocol = uri.substr (0, protocol);
size_t host = uri.find ("/", protocol + 3);
if (host != String::kInvalidIndex) {
result.path = uri.substr (host + 1);
result.host = uri.substr (protocol + 3, host - protocol - 3);
return result;
}
}
return result;
}
};
};
// simple http client for downloading/uploading files only
class HttpClient final : public Singleton <HttpClient> {
private:
static constexpr int32 kMaxRecvErrors = 12;
private:
Socket m_socket;
String m_userAgent = "crlib";
HttpClientResult m_code = HttpClientResult::Undefined;
int32 m_chunkSize = 4096;
public:
HttpClient () = default;
~HttpClient () = default;
private:
HttpClientResult parseResponseHeader (uint8 *buffer) {
bool isFinished = false;
int32 pos = 0, symbols = 0, errors = 0;
// prase response header
while (!isFinished && pos < m_chunkSize) {
if (m_socket.recv (&buffer[pos], 1) < 1) {
if (++errors > kMaxRecvErrors) {
isFinished = true;
}
else {
continue;
}
}
switch (buffer[pos]) {
case '\r':
break;
case '\n':
isFinished = (symbols == 0);
symbols = 0;
break;
default:
symbols++;
break;
}
pos++;
}
String response (reinterpret_cast <const char *> (buffer));
size_t responseCodeStart = response.find ("HTTP/1.1");
if (responseCodeStart != String::kInvalidIndex) {
String respCode = response.substr (responseCodeStart + 9, 3).trim ();
if (respCode == "200") {
return HttpClientResult::OK;
}
else if (respCode == "403") {
return HttpClientResult::Forbidden;
}
else if (respCode == "404") {
return HttpClientResult::NotFound;
}
}
return HttpClientResult::NotFound;
}
public:
// simple blocked download
bool downloadFile (const String &url, const String &localPath) {
if (File::exists (localPath.chars ())) {
m_code = HttpClientResult::LocalFileExists;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
m_code = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!m_socket.connect (uri.host)) {
m_code = HttpClientResult::ConnectError;
m_socket.disconnect ();
return false;
}
String request;
request.appendf ("GET /%s HTTP/1.1\r\n", uri.path.chars ());
request.append ("Accept: */*\r\n");
request.append ("Connection: close\r\n");
request.append ("Keep-Alive: 115\r\n");
request.appendf ("User-Agent: %s\r\n", m_userAgent.chars ());
request.appendf ("Host: %s\r\n\r\n", uri.host.chars ());
if (m_socket.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
Array <uint8, ReservePolicy::PlusOne> buffer (m_chunkSize);
m_code = parseResponseHeader (buffer.data ());
if (m_code != HttpClientResult::OK) {
m_socket.disconnect ();
return false;
}
// receive the file
File file (localPath, "wb");
if (!file) {
m_code = HttpClientResult::Undefined;
m_socket.disconnect ();
return false;
}
int32 length = 0;
int32 errors = 0;
for (;;) {
length = m_socket.recv (buffer.data (), m_chunkSize);
if (length > 0) {
file.write (buffer.data (), length);
}
else if (++errors > 12) {
break;
}
}
file.close ();
m_socket.disconnect ();
m_code = HttpClientResult::OK;
return true;
}
bool uploadFile (const String &url, const String &localPath) {
if (!File::exists (localPath.chars ())) {
m_code = HttpClientResult::NoLocalFile;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
m_code = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!m_socket.connect (uri.host)) {
m_code = HttpClientResult::ConnectError;
m_socket.disconnect ();
return false;
}
// receive the file
File file (localPath, "rb");
if (!file) {
m_code = HttpClientResult::Undefined;
m_socket.disconnect ();
return false;
}
String boundaryName = localPath;
size_t boundarySlash = localPath.findLastOf ("\\/");
if (boundarySlash != String::kInvalidIndex) {
boundaryName = localPath.substr (boundarySlash + 1);
}
const String &kBoundary = "---crlib_upload_boundary_1337";
String request, start, end;
start.appendf ("--%s\r\n", kBoundary.chars ());
start.appendf ("Content-Disposition: form-data; name='file'; filename='%s'\r\n", boundaryName.chars ());
start.append ("Content-Type: application/octet-stream\r\n\r\n");
end.appendf ("\r\n--%s--\r\n\r\n", kBoundary.chars ());
request.appendf ("POST /%s HTTP/1.1\r\n", uri.path.chars ());
request.appendf ("Host: %s\r\n", uri.host.chars ());
request.appendf ("User-Agent: %s\r\n", m_userAgent.chars ());
request.appendf ("Content-Type: multipart/form-data; boundary=%s\r\n", kBoundary.chars ());
request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ());
// send the main request
if (m_socket.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
// send boundary start
if (m_socket.send (start.chars (), static_cast <int32> (start.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
Array <uint8, ReservePolicy::PlusOne> buffer (m_chunkSize);
int32 length = 0;
for (;;) {
length = static_cast <int32> (file.read (buffer.data (), 1, m_chunkSize));
if (length > 0) {
m_socket.send (buffer.data (), length);
}
else {
break;
}
}
// send boundary end
if (m_socket.send (end.chars (), static_cast <int32> (end.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
m_code = parseResponseHeader (buffer.data ());
m_socket.disconnect ();
return m_code == HttpClientResult::OK;
}
public:
void setUserAgent (const String &ua) {
m_userAgent = ua;
}
HttpClientResult getLastStatusCode () {
return m_code;
}
void setChunkSize (int32 chunkSize) {
m_chunkSize = chunkSize;
}
void setTimeout (uint32 timeout) {
m_socket.setTimeout (timeout);
}
};
// expose global http client
static auto &http = HttpClient::get ();
CR_NAMESPACE_END

173
include/crlib/cr-lambda.h Normal file
View file

@ -0,0 +1,173 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-alloc.h>
#include <crlib/cr-uniqueptr.h>
CR_NAMESPACE_BEGIN
static constexpr uint32 kLambdaSmallBufferSize = sizeof (void *) * 16;
template <typename> class Lambda;
template <typename R, typename ...Args> class Lambda <R (Args...)> {
class LambdaFunctorWrapper {
public:
LambdaFunctorWrapper () = default;
virtual ~LambdaFunctorWrapper () = default;
public:
virtual void move (uint8 *to) = 0;
virtual void small (uint8 *to) const = 0;
virtual R invoke (Args &&...) = 0;
virtual UniquePtr <LambdaFunctorWrapper> clone () const = 0;
public:
void operator delete (void *ptr) {
alloc.deallocate (ptr);
}
};
template <typename T> class LambdaFunctor : public LambdaFunctorWrapper {
private:
T m_callable;
public:
LambdaFunctor (const T &callable) : LambdaFunctorWrapper (), m_callable (callable)
{ }
LambdaFunctor (T &&callable) : LambdaFunctorWrapper (), m_callable (cr::move (callable))
{ }
~LambdaFunctor () override = default;
public:
void move (uint8 *to) override {
new (to) LambdaFunctor<T> (cr::move (m_callable));
}
void small (uint8 *to) const override {
new (to) LambdaFunctor<T> (m_callable);
}
R invoke (Args &&... args) override {
return m_callable (cr::forward <Args> (args)...);
}
UniquePtr <LambdaFunctorWrapper> clone () const override {
return createUniqueBase <LambdaFunctor <T>, LambdaFunctorWrapper> (m_callable);
}
};
union {
UniquePtr <LambdaFunctorWrapper> m_functor;
uint8 m_small[kLambdaSmallBufferSize];
};
bool m_smallObject;
private:
void destroy () {
if (m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (m_small)->~LambdaFunctorWrapper ();
}
else {
m_functor.reset ();
}
}
void swap (Lambda &rhs) noexcept {
cr::swap (rhs, *this);
}
public:
Lambda () noexcept : Lambda (nullptr)
{ }
Lambda (decltype (nullptr)) noexcept : m_functor (nullptr), m_smallObject (false)
{ }
Lambda (const Lambda &rhs) {
if (rhs.m_smallObject) {
reinterpret_cast <const LambdaFunctorWrapper *> (rhs.m_small)->small (m_small);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (rhs.m_functor->clone ());
}
m_smallObject = rhs.m_smallObject;
}
Lambda (Lambda &&rhs) noexcept {
if (rhs.m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.m_small)->move (m_small);
new (rhs.m_small) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.m_functor));
}
m_smallObject = rhs.m_smallObject;
rhs.m_smallObject = false;
}
template <typename F> Lambda (F function) {
if (cr::fix (sizeof (function) > kLambdaSmallBufferSize)) {
m_smallObject = false;
new (m_small) UniquePtr <LambdaFunctorWrapper> (createUniqueBase <LambdaFunctor <F>, LambdaFunctorWrapper> (cr::move (function)));
}
else {
m_smallObject = true;
new (m_small) LambdaFunctor<F> (cr::move (function));
}
}
~Lambda () {
destroy ();
}
public:
Lambda &operator = (const Lambda &rhs) {
destroy ();
Lambda tmp (rhs);
swap (tmp);
return *this;
}
Lambda &operator = (Lambda &&rhs) noexcept {
destroy ();
if (rhs.m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.m_small)->move (m_small);
new (rhs.m_small) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.m_functor));
}
m_smallObject = rhs.m_smallObject;
rhs.m_smallObject = false;
return *this;
}
explicit operator bool () const noexcept {
return m_smallObject || !!m_functor;
}
public:
R operator () (Args ...args) {
return m_smallObject ? reinterpret_cast <LambdaFunctorWrapper *> (m_small)->invoke (cr::forward <Args> (args)...) : m_functor->invoke (cr::forward <Args> (args)...);
}
};
CR_NAMESPACE_END

View file

@ -0,0 +1,89 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-string.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <dlfcn.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <unistd.h>
#elif defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
CR_NAMESPACE_BEGIN
// handling dynamic library loading
class SharedLibrary final : public DenyCopying {
private:
void *m_handle = nullptr;
public:
explicit SharedLibrary () = default;
SharedLibrary (const String &file) {
if (file.empty ()) {
return;
}
load (file);
}
~SharedLibrary () {
unload ();
}
public:
inline bool load (const String &file) noexcept {
#ifdef CR_WINDOWS
m_handle = LoadLibraryA (file.chars ());
#else
m_handle = dlopen (file.chars (), RTLD_NOW);
#endif
return m_handle != nullptr;
}
void unload () noexcept {
if (!*this) {
return;
}
#ifdef CR_WINDOWS
FreeLibrary (static_cast <HMODULE> (m_handle));
#else
dlclose (m_handle);
#endif
}
template <typename R> R resolve (const char *function) const {
if (!*this) {
return nullptr;
}
return reinterpret_cast <R> (
#ifdef CR_WINDOWS
GetProcAddress (static_cast <HMODULE> (m_handle), function)
#else
dlsym (m_handle, function)
#endif
);
}
public:
explicit operator bool () const {
return m_handle != nullptr;
}
};
CR_NAMESPACE_END

98
include/crlib/cr-logger.h Normal file
View file

@ -0,0 +1,98 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <time.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
#if defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
CR_NAMESPACE_BEGIN
class SimpleLogger final : public Singleton <SimpleLogger> {
public:
using PrintFunction = Lambda <void (const char *)>;
private:
File m_handle;
PrintFunction m_printer;
public:
SimpleLogger () = default;
~SimpleLogger () {
m_handle.close ();
}
private:
void logToFile (const char *level, const char *msg) {
if (!m_handle) {
return;
}
time_t ticks = time (&ticks);
auto tm = localtime (&ticks);
auto timebuf = strings.chars ();
strftime (timebuf, StringBuffer::StaticBufferSize, "%Y-%m-%d %H:%M:%S", tm);
m_handle.puts ("%s (%s): %s\n", timebuf, level, msg);
}
public:
template <typename ...Args> void fatal (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("FATAL", msg);
if (m_printer) {
m_printer (msg);
}
plat.abort (msg);
}
template <typename ...Args> void error (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("ERROR", msg);
if (m_printer) {
m_printer (msg);
}
}
template <typename ...Args> void message (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("INFO", msg);
if (m_printer) {
m_printer (msg);
}
}
public:
void initialize (const String &filename, PrintFunction printFunction) {
if (m_handle) {
m_handle.close ();
}
m_printer = cr::move (printFunction);
m_handle.open (filename, "at");
}
};
// expose global instance
static auto &logger = SimpleLogger::get ();
CR_NAMESPACE_END

179
include/crlib/cr-math.h Normal file
View file

@ -0,0 +1,179 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#if defined (CR_HAS_SSE2)
# include <immintrin.h>
#endif
CR_NAMESPACE_BEGIN
constexpr float kFloatEpsilon = 0.01f;
constexpr float kFloatEqualEpsilon = 0.001f;
constexpr float kFloatCmpEpsilon = 1.192092896e-07f;
constexpr float kMathPi = 3.141592653589793115997963468544185161590576171875f;
constexpr float kMathPiReciprocal = 1.0f / kMathPi;
constexpr float kMathPiHalf = kMathPi / 2;
constexpr float kDegreeToRadians = kMathPi / 180.0f;
constexpr float kRadiansToDegree = 180.0f / kMathPi;
constexpr bool fzero (const float e) {
return cr::abs (e) < kFloatEpsilon;
}
constexpr bool fequal (const float a, const float b) {
return cr:: abs (a - b) < kFloatEqualEpsilon;
}
constexpr float radiansToDegrees (const float r) {
return r * kRadiansToDegree;
}
constexpr float degreesToRadians (const float d) {
return d * kDegreeToRadians;
}
constexpr float modAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> (a * (65536.0f / 360.0f)) & 65535);
}
constexpr float normalizeAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> ((a + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f;
}
constexpr float anglesDifference (const float a, const float b) {
return normalizeAngles (a - b);
}
inline float sinf (const float value) {
const auto sign = static_cast <int32> (value * kMathPiReciprocal);
const float calc = (value - static_cast <float> (sign) * kMathPi);
const float sqr = cr::square (calc);
const float res = 1.00000000000000000000e+00f + sqr * (-1.66666671633720397949e-01f + sqr * (8.33333376795053482056e-03f + sqr * (-1.98412497411482036114e-04f +
sqr * (2.75565571428160183132e-06f + sqr * (-2.50368472620721149724e-08f + sqr * (1.58849267073435385100e-10f + sqr * -6.58925550841432672300e-13f))))));
return (sign & 1) ? -calc * res : value * res;
}
inline float cosf (const float value) {
const auto sign = static_cast <int32> (value * kMathPiReciprocal);
const float calc = (value - static_cast <float> (sign) * kMathPi);
const float sqr = cr::square (calc);
const float res = sqr * (-5.00000000000000000000e-01f + sqr * (4.16666641831398010254e-02f + sqr * (-1.38888671062886714935e-03f + sqr * (2.48006890615215525031e-05f +
sqr * (-2.75369927749125054106e-07f + sqr * (2.06207229069832465029e-09f + sqr * -9.77507137733812925262e-12f))))));
const float f = -1.00000000000000000000e+00f;
return (sign & 1) ? f - res : -f + res;
}
inline float atanf (const float x) {
const float sqr = cr::square (x);
return x * (48.70107004404898384f + sqr * (49.5326263772254345f + sqr * 9.40604244231624f)) / (48.70107004404996166f + sqr * (65.7663163908956299f + sqr * (21.587934067020262f + sqr)));
};
inline float atan2f (const float y, const float x) {
const float ax = cr::abs (x);
const float ay = cr::abs (y);
if (ax < 1e-7f && ay < 1e-7f) {
return 0.0f;
}
if (ax > ay) {
if (x < 0.0f) {
if (y >= 0.0f) {
return atanf (y / x) + kMathPi;
}
return atanf (y / x) - kMathPi;
}
return atanf (y / x);
}
if (y < 0.0f) {
return atanf (-x / y) - kMathPiHalf;
}
return atanf (-x / y) + kMathPiHalf;
}
inline float powf (const float x, const float y) {
union {
float d;
int x;
} res { x };
res.x = static_cast <int> (y * (res.x - 1064866805) + 1064866805);
return res.d;
}
inline float sqrtf (const float value) {
return powf (value, 0.5f);
}
inline float tanf (const float value) {
return sinf (value) / cosf (value);
}
constexpr float ceilf (const float x) {
return static_cast <float> (65536 - static_cast <int> (65536.0f - x));
}
inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) {
#if defined (CR_HAS_SSE2)
auto set = _mm_set_ps (x, y, z, 0.0f);
auto _mm_sin = [] (__m128 rad) -> __m128 {
static auto pi2 = _mm_set_ps1 (kMathPi * 2);
static auto rp1 = _mm_set_ps1 (4.0f / kMathPi);
static auto rp2 = _mm_set_ps1 (-4.0f / (kMathPi * kMathPi));
static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (kMathPi));
static auto csi = _mm_castsi128_ps (_mm_set1_epi32 (0x80000000));
val = _mm_and_ps (val, pi2);
rad = _mm_sub_ps (rad, val);
val = _mm_cmpngt_ps (rad, _mm_set_ps1 (-kMathPi));
val = _mm_and_ps (val, pi2);
rad = _mm_add_ps (rad, val);
val = _mm_mul_ps (_mm_andnot_ps (csi, rad), rp2);
val = _mm_add_ps (val, rp1);
auto si = _mm_mul_ps (val, rad);
val = _mm_mul_ps (_mm_andnot_ps (csi, si), si);
val = _mm_sub_ps (val, si);
val = _mm_mul_ps (val, _mm_set_ps1 (0.225f));
return _mm_add_ps (val, si);
};
static auto hpi = _mm_set_ps1 (kMathPiHalf);
auto s = _mm_sin (set);
auto c = _mm_sin (_mm_add_ps (set, hpi));
_mm_store_ps (sines, _mm_shuffle_ps (s, s, _MM_SHUFFLE (0, 1, 2, 3)));
_mm_store_ps (cosines, _mm_shuffle_ps (c, c, _MM_SHUFFLE (0, 1, 2, 3)));
#else
sines[0] = cr::sinf (x);
sines[1] = cr::sinf (y);
sines[2] = cr::sinf (z);
cosines[0] = cr::cosf (x);
cosines[1] = cr::cosf (y);
cosines[2] = cr::cosf (z);
#endif
}
CR_NAMESPACE_END

View file

@ -0,0 +1,58 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
namespace detail {
template <typename T> struct ClearRef {
using Type = T;
};
template <typename T> struct ClearRef <T &> {
using Type = T;
};
template <typename T> struct ClearRef <T &&> {
using Type = T;
};
};
template <typename T> typename detail::ClearRef <T>::Type constexpr &&move (T &&type) noexcept {
return static_cast <typename detail::ClearRef <T>::Type &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &&type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> inline void swap (T &left, T &right) noexcept {
auto temp = cr::move (left);
left = cr::move (right);
right = cr::move (temp);
}
template <typename T, size_t S> inline void swap (T (&left)[S], T (&right)[S]) noexcept {
if (&left == &right) {
return;
}
auto begin = left;
auto end = begin + S;
for (auto temp = right; begin != end; ++begin, ++temp) {
cr::swap (*begin, *temp);
}
}
CR_NAMESPACE_END

172
include/crlib/cr-platform.h Normal file
View file

@ -0,0 +1,172 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
CR_NAMESPACE_BEGIN
// detects the build platform
#if defined(__linux__)
# define CR_LINUX
#elif defined(__APPLE__)
# define CR_OSX
#elif defined(_WIN32)
# define CR_WINDOWS
#endif
#if defined(__ANDROID__)
# define CR_ANDROID
# if defined(LOAD_HARDFP)
# define CR_ANDROID_HARD_FP
# endif
#endif
// detects the compiler
#if defined(_MSC_VER)
# define CR_CXX_MSVC
#elif defined(__clang__)
# define CR_CXX_CLANG
#endif
// configure export macros
#if defined(CR_WINDOWS)
# define CR_EXPORT extern "C" __declspec (dllexport)
#elif defined(CR_LINUX) || defined(CR_OSX)
# define CR_EXPORT extern "C" __attribute__((visibility("default")))
#else
# error "Can't configure export macros. Compiler unrecognized."
#endif
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__amd64) || (defined(_MSC_VER) && defined(_M_X64))
# define CR_ARCH_X64
#elif defined(__i686) || defined(__i686__) || defined(__i386) || defined(__i386__) || defined(i386) || (defined(_MSC_VER) && defined(_M_IX86))
# define CR_ARCH_X86
#endif
#if (defined(CR_ARCH_X86) || defined(CR_ARCH_X64)) && !defined(CR_DEBUG)
# define CR_HAS_SSE2
#endif
CR_NAMESPACE_END
#if defined(CR_WINDOWS)
# include <direct.h>
# define stricmp _stricmp
#else
# include <unistd.h>
# include <sys/stat.h>
# define stricmp strcasecmp
#endif
#include <assert.h>
#if defined (CR_ANDROID)
# include <android/log.h>
#endif
CR_NAMESPACE_BEGIN
// helper struct for platform detection
struct Platform : public Singleton <Platform> {
bool isWindows = false;
bool isLinux = false;
bool isOSX = false;
bool isAndroid = false;
bool isAndroidHardFP = false;
bool isX64 = false;
Platform () {
#if defined(CR_WINDOWS)
isWindows = true;
#endif
#if defined(CR_ANDROID)
isAndroid = true;
# if defined (CR_ANDROID_HARD_FP)
isAndroidHardFP = true;
# endif
#endif
#if defined(CR_LINUX)
isLinux = true;
#endif
#if defined (CR_OSX)
isOSX = true;
#endif
#if defined (CR_ARCH_X64)
isX64 = true;
#endif
}
// helper platform-dependant functions
template <typename U> bool checkPointer (U *ptr) {
#if defined(CR_WINDOWS)
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr))) {
return false;
}
#else
(void) (ptr);
#endif
return true;
}
bool createDirectory (const char *dir) {
int result = 0;
#if defined(CR_WINDOWS)
result = _mkdir (dir);
#else
result = mkdir (dir, 0777);
#endif
return !!result;
}
bool removeDirectory (const char *dir) {
#if defined(CR_WINDOWS)
_unlink (dir);
#else
unlink (dir);
#endif
return true;
}
bool hasModule (const char *mod) {
#if defined(CR_WINDOWS)
return GetModuleHandleA (mod) != nullptr;
#else
(void) (mod);
return true;
#endif
}
void abort (const char *msg = "OUT OF MEMORY!") noexcept {
fprintf (stderr, "%s\n", msg);
#if defined (CR_ANDROID)
__android_log_write (ANDROID_LOG_ERROR, "crlib.fatal", msg);
#endif
#if defined(CR_WINDOWS)
if (msg) {
DestroyWindow (GetForegroundWindow ());
MessageBoxA (GetActiveWindow (), msg, "crlib.fatal", MB_ICONSTOP);
}
#endif
::abort ();
}
};
// expose platform singleton
static auto &plat = Platform::get ();
CR_NAMESPACE_END

66
include/crlib/cr-random.h Normal file
View file

@ -0,0 +1,66 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <time.h>
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// random number generator see: https://github.com/preshing/RandomSequence/
class Random final : public Singleton <Random> {
private:
uint32 m_index, m_offset;
uint64 m_divider;
public:
explicit Random () {
const auto base = static_cast <uint32> (time (nullptr));
const auto offset = base + 1;
m_index = premute (premute (base) + 0x682f0161);
m_offset = premute (premute (offset) + 0x46790905);
m_divider = (static_cast <uint64> (1)) << 32;
}
~Random () = default;
private:
uint32 premute (uint32 index) {
static constexpr auto prime = 4294967291u;
if (index >= prime) {
return index;
}
const uint32 residue = (static_cast <uint64> (index) * index) % prime;
return (index <= prime / 2) ? residue : prime - residue;
}
uint32 generate () {
return premute ((premute (m_index++) + m_offset) ^ 0x5bf03635);
}
public:
template <typename U> U int_ (U low, U high) {
return static_cast <U> (generate () * (static_cast <double> (high) - static_cast <double> (low) + 1.0) / m_divider + static_cast <double> (low));
}
float float_ (float low, float high) {
return static_cast <float> (generate () * (static_cast <double> (high) - static_cast <double> (low)) / (m_divider - 1) + static_cast <double> (low));
}
template <typename U> bool chance (const U max, const U maxChance = 100) {
return int_ <U> (0, maxChance) < max;
}
};
// expose global random generator
static auto &rg = Random::get ();
CR_NAMESPACE_END

870
include/crlib/cr-string.h Normal file
View file

@ -0,0 +1,870 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <string.h>
#include <ctype.h>
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-twin.h>
#include <crlib/cr-uniqueptr.h>
CR_NAMESPACE_BEGIN
// small-string optimized string class, sso stuff based on: https://github.com/elliotgoodrich/SSO-23/
class String final {
public:
static constexpr size_t kInvalidIndex = static_cast <size_t> (-1);
private:
static constexpr size_t kExcessSpace = 32;
private:
using Length = Twin <size_t, size_t>;
private:
union Data {
struct Big {
char excess[kExcessSpace - sizeof (char *) - 2 * sizeof (size_t)];
char *ptr;
size_t length;
size_t capacity;
} big;
struct Small {
char str[sizeof (Big) / sizeof (char) - 1];
uint8 length;
} small;
} m_data;
private:
static size_t const kSmallCapacity = sizeof (typename Data::Big) / sizeof (char) - 1;
public:
explicit String () {
reset ();
assign ("", 0);
}
String (const char *str, size_t length = 0) {
reset ();
assign (str, length);
}
String (const String &str) {
reset ();
assign (str.data (), str.length ());
}
String (const char ch) {
reset ();
assign (ch);
}
String (String &&rhs) noexcept {
m_data = rhs.m_data;
rhs.setMoved ();
}
~String () {
destroy ();
}
private:
template <typename T> static uint8 &getMostSignificantByte (T &object) {
return *(reinterpret_cast <uint8 *> (&object) + sizeof (object) - 1);
}
template <size_t N> static bool getLeastSignificantBit (uint8 byte) {
return byte & cr::bit (N);
}
template <size_t N> static bool getMostSignificantBit (uint8 byte) {
return byte & cr::bit (CHAR_BIT - N - 1);
}
template <size_t N> static void setLeastSignificantBit (uint8 &byte, bool bit) {
if (bit) {
byte |= cr::bit (N);
}
else {
byte &= ~cr::bit (N);
}
}
template <size_t N> static void setMostSignificantBit (uint8 &byte, bool bit) {
if (bit) {
byte |= cr::bit (CHAR_BIT - N - 1);
}
else {
byte &= ~cr::bit (CHAR_BIT - N - 1);
}
}
void destroy () {
if (!isSmall ()) {
alloc.deallocate (m_data.big.ptr);
}
}
void reset () {
m_data.small.length = 0;
m_data.small.str[0] = '\0';
m_data.big.ptr = nullptr;
m_data.big.length = 0;
}
void endString (const char *str, size_t at) {
const_cast <char *> (str)[at] = '\0';
}
void moveString (const char *dst, const char *src, size_t length) {
if (!dst) {
return;
}
memmove (const_cast <char *> (dst), src, length);
}
const char *data () const {
return isSmall () ? m_data.small.str : m_data.big.ptr;
}
void setMoved () {
setSmallLength (0);
}
void setLength (size_t amount, size_t capacity) {
if (amount <= kSmallCapacity) {
endString (m_data.small.str, amount);
setSmallLength (static_cast <uint8> (amount));
}
else {
endString (m_data.big.ptr, amount);
setDataNonSmall (amount, capacity);
}
}
bool isSmall () const {
return !getLeastSignificantBit <0> (m_data.small.length) && !getLeastSignificantBit <1> (m_data.small.length);
}
void setSmallLength (uint8 length) {
m_data.small.length = static_cast <char> (kSmallCapacity - length) << 2;
}
size_t getSmallLength () const {
return kSmallCapacity - ((m_data.small.length >> 2) & 63u);
}
void setDataNonSmall (size_t length, size_t capacity) {
uint8 &lengthHighByte = getMostSignificantByte (length);
uint8 &capacityHighByte = getMostSignificantByte (capacity);
const bool lengthHasHighBit = getMostSignificantBit <0> (lengthHighByte);
const bool capacityHasHighBit = getMostSignificantBit <0> (capacityHighByte);
const bool capacityHasSecHighBit = getMostSignificantBit <1> (capacityHighByte);
setMostSignificantBit <0> (lengthHighByte, capacityHasSecHighBit);
capacityHighByte <<= 2;
setLeastSignificantBit <0> (capacityHighByte, capacityHasHighBit);
setLeastSignificantBit <1> (capacityHighByte, !lengthHasHighBit);
m_data.big.length = length;
m_data.big.capacity = capacity;
}
Length getDataNonSmall () const {
size_t length = m_data.big.length;
size_t capacity = m_data.big.capacity;
uint8 &lengthHighByte = getMostSignificantByte (length);
uint8 &capacityHighByte = getMostSignificantByte (capacity);
const bool capacityHasHighBit = getLeastSignificantBit <0> (capacityHighByte);
const bool lengthHasHighBit = !getLeastSignificantBit <1> (capacityHighByte);
const bool capacityHasSecHighBit = getMostSignificantBit <0> (lengthHighByte);
setMostSignificantBit <0> (lengthHighByte, lengthHasHighBit);
capacityHighByte >>= 2;
setMostSignificantBit <0> (capacityHighByte, capacityHasHighBit);
setMostSignificantBit <1> (capacityHighByte, capacityHasSecHighBit);
return { length, capacity };
}
public:
String &assign (const char *str, size_t length = 0) {
length = length > 0 ? length : strlen (str);
if (length <= kSmallCapacity) {
moveString (m_data.small.str, str, length);
endString (m_data.small.str, length);
setSmallLength (static_cast <uint8> (length));
}
else {
auto capacity = cr::max (kSmallCapacity * 2, length);
m_data.big.ptr = alloc.allocate <char> (capacity + 1);
if (m_data.big.ptr) {
moveString (m_data.big.ptr, str, length);
endString (m_data.big.ptr, length);
setDataNonSmall (length, capacity);
}
}
return *this;
}
String &assign (const String &str, size_t length = 0) {
return assign (str.chars (), length);
}
String &assign (const char ch) {
const char str[] { ch, '\0' };
return assign (str, strlen (str));
}
String &append (const char *str, size_t length = 0) {
if (empty ()) {
return assign (str, length);
}
length = length > 0 ? length : strlen (str);
size_t oldLength = this->length ();
size_t newLength = oldLength + length;
resize (newLength);
moveString (&data ()[oldLength], str, length);
endString (data (), newLength);
return *this;
}
String &append (const String &str, size_t length = 0) {
return append (str.chars (), length);
}
String &append (const char ch) {
const char str[] { ch, '\0' };
return append (str, strlen (str));
}
template <typename ...Args> String &assignf (const char *fmt, Args ...args) {
const size_t size = snprintf (nullptr, 0, fmt, args...);
Array <char, ReservePolicy::PlusOne> buffer (size + 1);
snprintf (buffer.data (), size + 1, fmt, cr::forward <Args> (args)...);
return assign (buffer.data ());
}
template <typename ...Args> String &appendf (const char *fmt, Args ...args) {
if (empty ()) {
return assignf (fmt, cr::forward <Args> (args)...);
}
const size_t size = snprintf (nullptr, 0, fmt, args...) + length ();
Array <char, ReservePolicy::PlusOne> buffer (size + 1);
snprintf (buffer.data (), size + 1, fmt, cr::forward <Args> (args)...);
return append (buffer.data ());
}
void resize (size_t amount) {
size_t oldLength = length ();
if (amount <= kSmallCapacity) {
if (!isSmall ()) {
auto ptr = m_data.big.ptr;
moveString (m_data.small.str, ptr, cr::min (oldLength, amount));
alloc.deallocate (ptr);
}
setLength (amount, 0);
}
else {
size_t newCapacity = 0;
if (isSmall ()) {
newCapacity = cr::max (amount, kSmallCapacity * 2);
auto ptr = alloc.allocate <char> (newCapacity + 1);
moveString (ptr, m_data.small.str, cr::min (oldLength, amount));
m_data.big.ptr = ptr;
}
else if (amount < capacity ()) {
newCapacity = capacity ();
}
else {
newCapacity = cr::max (amount, capacity () * 3 / 2);
auto ptr = alloc.allocate <char> (newCapacity + 1);
moveString (ptr, m_data.big.ptr, cr::min (oldLength, amount));
alloc.deallocate (m_data.big.ptr);
m_data.big.ptr = ptr;
}
setLength (amount, newCapacity);
}
}
bool insert (size_t index, const String &str) {
if (str.empty ()) {
return false;
}
const size_t strLength = str.length ();
const size_t dataLength = length ();
if (index >= dataLength) {
append (str.chars (), strLength);
}
else {
resize (dataLength + strLength);
for (size_t i = dataLength; i > index; --i) {
at (i + strLength - 1) = at (i - 1);
}
for (size_t i = 0; i < strLength; ++i) {
at (i + index) = str.at (i);
}
}
return true;
}
bool erase (size_t index, size_t count = 1) {
const size_t dataLength = length ();
if (index + count > dataLength) {
return false;
}
const size_t newLength = dataLength - count;
for (size_t i = index; i < newLength; ++i) {
at (i) = at (i + count);
}
resize (newLength);
return true;
}
size_t find (char pattern, size_t start = 0) const {
for (size_t i = start; i < length (); ++i) {
if (at (i) == pattern) {
return i;
}
}
return kInvalidIndex;
}
size_t find (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
if (patternLength > dataLength || start > dataLength) {
return kInvalidIndex;
}
for (size_t i = start; i <= dataLength - patternLength; ++i) {
size_t index = 0;
for (; at (index) && index < patternLength; ++index) {
if (at (i + index) != pattern.at (index)) {
break;
}
}
if (!pattern.at (index)) {
return i;
}
}
return kInvalidIndex;
}
size_t rfind (char pattern) const {
for (size_t i = length (); i != 0; i--) {
if (at (i) == pattern) {
return i;
}
}
return kInvalidIndex;
}
size_t rfind (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
if (patternLength > dataLength) {
return kInvalidIndex;
}
bool match = true;
for (size_t i = dataLength - 1; i >= patternLength; i--) {
match = true;
for (size_t j = patternLength - 1; j > 0; j--) {
if (at (i + j) != pattern.at (j)) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return kInvalidIndex;
}
size_t findFirstOf (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
for (size_t i = start; i < dataLength; ++i) {
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
return i;
}
}
}
return kInvalidIndex;
}
size_t findLastOf (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
for (size_t i = dataLength - 1; i > 0; i--) {
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
return i;
}
}
}
return kInvalidIndex;
}
size_t findFirstNotOf (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
bool different = true;
for (size_t i = start; i < dataLength; ++i) {
different = true;
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
different = false;
break;
}
}
if (different) {
return i;
}
}
return kInvalidIndex;
}
size_t findLastNotOf (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
bool different = true;
for (size_t i = dataLength - 1; i > 0; i--) {
different = true;
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
different = false;
break;
}
}
if (different) {
return i;
}
}
return kInvalidIndex;
}
size_t countChar (char ch) const {
size_t count = 0;
for (size_t i = 0, e = length (); i != e; ++i) {
if (at (i) == ch) {
++count;
}
}
return count;
}
size_t countStr (const String &pattern) const {
const size_t patternLen = pattern.length ();
const size_t dataLength = length ();
if (patternLen > dataLength) {
return 0;
}
size_t count = 0;
for (size_t i = 0, e = dataLength - patternLen + 1; i != e; ++i) {
if (substr (i, patternLen).compare (pattern)) {
++count;
}
}
return count;
}
String substr (size_t start, size_t count = kInvalidIndex) const {
start = cr::min (start, length ());
if (count == kInvalidIndex) {
count = length ();
}
return String (data () + start, cr::min (count, length () - start));
}
size_t replace (const String &needle, const String &to) {
if (needle.empty () || to.empty ()) {
return 0;
}
size_t replaced = 0, pos = 0;
while (pos < length ()) {
pos = find (needle, pos);
if (pos == kInvalidIndex) {
break;
}
erase (pos, needle.length ());
insert (pos, to);
pos += to.length ();
replaced++;
}
return replaced;
}
bool startsWith (const String &prefix) const {
const size_t prefixLength = prefix.length ();
const size_t dataLength = length ();
return prefixLength <= dataLength && strncmp (data (), prefix.data (), prefixLength) == 0;
}
bool endsWith (const String &suffix) const {
const size_t suffixLength = suffix.length ();
const size_t dataLength = length ();
return suffixLength <= dataLength && strncmp (data () + dataLength - suffixLength, suffix.data (), suffixLength) == 0;
}
Array <String> split (const String &delim) const {
Array <String> tokens;
size_t prev = 0, pos = 0;
while ((pos = find (delim, pos)) != kInvalidIndex) {
tokens.push (substr (prev, pos - prev));
prev = ++pos;
}
tokens.push (substr (prev, pos - prev));
return tokens;
}
size_t length () const {
if (isSmall ()) {
return getSmallLength ();
}
else {
return getDataNonSmall ().first;
}
}
size_t capacity () const {
if (isSmall ()) {
return sizeof (m_data) - 1;
}
else {
return getDataNonSmall ().second;
}
}
bool small () const {
return isSmall ();
}
bool empty () const {
return length () == 0;
}
void clear () {
assign ("");
}
const char *chars () const {
return data ();
}
const char &at (size_t index) const {
return begin ()[index];
}
char &at (size_t index) {
return begin ()[index];
}
int32 compare (const String &rhs) const {
return strcmp (rhs.data (), data ());
}
int32 compare (const char *rhs) const {
return strcmp (rhs, data ());
}
bool contains (const String &rhs) const {
return find (rhs) != kInvalidIndex;
}
String &lowercase () {
for (auto &ch : *this) {
ch = static_cast <char> (::tolower (ch));
}
return *this;
}
String &uppercase () {
for (auto &ch : *this) {
ch = static_cast <char> (::toupper (ch));
}
return *this;
}
int32 int_ () const {
return atoi (data ());
}
float float_ () const {
return static_cast <float> (atof (data ()));
}
String &ltrim (const String &characters = "\r\n\t ") {
size_t begin = length ();
for (size_t i = 0; i < begin; ++i) {
if (characters.find (at (i)) == kInvalidIndex) {
begin = i;
break;
}
}
return *this = substr (begin, length () - begin);
}
String &rtrim (const String &characters = "\r\n\t ") {
size_t end = 0;
for (size_t i = length (); i > 0; --i) {
if (characters.find (at (i - 1)) == kInvalidIndex) {
end = i;
break;
}
}
return *this = substr (0, end);
}
String &trim (const String &characters = "\r\n\t ") {
return ltrim (characters).rtrim (characters);
}
public:
String &operator = (String &&rhs) noexcept {
destroy ();
m_data = rhs.m_data;
rhs.setMoved ();
return *this;
}
String &operator = (const String &rhs) {
return assign (rhs);
}
String &operator = (const char *rhs) {
return assign (rhs);
}
String &operator = (char rhs) {
return assign (rhs);
}
String &operator += (const String &rhs) {
return append (rhs);
}
String &operator += (const char *rhs) {
return append (rhs);
}
const char &operator [] (size_t index) const {
return at (index);
}
char &operator [] (size_t index) {
return at (index);
}
friend String operator + (const String &lhs, char rhs) {
return String (lhs).append (rhs);
}
friend String operator + (char lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const String &lhs, const char *rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const char *lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const String &lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend bool operator == (const String &lhs, const String &rhs) {
return lhs.compare (rhs) == 0;
}
friend bool operator < (const String &lhs, const String &rhs) {
return lhs.compare (rhs) < 0;
}
friend bool operator > (const String &lhs, const String &rhs) {
return lhs.compare (rhs) > 0;
}
friend bool operator == (const char *lhs, const String &rhs) {
return rhs.compare (lhs) == 0;
}
friend bool operator == (const String &lhs, const char *rhs) {
return lhs.compare (rhs) == 0;
}
friend bool operator != (const String &lhs, const String &rhs) {
return lhs.compare (rhs) != 0;
}
friend bool operator != (const char *lhs, const String &rhs) {
return rhs.compare (lhs) != 0;
}
friend bool operator != (const String &lhs, const char *rhs) {
return lhs.compare (rhs) != 0;
}
public:
static String join (const Array <String> &sequence, const String &delim, size_t start = 0) {
if (sequence.empty ()) {
return "";
}
if (sequence.length () == 1) {
return sequence.at (0);
}
String result;
for (size_t index = start; index < sequence.length (); ++index) {
if (index != start) {
result += delim + sequence[index];
}
else {
result += sequence[index];
}
}
return result;
}
// for range-based loops
public:
char *begin () {
return const_cast <char *> (data ());
}
char *begin () const {
return const_cast <char *> (data ());
}
char *end () {
return begin () + length ();
}
char *end () const {
return begin () + length ();
}
};
// simple rotation-string pool for holding temporary data passed to different modules and for formatting
class StringBuffer final : public Singleton <StringBuffer> {
public:
enum : size_t {
StaticBufferSize = static_cast <size_t> (768),
RotationCount = static_cast <size_t> (32)
};
private:
char m_data[RotationCount + 1][StaticBufferSize] {};
size_t m_rotate = 0;
public:
StringBuffer () = default;
~StringBuffer () = default;
public:
char *chars () {
if (++m_rotate >= RotationCount) {
m_rotate = 0;
}
return m_data[cr::clamp <size_t> (m_rotate, 0, RotationCount)];
}
template <typename U, typename ...Args> U *format (const U *fmt, Args ...args) {
auto buffer = Singleton <StringBuffer>::get ().chars ();
snprintf (buffer, StaticBufferSize, fmt, args...);
return buffer;
}
template <typename U> U *format (const U *fmt) {
auto buffer = Singleton <StringBuffer>::get ().chars ();
strncpy (buffer, fmt, StaticBufferSize);
return buffer;
}
};
// expose global string pool
static auto &strings = StringBuffer::get ();
CR_NAMESPACE_END

75
include/crlib/cr-twin.h Normal file
View file

@ -0,0 +1,75 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple pair (twin)
template <typename A, typename B> class Twin {
public:
A first;
B second;
public:
template <typename T, typename U> Twin (T &&a, U &&b) : first (cr::forward <T> (a)), second (cr::forward <U> (b)) { }
template <typename T, typename U> Twin (const Twin <T, U> &rhs) : first (rhs.first), second (rhs.second) { }
template <typename T, typename U> Twin (Twin <T, U> &&rhs) noexcept : first (cr::move (rhs.first)), second (cr::move (rhs.second)) { }
public:
explicit Twin () = default;
~Twin () = default;
public:
template <typename T, typename U> Twin &operator = (const Twin <T, U> &rhs) {
first = rhs.first;
second = rhs.second;
return *this;
}
template <typename T, typename U> Twin &operator = (Twin <T, U> &&rhs) {
first = cr::move (rhs.first);
second = cr::move (rhs.second);
return *this;
}
// specialized operators for binary heap, do not use as it's test only second element
public:
friend bool operator < (const Twin &a, const Twin &b) {
return a.second < b.second;
}
friend bool operator <= (const Twin &a, const Twin &b) {
return a.second <= b.second;
}
friend bool operator > (const Twin &a, const Twin &b) {
return b.second < a.second;
}
friend bool operator >= (const Twin &a, const Twin &b) {
return b.second <= a.second;
}
};
// creating pairs
template <typename A, typename B> constexpr Twin <A, B> makeTwin (A &&a, B &&b) {
return Twin <A, B> (cr::forward <A> (a), cr::forward <B> (b));
}
template <typename A, typename B> constexpr Twin <A, B> makeTwin (const A &a, const B &b) {
return Twin <A, B> (a, b);
}
CR_NAMESPACE_END

View file

@ -9,62 +9,69 @@
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// see https://github.com/encode84/ulz/
class FastLZ final : NonCopyable {
class ULZ final : DenyCopying {
public:
static constexpr int EXCESS = 16;
static constexpr int WINDOW_BITS = 17;
static constexpr int WINDOW_SIZE = cr::bit (WINDOW_BITS);
static constexpr int WINDOW_MASK = WINDOW_SIZE - 1;
static constexpr int MIN_MATCH = 4;
static constexpr int MAX_CHAIN = cr::bit (5);
static constexpr int HASH_BITS = 19;
static constexpr int HASH_SIZE = cr::bit (HASH_BITS);
static constexpr int NIL = -1;
static constexpr int UNCOMPRESS_RESULT_FAILED = -1;
enum : int32 {
Excess = 16,
UncompressFailure = -1
};
private:
int *m_hashTable;
int *m_prevTable;
enum : int32 {
WindowBits = 17,
WindowSize = cr::bit (WindowBits),
WindowMask = WindowSize - 1,
MinMatch = 4,
MaxChain = cr::bit (5),
HashBits = 19,
HashLength = cr::bit (HashBits),
EmptyHash = -1,
};
private:
Array <int32, ReservePolicy::PlusOne> m_hashTable;
Array <int32, ReservePolicy::PlusOne> m_prevTable;
public:
FastLZ (void) {
m_hashTable = new int[HASH_SIZE];
m_prevTable = new int[WINDOW_SIZE];
}
~FastLZ (void) {
delete [] m_hashTable;
delete [] m_prevTable;
ULZ () {
m_hashTable.resize (HashLength);
m_prevTable.resize (WindowSize);
}
~ULZ () = default;
public:
int compress (uint8 *in, int inLength, uint8 *out) {
for (int i = 0; i < HASH_SIZE; i++) {
m_hashTable[i] = NIL;
int32 compress (uint8 *in, int32 inputLength, uint8 *out) {
for (auto &htb : m_hashTable) {
htb = EmptyHash;
}
uint8 *op = out;
int anchor = 0;
int cur = 0;
int32 anchor = 0;
int32 cur = 0;
while (cur < inLength) {
const int maxMatch = inLength - cur;
while (cur < inputLength) {
const int32 maxMatch = inputLength - cur;
int bestLength = 0;
int dist = 0;
int32 bestLength = 0;
int32 dist = 0;
if (maxMatch >= MIN_MATCH) {
const int limit = cr::max (cur - WINDOW_SIZE, NIL);
if (maxMatch >= MinMatch) {
const int32 limit = cr::max <int32> (cur - WindowSize, EmptyHash);
int chainLength = MAX_CHAIN;
int lookup = m_hashTable[hash32 (&in[cur])];
int32 chainLength = MaxChain;
int32 lookup = m_hashTable[hash32 (&in[cur])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[cur + bestLength] && load32 (&in[lookup]) == load32 (&in[cur])) {
int length = MIN_MATCH;
int32 length = MinMatch;
while (length < maxMatch && in[lookup + length] == in[cur + length]) {
length++;
@ -83,31 +90,31 @@ public:
if (--chainLength == 0) {
break;
}
lookup = m_prevTable[lookup & WINDOW_MASK];
lookup = m_prevTable[lookup & WindowMask];
}
}
if (bestLength == MIN_MATCH && (cur - anchor) >= (7 + 128)) {
if (bestLength == MinMatch && (cur - anchor) >= (7 + 128)) {
bestLength = 0;
}
if (bestLength >= MIN_MATCH && bestLength < maxMatch && (cur - anchor) != 6) {
const int next = cur + 1;
const int targetLength = bestLength + 1;
const int limit = cr::max (next - WINDOW_SIZE, NIL);
if (bestLength >= MinMatch && bestLength < maxMatch && (cur - anchor) != 6) {
const int32 next = cur + 1;
const int32 target = bestLength + 1;
const int32 limit = cr::max <int32> (next - WindowSize, EmptyHash);
int chainLength = MAX_CHAIN;
int lookup = m_hashTable[hash32 (&in[next])];
int32 chainLength = MaxChain;
int32 lookup = m_hashTable[hash32 (&in[next])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[next + bestLength] && load32 (&in[lookup]) == load32 (&in[next])) {
int length = MIN_MATCH;
int32 length = MinMatch;
while (length < targetLength && in[lookup + length] == in[next + length]) {
while (length < target && in[lookup + length] == in[next + length]) {
length++;
}
if (length == targetLength) {
if (length == target) {
bestLength = 0;
break;
}
@ -116,16 +123,16 @@ public:
if (--chainLength == 0) {
break;
}
lookup = m_prevTable[lookup & WINDOW_MASK];
lookup = m_prevTable[lookup & WindowMask];
}
}
if (bestLength >= MIN_MATCH) {
const int length = bestLength - MIN_MATCH;
const int token = ((dist >> 12) & 16) + cr::min (length, 15);
if (bestLength >= MinMatch) {
const int32 length = bestLength - MinMatch;
const int32 token = ((dist >> 12) & 16) + cr::min <int32> (length, 15);
if (anchor != cur) {
const int run = cur - anchor;
const int32 run = cur - anchor;
if (run >= 7) {
add (op, (7 << 5) + token);
@ -150,7 +157,7 @@ public:
while (bestLength-- != 0) {
const uint32 hash = hash32 (&in[cur]);
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
m_prevTable[cur & WindowMask] = m_hashTable[hash];
m_hashTable[hash] = cur++;
}
anchor = cur;
@ -158,13 +165,13 @@ public:
else {
const uint32 hash = hash32 (&in[cur]);
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
m_prevTable[cur & WindowMask] = m_hashTable[hash];
m_hashTable[hash] = cur++;
}
}
if (anchor != cur) {
const int run = cur - anchor;
const int32 run = cur - anchor;
if (run >= 7) {
add (op, 7 << 5);
@ -179,25 +186,25 @@ public:
return op - out;
}
int uncompress (uint8 *in, int inLength, uint8 *out, int outLength) {
int32 uncompress (uint8 *in, int32 inputLength, uint8 *out, int32 outLength) {
uint8 *op = out;
uint8 *ip = in;
const uint8 *opEnd = op + outLength;
const uint8 *ipEnd = ip + inLength;
const uint8 *ipEnd = ip + inputLength;
while (ip < ipEnd) {
const int token = *ip++;
const int32 token = *ip++;
if (token >= 32) {
int run = token >> 5;
int32 run = token >> 5;
if (run == 7) {
run += decode (ip);
}
if ((opEnd - op) < run || (ipEnd - ip) < run) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
copy (op, ip, run);
@ -208,22 +215,22 @@ public:
break;
}
}
int length = (token & 15) + MIN_MATCH;
int32 length = (token & 15) + MinMatch;
if (length == (15 + MIN_MATCH)) {
if (length == (15 + MinMatch)) {
length += decode (ip);
}
if ((opEnd - op) < length) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
const int dist = ((token & 16) << 12) + load16 (ip);
const int32 dist = ((token & 16) << 12) + load16 (ip);
ip += 2;
uint8 *cp = op - dist;
if ((op - out) < dist) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
if (dist >= 8) {
@ -232,7 +239,7 @@ public:
}
else
{
for (int i = 0; i < 4; i++) {
for (int32 i = 0; i < 4; ++i) {
*op++ = *cp++;
}
@ -241,7 +248,7 @@ public:
}
}
}
return (ip == ipEnd) ? op - out : UNCOMPRESS_RESULT_FAILED;
return static_cast <int32> (ip == ipEnd) ? static_cast <int32> (op - out) : UncompressFailure;
}
private:
@ -253,7 +260,7 @@ private:
return *reinterpret_cast <const uint32 *> (ptr);
}
inline void store16 (void *ptr, int val) {
inline void store16 (void *ptr, int32 val) {
*reinterpret_cast <uint16 *> (ptr) = static_cast <uint16> (val);
}
@ -262,18 +269,18 @@ private:
}
inline uint32 hash32 (void *ptr) {
return (load32 (ptr) * 0x9E3779B9) >> (32 - HASH_BITS);
return (load32 (ptr) * 0x9E3779B9) >> (32 - HashBits);
}
inline void copy (uint8 *dst, uint8 *src, int count) {
inline void copy (uint8 *dst, uint8 *src, int32 count) {
copy64 (dst, src);
for (int i = 8; i < count; i += 8) {
for (int32 i = 8; i < count; i += 8) {
copy64 (dst + i, src + i);
}
}
inline void add (uint8 *&dst, int val) {
inline void add (uint8 *&dst, int32 val) {
*dst++ = static_cast <uint8> (val);
}
@ -290,7 +297,7 @@ private:
inline uint32 decode (uint8 *&ptr) {
uint32 val = 0;
for (int i = 0; i <= 21; i += 7) {
for (int32 i = 0; i <= 21; i += 7) {
const uint32 cur = *ptr++;
val += cur << i;
@ -301,3 +308,7 @@ private:
return val;
}
};
CR_NAMESPACE_END

View file

@ -0,0 +1,99 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple unique ptr
template <typename T> class UniquePtr final : public DenyCopying {
private:
T *m_ptr = nullptr;
public:
UniquePtr () = default;
explicit UniquePtr (T *ptr) : m_ptr (ptr)
{ }
UniquePtr (UniquePtr &&rhs) noexcept : m_ptr (rhs.m_ptr) {
rhs.m_ptr = nullptr;
}
~UniquePtr () {
destroy ();
}
public:
T *get () const {
return m_ptr;
}
T *release () {
auto ret = m_ptr;
m_ptr = nullptr;
return ret;
}
void reset (T *ptr = nullptr) {
destroy ();
m_ptr = ptr;
}
private:
void destroy () {
if (m_ptr) {
alloc.destroy (m_ptr);
m_ptr = nullptr;
}
}
public:
UniquePtr &operator = (UniquePtr &&rhs) noexcept {
if (this != &rhs) {
destroy ();
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
}
return *this;
}
UniquePtr &operator = (decltype (nullptr)) {
destroy ();
return *this;
}
T &operator * () const {
return *m_ptr;
}
T *operator -> () const {
return m_ptr;
}
explicit operator bool () const {
return m_ptr != nullptr;
}
};
// create unique
template <typename T, typename... Args> UniquePtr <T> createUnique (Args &&... args) {
return UniquePtr <T> (alloc.create <T> (cr::forward <Args> (args)...));
}
// create unique (base class)
template <typename D, typename B, typename... Args> UniquePtr <B> createUniqueBase (Args &&... args) {
return UniquePtr <B> (alloc.create <D> (cr::forward <Args> (args)...));
}
CR_NAMESPACE_END

232
include/crlib/cr-vector.h Normal file
View file

@ -0,0 +1,232 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-math.h>
CR_NAMESPACE_BEGIN
// 3dmath vector
class Vector final {
public:
float x = 0.0f, y = 0.0f, z = 0.0f;
public:
Vector (const float scaler = 0.0f) : x (scaler), y (scaler), z (scaler)
{ }
explicit Vector (const float _x, const float _y, const float _z) : x (_x), y (_y), z (_z)
{ }
Vector (float *rhs) : x (rhs[0]), y (rhs[1]), z (rhs[2])
{ }
Vector (const Vector &) = default;
public:
operator float *() {
return &x;
}
operator const float * () const {
return &x;
}
Vector operator + (const Vector &rhs) const {
return Vector (x + rhs.x, y + rhs.y, z + rhs.z);
}
Vector operator - (const Vector &rhs) const {
return Vector (x - rhs.x, y - rhs.y, z - rhs.z);
}
Vector operator - () const {
return Vector (-x, -y, -z);
}
friend Vector operator * (const float scale, const Vector &rhs) {
return Vector (rhs.x * scale, rhs.y * scale, rhs.z * scale);
}
Vector operator * (const float scale) const {
return Vector (scale * x, scale * y, scale * z);
}
Vector operator / (const float div) const {
const float inv = 1 / div;
return Vector (inv * x, inv * y, inv * z);
}
// cross product
Vector operator ^ (const Vector &rhs) const {
return Vector (y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);
}
// dot product
float operator | (const Vector &rhs) const {
return x * rhs.x + y * rhs.y + z * rhs.z;
}
const Vector &operator += (const Vector &rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
const Vector &operator -= (const Vector &right) {
x -= right.x;
y -= right.y;
z -= right.z;
return *this;
}
const Vector &operator *= (float scale) {
x *= scale;
y *= scale;
z *= scale;
return *this;
}
const Vector &operator /= (float div) {
const float inv = 1 / div;
x *= inv;
y *= inv;
z *= inv;
return *this;
}
bool operator == (const Vector &rhs) const {
return cr::fequal (x, rhs.x) && cr::fequal (y, rhs.y) && cr::fequal (z, rhs.z);
}
bool operator != (const Vector &rhs) const {
return !cr::fequal (x, rhs.x) && !cr::fequal (y, rhs.y) && !cr::fequal (z, rhs.z);
}
Vector &operator = (const Vector &) = default;
public:
float length () const {
return cr::sqrtf (lengthSq ());
}
float length2d () const {
return cr::sqrtf (x * x + y * y);
}
float lengthSq () const {
return x * x + y * y + z * z;
}
Vector get2d () const {
return Vector (x, y, 0.0f);
}
Vector normalize () const {
float len = length () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return Vector (0.0f, 0.0f, 1.0f);
}
len = 1.0f / len;
return Vector (x * len, y * len, z * len);
}
Vector normalize2d () const {
float len = length2d () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return Vector (0.0f, 1.0f, 0.0f);
}
len = 1.0f / len;
return Vector (x * len, y * len, 0.0f);
}
bool empty () const {
return cr::fzero (x) && cr::fzero (y) && cr::fzero (z);
}
static const Vector &null () {
static const auto s_zero = Vector (0.0f, 0.0f, 0.0f);
return s_zero;
}
void clear () {
x = y = z = 0.0f;
}
Vector clampAngles () {
x = cr::normalizeAngles (x);
y = cr::normalizeAngles (y);
z = 0.0f;
return *this;
}
float pitch () const {
if (cr::fzero (x) && cr::fzero (y)) {
return 0.0f;
}
return cr::degreesToRadians (cr::atan2f (z, length2d ()));
}
float yaw () const {
if (cr::fzero (x) && cr::fzero (y)) {
return 0.0f;
}
return cr::radiansToDegrees (cr:: atan2f (y, x));
}
Vector angles () const {
if (cr::fzero (x) && cr::fzero (y)) {
return Vector (z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f);
}
return Vector (cr::radiansToDegrees (cr::atan2f (z, length2d ())), cr::radiansToDegrees (cr::atan2f (y, x)), 0.0f);
}
void buildVectors (Vector *forward, Vector *right, Vector *upward) const {
enum { pitch, yaw, roll, unused, max };
float sines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
// compute the sine and cosine compontents
cr::sincosf (cr::degreesToRadians (x), cr::degreesToRadians (y), cr::degreesToRadians (z), sines, cosines);
if (forward) {
forward->x = cosines[pitch] * cosines[yaw];
forward->y = cosines[pitch] * sines[yaw];
forward->z = -sines[pitch];
}
if (right) {
right->x = -sines[roll] * sines[pitch] * cosines[yaw] + cosines[roll] * sines[yaw];
right->y = -sines[roll] * sines[pitch] * sines[yaw] - cosines[roll] * cosines[yaw];
right->z = -sines[roll] * cosines[pitch];
}
if (upward) {
upward->x = cosines[roll] * sines[pitch] * cosines[yaw] + sines[roll] * sines[yaw];
upward->y = cosines[roll] * sines[pitch] * sines[yaw] - sines[roll] * cosines[yaw];
upward->z = cosines[roll] * cosines[pitch];
}
}
};
// expose global null vector
static auto &nullvec = Vector::null ();
CR_NAMESPACE_END

View file

@ -10,95 +10,91 @@
#pragma once
// line draw
enum DrawLineType : int {
DRAW_SIMPLE,
DRAW_ARROW,
DRAW_NUM
};
CR_DECLARE_SCOPED_ENUM (DrawLine,
Simple,
Arrow,
Count
);
// trace ignore
enum TraceIgnore : int {
TRACE_IGNORE_NONE = 0,
TRACE_IGNORE_GLASS = cr::bit (0),
TRACE_IGNORE_MONSTERS = cr::bit (1),
TRACE_IGNORE_EVERYTHING = TRACE_IGNORE_GLASS | TRACE_IGNORE_MONSTERS
};
CR_DECLARE_SCOPED_ENUM (TraceIgnore,
None = 0,
Glass = cr::bit (0),
Monsters = cr::bit (1),
Everything = Glass | Monsters
);
// variable type
enum VarType : int {
VT_NORMAL = 0,
VT_READONLY,
VT_PASSWORD,
VT_NOSERVER,
VT_NOREGISTER
};
CR_DECLARE_SCOPED_ENUM (Var,
Normal = 0,
ReadOnly,
Password,
NoServer,
NoRegister
);
// netmessage functions
enum NetMsgId : int {
NETMSG_UNDEFINED = -1,
NETMSG_VGUI = 1,
NETMSG_SHOWMENU = 2,
NETMSG_WEAPONLIST = 3,
NETMSG_CURWEAPON = 4,
NETMSG_AMMOX = 5,
NETMSG_AMMOPICKUP = 6,
NETMSG_DAMAGE = 7,
NETMSG_MONEY = 8,
NETMSG_STATUSICON = 9,
NETMSG_DEATH = 10,
NETMSG_SCREENFADE = 11,
NETMSG_HLTV = 12,
NETMSG_TEXTMSG = 13,
NETMSG_TEAMINFO = 14,
NETMSG_BARTIME = 15,
NETMSG_SENDAUDIO = 17,
NETMSG_SAYTEXT = 18,
NETMSG_BOTVOICE = 19,
NETMSG_NVGTOGGLE = 20,
NETMSG_FLASHBAT = 21,
NETMSG_FLASHLIGHT = 22,
NETMSG_ITEMSTATUS = 23,
NETMSG_NUM = 25
};
CR_DECLARE_SCOPED_ENUM (NetMsg,
None = -1,
VGUI = 1,
ShowMenu = 2,
WeaponList = 3,
CurWeapon = 4,
AmmoX = 5,
AmmoPickup = 6,
Damage = 7,
Money = 8,
StatusIcon = 9,
DeathMsg = 10,
ScreenFade = 11,
HLTV = 12,
TextMsg = 13,
TeamInfo = 14,
BarTime = 15,
SendAudio = 17,
SayText = 18,
BotVoice = 19,
NVGToggle = 20,
FlashBat = 21,
Fashlight = 22,
ItemStatus = 23,
Count = 25
);
// supported cs's
enum GameFlags : int {
GAME_CSTRIKE16 = cr::bit (0), // counter-strike 1.6 and above
GAME_XASH_ENGINE = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
GAME_CZERO = cr::bit (2), // counter-strike: condition zero
GAME_LEGACY = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
GAME_MOBILITY = cr::bit (4), // additional flag that bot is running on android (additional flag)
GAME_OFFICIAL_CSBOT = cr::bit (5), // additional flag that indicates official cs bots are in game
GAME_METAMOD = cr::bit (6), // game running under meta\mod
GAME_CSDM = cr::bit (7), // csdm mod currently in use
GAME_CSDM_FFA = cr::bit (8), // csdm mod with ffa mode
GAME_REGAMEDLL = cr::bit (9), // server dll is a regamedll
GAME_SUPPORT_SVC_PINGS = cr::bit (10), // on that game version we can fake bots pings
GAME_SUPPORT_BOT_VOICE = cr::bit (11) // on that game version we can use chatter
};
CR_DECLARE_SCOPED_ENUM (GameFlags,
Modern = cr::bit (0), // counter-strike 1.6 and above
Xash3D = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
ConditionZero = cr::bit (2), // counter-strike: condition zero
Legacy = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
Mobility = cr::bit (4), // additional flag that bot is running on android (additional flag)
CSBot = cr::bit (5), // additional flag that indicates official cs bots are in game
Metamod = cr::bit (6), // game running under meta\mod
CSDM = cr::bit (7), // csdm mod currently in use
FreeForAll = cr::bit (8), // csdm mod with ffa mode
ReGameDLL = cr::bit (9), // server dll is a regamedll
HasFakePings = cr::bit (10), // on that game version we can fake bots pings
HasBotVoice = cr::bit (11) // on that game version we can use chatter
);
// defines map type
enum MapFlags : int {
MAP_AS = cr::bit (0),
MAP_CS = cr::bit (1),
MAP_DE = cr::bit (2),
MAP_ES = cr::bit (3),
MAP_KA = cr::bit (4),
MAP_FY = cr::bit (5),
// additional flags
MAP_HAS_DOORS = cr::bit (6)
};
CR_DECLARE_SCOPED_ENUM (MapFlags,
Assassination = cr::bit (0),
HostageRescue = cr::bit (1),
Demolition = cr::bit (2),
Escape = cr::bit (3),
KnifeArena = cr::bit (4),
Fun = cr::bit (5),
HasDoors = cr::bit (10) // additional flags
);
// variable reg pair
struct VarPair {
VarType type;
Var type;
cvar_t reg;
bool missing;
const char *regval;
class ConVar *self;
bool regMissing;
const char *regVal;
};
// network message block
@ -106,7 +102,7 @@ struct MessageBlock {
int bot;
int state;
int msg;
int regMsgs[NETMSG_NUM];
int regMsgs[NetMsg::Count];
};
// referentia vector info
@ -117,14 +113,14 @@ struct RefVector {
// entity prototype
using EntityFunction = void (*) (entvars_t *);
// compare language
struct LangComprarer {
size_t operator () (const String &key) const {
char *str = const_cast <char *> (key.chars ());
size_t hash = key.length ();
// language hasher
struct HashLangString {
uint32 operator () (const String &key) const {
auto str = reinterpret_cast <uint8 *> (const_cast <char *> (key.chars ()));
uint32 hash = 0;
while (*str++) {
if (!isalpha (*str)) {
if (!isalnum (*str)) {
continue;
}
hash = ((*str << 5) + hash) + *str;
@ -136,8 +132,8 @@ struct LangComprarer {
// provides utility functions to not call original engine (less call-cost)
class Game final : public Singleton <Game> {
private:
int m_drawModels[DRAW_NUM];
int m_spawnCount[TEAM_UNASSIGNED];
int m_drawModels[DrawLine::Count];
int m_spawnCount[Team::Unassigned];
// bot client command
bool m_isBotCommand;
@ -147,9 +143,9 @@ private:
edict_t *m_localEntity;
Array <VarPair> m_cvars;
HashMap <String, String, LangComprarer> m_language;
Dictionary <String, String, HashLangString> m_language;
Library m_gameLib;
SharedLibrary m_gameLib;
MessageBlock m_msgBlock;
bool m_precached;
@ -161,33 +157,18 @@ public:
RefVector vec;
public:
Game (void);
~Game (void);
Game ();
~Game ();
public:
// precaches internal stuff
void precache (void);
void precache ();
// initialize levels
void levelInitialize (edict_t *ents, int max);
// prints data to servers console
void print (const char *fmt, ...);
// prints chat message to all players
void chatPrint (const char *fmt, ...);
// prints center message to all players
void centerPrint (const char *fmt, ...);
// prints center message to specified player
void centerPrint (edict_t *ent, const char *fmt, ...);
// prints message to client console
void clientPrint (edict_t *ent, const char *fmt, ...);
// display world line
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type = DRAW_SIMPLE);
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple);
// test line
void testLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr);
@ -199,34 +180,31 @@ public:
float getWaveLen (const char *fileName);
// we are on dedicated server ?
bool isDedicated (void);
bool isDedicated ();
// get stripped down mod name
const char *getModName (void);
const char *getModName ();
// get the valid mapname
const char *getMapName (void);
const char *getMapName ();
// get the "any" entity origin
Vector getAbsPos (edict_t *ent);
// send server command
void execCmd (const char *fmt, ...);
// registers a server command
void registerCmd (const char *command, void func (void));
void registerCmd (const char *command, void func_ ());
// play's sound to client
void playSound (edict_t *ent, const char *sound);
// sends bot command
void execBotCmd (edict_t *ent, const char *fmt, ...);
void prepareBotArgs (edict_t *ent, String str);
// adds cvar to registration stack
void pushVarToRegStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self);
void addNewCvar (const char *variable, const char *value, Var varType, bool regMissing, const char *regVal, class ConVar *self);
// sends local registration stack for engine registration
void pushRegStackToEngine (bool gameVars = false);
void registerCvars (bool gameVars = false);
// translates bot message into needed language
const char *translate (const char *input);
@ -235,19 +213,19 @@ public:
void processMessages (void *ptr);
// checks whether softwared rendering is enabled
bool isSoftwareRenderer (void);
bool isSoftwareRenderer ();
// load the cs binary in non metamod mode
bool loadCSBinary (void);
bool loadCSBinary ();
// do post-load stuff
bool postload (void);
bool postload ();
// detects if csdm mod is in use
void detectDeathmatch (void);
void detectDeathmatch ();
// executes stuff every 1 second
void slowFrame (void);
void slowFrame ();
// begin message handler
void beginMessage (edict_t *ent, int dest, int type);
@ -255,26 +233,23 @@ public:
// public inlines
public:
// get the current time on server
float timebase (void) const {
float timebase () const {
return globals->time;
}
// get "maxplayers" limit on server
int maxClients (void) const {
int maxClients () const {
return globals->maxClients;
}
// get the fakeclient command interface
bool isBotCmd (void) const {
bool isBotCmd () const {
return m_isBotCommand;
}
// gets custom engine args for client command
const char *botArgs (void) const {
static String args;
args = String::join (m_botArgs, " ", m_botArgs[0] == "say" || m_botArgs[0] == "say_team" ? 1 : 0);
return args.chars ();
const char *botArgs () const {
return strings.format (String::join (m_botArgs, " ", m_botArgs[0] == "say" || m_botArgs[0] == "say_team" ? 1 : 0).chars ());
}
// gets custom engine argv for client command
@ -286,7 +261,7 @@ public:
}
// gets custom engine argc for client command
int botArgc (void) const {
int botArgc () const {
return m_botArgs.length ();
}
@ -295,18 +270,28 @@ public:
return static_cast <edict_t *> (m_startEntity + index);
};
// gets edict pointer out of entity index (player)
edict_t *playerOfIndex (const int index) {
return entityOfIndex (index) + 1;
};
// gets edict index out of it's pointer
int indexOfEntity (const edict_t *ent) {
return static_cast <int> (ent - m_startEntity);
};
// gets edict index of it's pointer (player)
int indexOfPlayer (const edict_t *ent) {
return indexOfEntity (ent) - 1;
}
// verify entity isn't null
bool isNullEntity (const edict_t *ent) {
return !ent || !indexOfEntity (ent) || ent->free;
}
// get the wroldspawn entity
edict_t *getStartEntity (void) {
edict_t *getStartEntity () {
return m_startEntity;
}
@ -320,12 +305,17 @@ public:
// adds translation pair from config
void addTranslation (const String &original, const String &translated) {
m_language.put (original, translated);
m_language.push (original, translated);
}
// clear the translation table
void clearTranslation () {
m_language.clear ();
}
// resets the message capture mechanism
void resetMessages (void) {
m_msgBlock.msg = NETMSG_UNDEFINED;
void resetMessages () {
m_msgBlock.msg = NetMsg::None;
m_msgBlock.state = 0;
m_msgBlock.bot = 0;
};
@ -358,12 +348,12 @@ public:
}
// sets the precache to uninitialize
void setUnprecached (void) {
void setUnprecached () {
m_precached = false;
}
// gets the local entity (host edict)
edict_t *getLocalEntity (void) {
edict_t *getLocalEntity () {
return m_localEntity;
}
@ -374,17 +364,12 @@ public:
// builds referential vector
void makeVectors (const Vector &in) {
in.makeVectors (&vec.forward, &vec.right, &vec.up);
}
// what kind of map we're running ?
bool isMap (const int map) const {
return (m_mapFlags & map) == map;
in.buildVectors (&vec.forward, &vec.right, &vec.up);
}
// what kind of game engine / game dll / mod / tool we're running ?
bool is (const int type) const {
return (m_gameFlags & type) == type;
return !!(m_gameFlags & type);
}
// adds game flag
@ -394,43 +379,79 @@ public:
// gets the map type
bool mapIs (const int type) const {
return (m_mapFlags & type) == type;
return !!(m_mapFlags & type);
}
// get loaded gamelib
Library &getLib (void) {
const SharedLibrary &lib () {
return m_gameLib;
}
// helper to sending the client message
void sendClientMessage (bool console, edict_t *ent, const char *message);
// send server command
template <typename ...Args> void serverCommand (const char *fmt, Args ...args) {
engfuncs.pfnServerCommand (strncat (strings.format (fmt, cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// send a bot command
template <typename ...Args> void botCommand (edict_t *ent, const char *fmt, Args ...args) {
prepareBotArgs (ent, strings.format (fmt, cr::forward <Args> (args)...));
}
// prints data to servers console
template <typename ...Args> void print (const char *fmt, Args ...args) {
engfuncs.pfnServerPrint (strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// prints center message to specified player
template <typename ...Args> void clientPrint (edict_t *ent, const char *fmt, Args ...args) {
if (isNullEntity (ent)) {
print (fmt, cr::forward <Args> (args)...);
return;
}
sendClientMessage (true, ent, strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// prints message to client console
template <typename ...Args> void centerPrint (edict_t *ent, const char *fmt, Args ...args) {
if (isNullEntity (ent)) {
print (fmt, cr::forward <Args> (args)...);
return;
}
sendClientMessage (false, ent, strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
};
// simplify access for console variables
class ConVar {
public:
cvar_t *m_eptr;
cvar_t *eptr;
public:
ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr) : m_eptr (nullptr) {
Game::ref ().pushVarToRegStack (name, initval, type, regMissing, regVal, this);
ConVar (const char *name, const char *initval, Var type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : eptr (nullptr) {
Game::get ().addNewCvar (name, initval, type, regMissing, regVal, this);
}
bool boolean (void) const {
return m_eptr->value > 0.0f;
bool bool_ () const {
return eptr->value > 0.0f;
}
int integer (void) const {
return static_cast <int> (m_eptr->value);
int int_ () const {
return static_cast <int> (eptr->value);
}
float flt (void) const {
return m_eptr->value;
float float_ () const {
return eptr->value;
}
const char *str (void) const {
return m_eptr->string;
const char *str () const {
return eptr->string;
}
void set (float val) {
engfuncs.pfnCVarSetFloat (m_eptr->name, val);
engfuncs.pfnCVarSetFloat (eptr->name, val);
}
void set (int val) {
@ -438,7 +459,7 @@ public:
}
void set (const char *val) {
engfuncs.pfnCvar_DirectSet (m_eptr, const_cast <char *> (val));
engfuncs.pfnCvar_DirectSet (eptr, const_cast <char *> (val));
}
};
@ -447,26 +468,26 @@ private:
bool m_autoDestruct { false };
public:
MessageWriter (void) = default;
MessageWriter () = default;
MessageWriter (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
MessageWriter (int dest, int type, const Vector &pos = nullvec, edict_t *to = nullptr) {
start (dest, type, pos, to);
m_autoDestruct = true;
}
virtual ~MessageWriter (void) {
~MessageWriter () {
if (m_autoDestruct) {
end ();
}
}
public:
MessageWriter &start (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
MessageWriter &start (int dest, int type, const Vector &pos = nullvec, edict_t *to = nullptr) {
engfuncs.pfnMessageBegin (dest, type, pos, to);
return *this;
}
void end (void) {
void end () {
engfuncs.pfnMessageEnd ();
}
@ -475,6 +496,11 @@ public:
return *this;
}
MessageWriter &writeLong (int val) {
engfuncs.pfnWriteLong (val);
return *this;
}
MessageWriter &writeChar (int val) {
engfuncs.pfnWriteChar (val);
return *this;
@ -497,11 +523,11 @@ public:
public:
static inline uint16 fu16 (float value, float scale) {
return cr::clamp <uint16> (static_cast <uint16> (value * scale), 0, 0xffff);
return cr::clamp <uint16> (static_cast <uint16> (value * cr::bit (static_cast <short> (scale))), 0, 0xffff);
}
static inline short fs16 (float value, float scale) {
return cr::clamp <short> (static_cast <short> (value * scale), -32767, 32767);
return cr::clamp <short> (static_cast <short> (value * cr::bit (static_cast <short> (scale))), -32767, 32767);
}
};
@ -511,28 +537,28 @@ private:
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE];
bool m_doAnimation = false;
SimpleColor m_point;
Color m_point;
model_t *m_worldModel = nullptr;
public:
LightMeasure (void) {
LightMeasure () {
initializeLightstyles ();
m_point.reset ();
}
public:
void initializeLightstyles (void);
void animateLight (void);
void initializeLightstyles ();
void animateLight ();
void updateLight (int style, char *value);
float getLightLevel (const Vector &point);
float getSkyColor (void);
float getSkyColor ();
private:
template <typename S, typename M> bool recursiveLightPoint (const M *node, const Vector &start, const Vector &end);
public:
void resetWorldModel (void) {
void resetWorldModel () {
m_worldModel = nullptr;
}

View file

@ -85,6 +85,25 @@ typedef struct {
int iHitgroup; // 0 == generic, non zero is specific body part
} TraceResult;
typedef struct usercmd_s {
short lerp_msec; // Interpolation time on client
byte msec; // Duration in ms of command
vec3_t viewangles; // Command view angles.
// intended velocities
float forwardmove; // Forward velocity.
float sidemove; // Sideways velocity.
float upmove; // Upward velocity.
byte lightlevel; // Light level at spot where we are standing.
unsigned short buttons; // Attack buttons
byte impulse; // Impulse command issued.
byte weaponselect; // Current weapon id
// Experimental player impact stuff.
int impact_index;
vec3_t impact_position;
} usercmd_t;
typedef uint32 CRC32_t;
// Engine hands this to DLLs for functionality callbacks
@ -111,7 +130,7 @@ typedef struct enginefuncs_s {
edict_t *(*pfnEntitiesInPVS) (edict_t *pplayer);
void (*pfnMakeVectors) (const float *rgflVector);
void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up);
edict_t *(*pfnCreateEntity) (void);
edict_t *(*pfnCreateEntity) ();
void (*pfnRemoveEntity) (edict_t *e);
edict_t *(*pfnCreateNamedEntity) (int className);
void (*pfnMakeStatic) (edict_t *ent);
@ -130,14 +149,14 @@ typedef struct enginefuncs_s {
void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr);
void (*pfnGetAimVector) (edict_t *ent, float speed, float *rgflReturn);
void (*pfnServerCommand) (char *str);
void (*pfnServerExecute) (void);
void (*pfnServerExecute) ();
void (*pfnClientCommand) (edict_t *ent, char const *szFmt, ...);
void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count);
void (*pfnLightStyle) (int style, char *val);
int (*pfnDecalIndex) (const char *name);
int (*pfnPointContents) (const float *rgflVector);
void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
void (*pfnMessageEnd) (void);
void (*pfnMessageEnd) ();
void (*pfnWriteByte) (int value);
void (*pfnWriteChar) (int value);
void (*pfnWriteShort) (int value);
@ -151,7 +170,7 @@ typedef struct enginefuncs_s {
const char *(*pfnCVarGetString) (const char *szVarName);
void (*pfnCVarSetFloat) (const char *szVarName, float flValue);
void (*pfnCVarSetString) (const char *szVarName, const char *szValue);
void (*pfnAlertMessage) (ALERT_TYPE atype, char *szFmt, ...);
void (*pfnAlertMessage) (ALERT_TYPE atype, const char *szFmt, ...);
void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...);
void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32 cb);
void *(*pfnPvEntPrivateData) (edict_t *ent);
@ -172,9 +191,9 @@ typedef struct enginefuncs_s {
const char *(*pfnNameForFunction) (uint32 function);
void (*pfnClientPrintf) (edict_t *ent, PRINT_TYPE ptype, const char *szMsg); // JOHN: engine callbacks so game DLL can print messages to individual clients
void (*pfnServerPrint) (const char *szMsg);
const char *(*pfnCmd_Args) (void); // these 3 added
const char *(*pfnCmd_Args) (); // these 3 added
const char *(*pfnCmd_Argv) (int argc); // so game DLL can easily
int (*pfnCmd_Argc) (void); // access client 'cmd' strings
int (*pfnCmd_Argc) (); // access client 'cmd' strings
void (*pfnGetAttachment) (const edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles);
void (*pfnCRC32_Init) (CRC32_t *pulCRC);
void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len);
@ -183,7 +202,7 @@ typedef struct enginefuncs_s {
int32 (*pfnRandomLong) (int32 lLow, int32 lHigh);
float (*pfnRandomFloat) (float flLow, float flHigh);
void (*pfnSetView) (const edict_t *client, const edict_t *pViewent);
float (*pfnTime) (void);
float (*pfnTime) ();
void (*pfnCrosshairAngle) (const edict_t *client, float pitch, float yaw);
uint8 *(*pfnLoadFileForMe) (char const *szFilename, int *pLength);
void (*pfnFreeFile) (void *buffer);
@ -195,7 +214,7 @@ typedef struct enginefuncs_s {
void (*pfnSetClientMaxspeed) (const edict_t *ent, float fNewMaxspeed);
edict_t *(*pfnCreateFakeClient) (const char *netname); // returns nullptr if fake client can't be created
void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16 buttons, uint8 impulse, uint8 msec);
int (*pfnNumberOfEntities) (void);
int (*pfnNumberOfEntities) ();
char *(*pfnGetInfoKeyBuffer) (edict_t *e); // passing in nullptr gets the serverinfo
char *(*pfnInfoKeyValue) (char *infobuffer, char const *key);
void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value);
@ -205,7 +224,7 @@ typedef struct enginefuncs_s {
int (*pfnPrecacheGeneric) (char *s);
int (*pfnGetPlayerUserId) (edict_t *e); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients
void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
int (*pfnIsDedicatedServer) (void); // is this a dedicated server?
int (*pfnIsDedicatedServer) (); // is this a dedicated server?
cvar_t *(*pfnCVarGetPointer) (const char *szVarName);
unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients
@ -221,7 +240,7 @@ typedef struct enginefuncs_s {
void (*pfnDeltaSetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaUnsetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8 *from, const uint8 *to));
int (*pfnGetCurrentPlayer) (void);
int (*pfnGetCurrentPlayer) ();
int (*pfnCanSkipPlayer) (const edict_t *player);
int (*pfnDeltaFindField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaSetFieldByIndex) (struct delta_s *pFields, int fieldNumber);
@ -231,7 +250,7 @@ typedef struct enginefuncs_s {
void (*pfnCvar_DirectSet) (struct cvar_t *var, char *value);
void (*pfnForceUnmodified) (FORCE_TYPE type, float *mins, float *maxs, const char *szFilename);
void (*pfnGetPlayerStats) (const edict_t *client, int *ping, int *packet_loss);
void (*pfnAddServerCommand) (char *cmd_name, void (*function) (void));
void (*pfnAddServerCommand) (char *cmd_name, void (*function) ());
int (*pfnVoice_GetClientListening) (int iReceiver, int iSender);
int (*pfnVoice_SetClientListening) (int iReceiver, int iSender, int bListen);
@ -244,18 +263,20 @@ typedef struct enginefuncs_s {
int (*pfnGetFileSize) (char *szFilename);
unsigned int (*pfnGetApproxWavePlayLen) (const char *filepath);
int (*pfnIsCareerMatch) (void);
int (*pfnIsCareerMatch) ();
int (*pfnGetLocalizedStringLength) (const char *label);
void (*pfnRegisterTutorMessageShown) (int mid);
int (*pfnGetTimesTutorMessageShown) (int mid);
void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength);
void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength);
void (*pfnResetTutorMessageDecayData) (void);
void (*pfnResetTutorMessageDecayData) ();
void (*pfnQueryClientCVarValue) (const edict_t *player, const char *cvarName);
void (*pfnQueryClientCVarValue2) (const edict_t *player, const char *cvarName, int requestID);
int (*pfnCheckParm) (const char *pchCmdLineToken, char **ppnext);
#ifdef EIFACE_2019
edict_t *(*pfnPEntityOfEntIndexAllEntities) (int iEntIndex);
#endif
} enginefuncs_t;
// Passed to pfnKeyValue
@ -270,7 +291,7 @@ typedef struct customization_s customization_t;
typedef struct {
// Initialize/shutdown the game (one-time call after loading of game .dll )
void (*pfnGameInit) (void);
void (*pfnGameInit) ();
int (*pfnSpawn) (edict_t *pent);
void (*pfnThink) (edict_t *pent);
void (*pfnUse) (edict_t *pentUsed, edict_t *pentOther);
@ -286,7 +307,7 @@ typedef struct {
void (*pfnSaveGlobalState) (SAVERESTOREDATA *);
void (*pfnRestoreGlobalState) (SAVERESTOREDATA *);
void (*pfnResetGlobalState) (void);
void (*pfnResetGlobalState) ();
int (*pfnClientConnect) (edict_t *ent, const char *pszName, const char *pszAddress, char szRejectReason[128]);
@ -297,17 +318,17 @@ typedef struct {
void (*pfnClientUserInfoChanged) (edict_t *ent, char *infobuffer);
void (*pfnServerActivate) (edict_t *edictList, int edictCount, int clientMax);
void (*pfnServerDeactivate) (void);
void (*pfnServerDeactivate) ();
void (*pfnPlayerPreThink) (edict_t *ent);
void (*pfnPlayerPostThink) (edict_t *ent);
void (*pfnStartFrame) (void);
void (*pfnParmsNewLevel) (void);
void (*pfnParmsChangeLevel) (void);
void (*pfnStartFrame) ();
void (*pfnParmsNewLevel) ();
void (*pfnParmsChangeLevel) ();
// Returns string describing current .dll. E.g., TeamFotrress 2, Half-Life
const char *(*pfnGetGameDescription) (void);
const char *(*pfnGetGameDescription) ();
// Notify dll about a player customization.
void (*pfnPlayerCustomization) (edict_t *ent, struct customization_s *pCustom);
@ -327,10 +348,10 @@ typedef struct {
void (*pfnUpdateClientData) (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd);
int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8 *pSet);
void (*pfnCreateBaseline) (int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, float *player_mins, float *player_maxs);
void (*pfnRegisterEncoders) (void);
void (*pfnRegisterEncoders) ();
int (*pfnGetWeaponData) (struct edict_s *player, struct weapon_data_s *info);
void (*pfnCmdStart) (const edict_t *player, const struct c *cmd, unsigned int random_seed);
void (*pfnCmdStart) (const edict_t *player, usercmd_t *cmd, unsigned int random_seed);
void (*pfnCmdEnd) (const edict_t *player);
// Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max
@ -341,7 +362,7 @@ typedef struct {
int (*pfnGetHullBounds) (int hullnumber, float *mins, float *maxs);
// Create baselines for certain "unplaced" items.
void (*pfnCreateInstancedBaselines) (void);
void (*pfnCreateInstancedBaselines) ();
// One of the pfnForceUnmodified files failed the consistency check for the specified player
// Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters )
@ -351,7 +372,7 @@ typedef struct {
// the sv_unlag cvar.
// Most games right now should return 0, until client-side weapon prediction code is written
// and tested for them.
int (*pfnAllowLagCompensation) (void);
int (*pfnAllowLagCompensation) ();
} gamefuncs_t;
// Current version.
@ -361,7 +382,7 @@ typedef struct {
// Called right before the object's memory is freed.
// Calls its destructor.
void (*pfnOnFreeEntPrivateData) (edict_t *pEnt);
void (*pfnGameShutdown) (void);
void (*pfnGameShutdown) ();
int (*pfnShouldCollide) (edict_t *pentTouched, edict_t *pentOther);
void (*pfnCvarValue) (const edict_t *pEnt, const char *value);

View file

@ -54,9 +54,9 @@ typedef int func_t; //
typedef int string_t; // from engine's pr_comp.h;
typedef float vec_t; // needed before including progdefs.h
#include "corelib.h"
#include <crlib/cr-vector.h>
typedef cr::classes::Vector vec3_t;
typedef cr::Vector vec3_t;
using namespace cr::types;
#include "const.h"

View file

@ -24,6 +24,7 @@ extern gamefuncs_t dllapi;
#define STRING(offset) (const char *)(globals->pStringBase + (int)offset)
// form fwgs-hlsdk
#if defined (CR_ARCH_X64)
static inline int MAKE_STRING (const char *val) {
long long ptrdiff = val - STRING (0);
@ -32,6 +33,9 @@ static inline int MAKE_STRING (const char *val) {
}
return static_cast <int> (ptrdiff);
}
#else
#define MAKE_STRING(str) ((uint64)(str) - (uint64)(STRING(0)))
#endif
#define ENGINE_STR(str) (const_cast <char *> (STRING (engfuncs.pfnAllocString (str))))
@ -93,17 +97,17 @@ typedef struct hudtextparms_s {
#define PUSH_BLOCK_ONLY_X 1
#define PUSH_BLOCK_ONLY_Y 2
#define VEC_HULL_MIN Vector (-16, -16, -36)
#define VEC_HULL_MAX Vector (16, 16, 36)
#define VEC_HUMAN_HULL_MIN Vector (-16, -16, 0)
#define VEC_HUMAN_HULL_MAX Vector (16, 16, 72)
#define VEC_HUMAN_HULL_DUCK Vector (16, 16, 36)
#define VEC_HULL_MIN Vector(-16, -16, -36)
#define VEC_HULL_MAX Vector(16, 16, 36)
#define VEC_HUMAN_HULL_MIN Vector(-16, -16, 0)
#define VEC_HUMAN_HULL_MAX Vector(16, 16, 72)
#define VEC_HUMAN_HULL_DUCK Vector(16, 16, 36)
#define VEC_VIEW Vector (0, 0, 28)
#define VEC_VIEW Vector(0, 0, 28)
#define VEC_DUCK_HULL_MIN Vector (-16, -16, -18)
#define VEC_DUCK_HULL_MAX Vector (16, 16, 18)
#define VEC_DUCK_VIEW Vector (0, 0, 12)
#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18)
#define VEC_DUCK_HULL_MAX Vector(16, 16, 18)
#define VEC_DUCK_VIEW Vector(0, 0, 12)
#define SVC_TEMPENTITY 23
#define SVC_CENTERPRINT 26

View file

@ -1,97 +0,0 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// detects the build platform
#if defined(__linux__)
#define PLATFORM_LINUX
#elif defined(__APPLE__)
#define PLATFORM_OSX
#elif defined(_WIN32)
#define PLATFORM_WIN32
#endif
// by default sse has everyone
#define PLATFORM_HAS_SSE2
// detects the compiler
#if defined(_MSC_VER)
#define CXX_MSVC
#elif defined(__clang__)
#define CXX_CLANG
#endif
// configure export macros
#if defined(PLATFORM_WIN32)
#define SHARED_LIBRARAY_EXPORT extern "C" __declspec (dllexport)
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
#define SHARED_LIBRARAY_EXPORT extern "C" __attribute__ ((visibility ("default")))
#else
#error "Can't configure export macros. Compiler unrecognized."
#endif
// operating system specific macros, functions and typedefs
#ifdef PLATFORM_WIN32
#include <direct.h>
#include <string.h>
#define STD_CALL __stdcall
#define DLL_ENTRYPOINT int STD_CALL DllMain (HINSTANCE, DWORD dwReason, LPVOID)
#define DLL_DETACHING (dwReason == DLL_PROCESS_DETACH)
#define DLL_RETENTRY return TRUE
#if defined(CXX_MSVC) && !defined (_M_X64)
#define DLL_GIVEFNPTRSTODLL extern "C" void STD_CALL
#elif defined(CXX_CLANG) || defined (_M_X64)
#define DLL_GIVEFNPTRSTODLL SHARED_LIBRARAY_EXPORT void STD_CALL
#endif
// specify export parameter
#if defined(CXX_MSVC) || defined (CXX_CLANG)
#if !defined (_M_X64)
#pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1")
#endif
#pragma comment(linker, "/SECTION:.data,RW")
#endif
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#define DLL_ENTRYPOINT __attribute__ ((destructor)) void _fini (void)
#define DLL_DETACHING TRUE
#define DLL_RETENTRY return
#define DLL_GIVEFNPTRSTODLL extern "C" void __attribute__ ((visibility ("default")))
#define STD_CALL /* */
// android is a linux with a special cases
// @todo: sse should be working ok on x86 android?
#if defined(__ANDROID__)
#define PLATFORM_ANDROID
#if defined (__arm__) || defined (__aarch64__ )
#undef PLATFORM_HAS_SSE2
#endif
#endif
#else
#error "Platform unrecognized."
#endif

View file

@ -12,7 +12,7 @@
// general product information
#define PRODUCT_NAME "Yet Another POD-Bot"
#define PRODUCT_SHORT_NAME "YaPB"
#define PRODUCT_VERSION "2.91"
#define PRODUCT_VERSION "2.92"
#define PRODUCT_AUTHOR "YaPB Dev Team"
#define PRODUCT_URL "https://yapb.ru/"
#define PRODUCT_EMAIL "d@entix.io"
@ -26,7 +26,7 @@
#define PRODUCT_GIT_HASH "unspecified_hash"
#define PRODUCT_GIT_COMMIT_AUTHOR "unspecified_author"
#define PRODUCT_GIT_COMMIT_ID 0000
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 91
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 92
#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL, PRODUCT_GIT_COMMIT_ID
#define PRODUCT_SUPPORT_VERSION "Beta 6.6 - Condition Zero"
#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/"

File diff suppressed because it is too large Load diff

View file

@ -11,18 +11,19 @@ PROJECT = yapb
SOURCES = ../source
OBJECTS = $(wildcard $(SOURCES)/*.cpp)
COMPILER_FLAGS = -mtune=generic -std=c++11 -m32 -Wall -Wextra -Werror -fno-exceptions -fno-rtti -DPOSIX
LINKER_FLAGS = -m32
COMPILER_FLAGS = -std=c++11 -m32 -Wall -Wextra -Werror -fno-exceptions -fno-rtti
LINKER_FLAGS = -m32 -ldl
ifeq "$(DEBUG)" "true"
COMPILER_FLAGS += -D_DEBUG -DDEBUG -g3
COMPILER_FLAGS += -g3 -DCR_DEBUG
BINARY_DIR = debug
else
COMPILER_FLAGS += -DNDEBUG -pipe -O3 -msse2 -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden -nostdinc++
COMPILER_FLAGS += -pipe -O3 -march=core2 -msse2 -mfpmath=sse -ffast-math -fno-builtin -fno-threadsafe-statics -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fvisibility-inlines-hidden
BINARY_DIR = release
LINKER_FLAGS += -static-libgcc
endif
INCLUDE = -I../include -I../include/engine
INCLUDE = -I../include
COMPILER = $(CC)
ifeq "$(shell uname -s)" "Darwin"
@ -31,11 +32,10 @@ endif
ifeq "$(OSX)" "true"
LIBRARY_EXT = dylib
COMPILER_FLAGS += -DOSX -D_OSX -mmacosx-version-min=10.9
COMPILER_FLAGS += -mmacosx-version-min=10.9
LINKER_FLAGS += -dynamiclib -lstdc++ -mmacosx-version-min=10.9 -arch i386
else
LIBRARY_EXT = so
COMPILER_FLAGS += -DLINUX -D_LINUX
LINKER_FLAGS += -shared
endif
@ -45,16 +45,26 @@ ifeq ($(findstring clang,$(COMPILER)),clang)
ifeq "$(OSX)" "false"
LINKER_FLAGS += -lgcc_eh
else
LINKER_FLAGS += -nostdlib++ -Wunused-command-line-argument
ifeq "$(DEBUG)" "true"
LINKER_FLAGS += -lstdc++
else
LINKER_FLAGS += -nostdlib++ -Wunused-command-line-argument -fuse-ld=lld -Wl,-z,notext --no-undefined
endif
endif
else ifeq ($(findstring gcc,$(COMPILER)),gcc)
ifneq "$(OSX)" "false"
LINKER_FLAGS += -static-libgcc
COMPILER_FLAGS += -funroll-all-loops -Wno-implicit-fallthrough
ifneq "$(DEBUG)" "true"
LINKER_FLAGS += -Wl,--no-undefined -flto=thin
COMPILER_FLAGS += -funroll-all-loops -flto=thin
endif
endif
else ifeq ($(findstring icc,$(COMPILER)),icc)
COMPILER_FLAGS += -funroll-all-loops -no-prec-div -no-inline-min-size -no-inline-max-size -wd11076 -wd11074
LINKER_FLAGS += -cxxlib-nostd -static-intel -no-intel-extensions
LINKER_FLAGS += -static-intel -no-intel-extensions
ifneq "$(DEBUG)" "true"
COMPILER_FLAGS += -funroll-all-loops -ipo -wd11076 -wd11074
LINKER_FLAGS += -cxxlib-nostd -Wl,--no-undefined,-z,notext,--gc-sections -ipo
endif
endif
OBJECTS_BIN := $(OBJECTS:%.cpp=$(BINARY_DIR)/%.o)
@ -78,6 +88,7 @@ debug:
all:
$(MAKE) compile DEBUG=true
$(MAKE) compile DEBUG=false
clean:
rm -rf release/*.o
rm -rf release/$(BINARY_OUTPUT)

View file

@ -11,10 +11,29 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\compress.h" />
<ClInclude Include="..\include\crlib\cr-alloc.h" />
<ClInclude Include="..\include\crlib\cr-array.h" />
<ClInclude Include="..\include\crlib\cr-basic.h" />
<ClInclude Include="..\include\crlib\cr-binheap.h" />
<ClInclude Include="..\include\crlib\cr-color.h" />
<ClInclude Include="..\include\crlib\cr-complete.h" />
<ClInclude Include="..\include\crlib\cr-dict.h" />
<ClInclude Include="..\include\crlib\cr-files.h" />
<ClInclude Include="..\include\crlib\cr-http.h" />
<ClInclude Include="..\include\crlib\cr-lambda.h" />
<ClInclude Include="..\include\crlib\cr-library.h" />
<ClInclude Include="..\include\crlib\cr-logger.h" />
<ClInclude Include="..\include\crlib\cr-math.h" />
<ClInclude Include="..\include\crlib\cr-movable.h" />
<ClInclude Include="..\include\crlib\cr-platform.h" />
<ClInclude Include="..\include\crlib\cr-random.h" />
<ClInclude Include="..\include\crlib\cr-string.h" />
<ClInclude Include="..\include\crlib\cr-twin.h" />
<ClInclude Include="..\include\crlib\cr-ulz.h" />
<ClInclude Include="..\include\crlib\cr-uniqueptr.h" />
<ClInclude Include="..\include\crlib\cr-vector.h" />
<ClInclude Include="..\include\engine\model.h" />
<ClInclude Include="..\include\yapb.h" />
<ClInclude Include="..\include\corelib.h" />
<ClInclude Include="..\include\engine.h" />
<ClInclude Include="..\include\engine\const.h" />
<ClInclude Include="..\include\engine\eiface.h" />
@ -22,7 +41,6 @@
<ClInclude Include="..\include\engine\meta_api.h" />
<ClInclude Include="..\include\engine\progdefs.h" />
<ClInclude Include="..\include\engine\util.h" />
<ClInclude Include="..\include\platform.h" />
<ClInclude Include="..\include\resource.h" />
</ItemGroup>
<ItemGroup>
@ -35,12 +53,13 @@
<ClCompile Include="..\source\interface.cpp" />
<ClCompile Include="..\source\navigate.cpp" />
<ClCompile Include="..\source\support.cpp" />
<ClCompile Include="..\source\waypoint.cpp" />
<ClCompile Include="..\source\graph.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="yapb.rc" />
</ItemGroup>
<ItemGroup>
<None Include="..\source\Android.mk" />
<None Include="makefile" />
</ItemGroup>
<PropertyGroup Label="Globals">
@ -108,7 +127,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\include\engine;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader />
@ -181,7 +200,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<AdditionalIncludeDirectories>..\mmgr;..\include\engine;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>

View file

@ -13,14 +13,11 @@
<Filter Include="include\engine">
<UniqueIdentifier>{f98ff5ec-055a-46cd-b5b1-462ef4c1c73e}</UniqueIdentifier>
</Filter>
<Filter Include="include\crlib">
<UniqueIdentifier>{76a583d1-8f55-451b-8516-2f7cce4d1875}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\compress.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\corelib.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\resource.h">
<Filter>include</Filter>
</ClInclude>
@ -42,9 +39,6 @@
<ClInclude Include="..\include\engine\util.h">
<Filter>include\engine</Filter>
</ClInclude>
<ClInclude Include="..\include\platform.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\engine.h">
<Filter>include</Filter>
</ClInclude>
@ -54,6 +48,69 @@
<ClInclude Include="..\include\engine\model.h">
<Filter>include\engine</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-alloc.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-array.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-basic.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-binheap.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-complete.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-dict.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-files.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-http.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-lambda.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-library.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-logger.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-math.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-movable.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-platform.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-random.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-string.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-twin.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-ulz.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-uniqueptr.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-vector.h">
<Filter>include\crlib</Filter>
</ClInclude>
<ClInclude Include="..\include\crlib\cr-color.h">
<Filter>include\crlib</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\source\chatlib.cpp">
@ -68,9 +125,6 @@
<ClCompile Include="..\source\support.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\source\waypoint.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\source\manager.cpp">
<Filter>source</Filter>
</ClCompile>
@ -80,10 +134,13 @@
<ClCompile Include="..\source\engine.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\source\control.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\source\basecode.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\source\control.cpp">
<ClCompile Include="..\source\graph.cpp">
<Filter>source</Filter>
</ClCompile>
</ItemGroup>
@ -96,5 +153,8 @@
<None Include="makefile">
<Filter>project</Filter>
</None>
<None Include="..\source\Android.mk">
<Filter>project</Filter>
</None>
</ItemGroup>
</Project>

View file

@ -1,7 +1,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(XASH3D_CONFIG)
LOCAL_MODULE := yapb
@ -25,9 +24,10 @@ LOCAL_SRC_FILES := \
interface.cpp \
navigate.cpp \
support.cpp \
waypoint.cpp \
graph.cpp \
LOCAL_CFLAGS += -O2 -std=c++11 -DLINUX -D_LINUX -DPOSIX -pipe -fno-strict-aliasing -Wall -Werror
LOCAL_CFLAGS += -O3 -std=c++11 -DLINUX -D_LINUX -DPOSIX -pipe -fno-strict-aliasing -Wall -Werror -Wno-array-bounds
LOCAL_CPPFLAGS += -fno-exceptions -fno-rtti
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

File diff suppressed because it is too large Load diff

View file

@ -19,11 +19,11 @@ void BotUtils::stripTags (String &line) {
for (const auto &tag : m_tags) {
const size_t start = line.find (tag.first, 0);
if (start != String::INVALID_INDEX) {
if (start != String::kInvalidIndex) {
const size_t end = line.find (tag.second, start);
const size_t diff = end - start;
if (end != String::INVALID_INDEX && end > start && diff < 32 && diff > 4) {
if (end != String::kInvalidIndex && end > start && diff < 32 && diff > 4) {
line.erase (start, diff + tag.second.length ());
break;
}
@ -37,7 +37,7 @@ void BotUtils::humanizePlayerName (String &playerName) {
}
// drop tag marks, 80 percent of time
if (rng.chance (80)) {
if (rg.chance (80)) {
stripTags (playerName);
}
else {
@ -45,14 +45,14 @@ void BotUtils::humanizePlayerName (String &playerName) {
}
// sometimes switch name to lower characters, only valid for the english languge
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
if (rg.chance (8) && strcmp (yb_language.str (), "en") == 0) {
playerName.lowercase ();
}
}
void BotUtils::addChatErrors (String &line) {
// sometimes switch name to lower characters, only valid for the english languge
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
if (rg.chance (8) && strcmp (yb_language.str (), "en") == 0) {
line.lowercase ();
}
auto length = line.length ();
@ -61,13 +61,13 @@ void BotUtils::addChatErrors (String &line) {
size_t percentile = line.length () / 2;
// "length / 2" percent of time drop a character
if (rng.chance (percentile)) {
line.erase (rng.getInt (length / 8, length - length / 8));
if (rg.chance (percentile)) {
line.erase (rg.int_ (length / 8, length - length / 8), 1);
}
// "length" / 4 precent of time swap character
if (rng.chance (percentile / 2)) {
size_t pos = rng.getInt (length / 8, 3 * length / 8); // choose random position in string
if (rg.chance (percentile / 2)) {
size_t pos = rg.int_ (length / 8, 3 * length / 8); // choose random position in string
cr::swap (line[pos], line[pos + 1]);
}
}
@ -76,7 +76,7 @@ void BotUtils::addChatErrors (String &line) {
bool BotUtils::checkKeywords (const String &line, String &reply) {
// this function checks is string contain keyword, and generates reply to it
if (!yb_chat.boolean () || line.empty ()) {
if (!yb_chat.bool_ () || line.empty ()) {
return false;
}
@ -84,7 +84,7 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
for (const auto &keyword : factory.keywords) {
// check is keyword has occurred in message
if (line.find (keyword, 0) != String::INVALID_INDEX) {
if (line.find (keyword) != String::kInvalidIndex) {
StringArray &usedReplies = factory.usedReplies;
if (usedReplies.length () >= factory.replies.length () / 4) {
@ -113,11 +113,9 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
}
}
}
auto &chat = conf.getChat ();
// didn't find a keyword? 70% of the time use some universal reply
if (rng.chance (70) && !chat[CHAT_NOKW].empty ()) {
reply.assign (chat[CHAT_NOKW].random ());
if (rg.chance (70) && conf.hasChatBank (Chat::NoKeyword)) {
reply.assign (conf.pickRandomFromChatBank (Chat::NoKeyword));
return true;
}
return false;
@ -126,47 +124,49 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
void Bot::prepareChatMessage (const String &message) {
// this function parses messages from the botchat, replaces keywords and converts names into a more human style
if (!yb_chat.boolean () || message.empty ()) {
if (!yb_chat.bool_ () || message.empty ()) {
return;
}
m_chatBuffer = message;
m_chatBuffer.assign (message.chars ());
// must be called before return or on the end
auto finishPreparation = [&] (void) {
auto finishPreparation = [&] () {
if (!m_chatBuffer.empty ()) {
util.addChatErrors (m_chatBuffer);
}
};
// need to check if we're have special symbols
size_t pos = message.find ('%', 0);
size_t pos = message.find ('%');
// nothing found, bail out
if (pos == String::INVALID_INDEX || pos >= message.length ()) {
if (pos == String::kInvalidIndex || pos >= message.length ()) {
finishPreparation ();
return;
}
// get the humanized name out of client
auto humanizedName = [] (const Client &client) -> String {
if (!util.isPlayer (client.ent)) {
return cr::move (String ("unknown"));
auto humanizedName = [] (int index) -> String {
auto ent = game.playerOfIndex (index);
if (!util.isPlayer (ent)) {
return "unknown";
}
String playerName = STRING (client.ent->v.netname);
String playerName = STRING (ent->v.netname);
util.humanizePlayerName (playerName);
return cr::move (playerName);
return playerName;
};
// find highfrag player
auto getHighfragPlayer = [&] (void) -> String {
auto getHighfragPlayer = [&] () -> String {
int highestFrags = -1;
int index = 0;
for (int i = 0; i < game.maxClients (); i++) {
for (int i = 0; i < game.maxClients (); ++i) {
const Client &client = util.getClient (i);
if (!(client.flags & CF_USED) || client.ent == ent ()) {
if (!(client.flags & ClientFlags::Used) || client.ent == ent ()) {
continue;
}
int frags = static_cast <int> (client.ent->v.frags);
@ -176,147 +176,116 @@ void Bot::prepareChatMessage (const String &message) {
index = i;
}
}
return humanizedName (util.getClient (index));
return humanizedName (index);
};
// get roundtime
auto getRoundTime = [] (void) -> String {
auto getRoundTime = [] () -> String {
auto roundTimeSecs = static_cast <int> (bots.getRoundEndTime () - game.timebase ());
String roundTime;
roundTime.assign ("%02d:%02d", cr::clamp (roundTimeSecs / 60, 0, 59), cr::clamp (cr::abs (roundTimeSecs % 60), 0, 59));
roundTime.assignf ("%02d:%02d", cr::clamp (roundTimeSecs / 60, 0, 59), cr::clamp (cr::abs (roundTimeSecs % 60), 0, 59));
return cr::move (roundTime);
return roundTime;
};
// get bot's victim
auto getMyVictim = [&] (void) -> String {
for (const Client &client : util.getClients ()) {
if (client.ent == m_lastVictim) {
return humanizedName (client);
}
}
return cr::move (String ("unknown"));
auto getMyVictim = [&] () -> String {;
return humanizedName (game.indexOfPlayer (m_lastVictim));
};
// get the game name alias
auto getGameName = [] (void) -> String {
auto getGameName = [] () -> String {
String gameName;
if (game.is (GAME_CZERO)) {
if (rng.chance (30)) {
if (game.is (GameFlags::ConditionZero)) {
if (rg.chance (30)) {
gameName = "CZ";
}
else {
gameName = "Condition Zero";
}
}
else if (game.is (GAME_CSTRIKE16) || game.is (GAME_LEGACY)) {
if (rng.chance (30)) {
else if (game.is (GameFlags::Modern) || game.is (GameFlags::Legacy)) {
if (rg.chance (30)) {
gameName = "CS";
}
else {
gameName = "Counter-Strike";
}
}
return cr::move (gameName);
return gameName;
};
// get enemy or teammate alive
auto getPlayerAlive = [&] (bool needsEnemy) -> String {
int index;
for (index = 0; index < game.maxClients (); index++) {
const Client &client = util.getClient (index);
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == ent ()) {
for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) {
continue;
}
if ((needsEnemy && m_team == client.team) || (!needsEnemy && m_team != client.team)) {
continue;
if (needsEnemy && m_team != client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
}
else if (!needsEnemy && m_team == client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
}
break;
}
return "UnknowPA";
};
size_t replaceCounter = 0;
if (index < game.maxClients ()) {
if (!needsEnemy && util.isPlayer (pev->dmg_inflictor) && m_team == game.getTeam (pev->dmg_inflictor)) {
return humanizedName (util.getClient (game.indexOfEntity (pev->dmg_inflictor) - 1));
while (replaceCounter < 6 && (pos = m_chatBuffer.find ('%')) != String::kInvalidIndex) {
// found one, let's do replace
switch (message[pos + 1]) {
// the highest frag player
case 'f':
m_chatBuffer.replace ("%f", getHighfragPlayer ());
break;
// current map name
case 'm':
m_chatBuffer.replace ("%m", game.getMapName ());
break;
// round time
case 'r':
m_chatBuffer.replace ("%r", getRoundTime ());
break;
// chat reply
case 's':
if (m_sayTextBuffer.entityIndex != -1) {
m_chatBuffer.replace ("%s", humanizedName (m_sayTextBuffer.entityIndex));
}
else {
return humanizedName (util.getClient (index));
m_chatBuffer.replace ("%s", getHighfragPlayer ());
}
}
else {
for (index = 0; index < game.maxClients (); index++) {
const Client &client = util.getClient (index);
break;
if (!(client.flags & CF_USED) || client.team != m_team || client.ent == ent ()) {
continue;
}
// last bot victim
case 'v':
m_chatBuffer.replace ("%v", getMyVictim ());
break;
if ((needsEnemy && m_team != client.team) || (!needsEnemy && m_team == client.team)) {
continue;
}
break;
}
// game name
case 'd':
m_chatBuffer.replace ("%d", getGameName ());
break;
if (index < game.maxClients ()) {
return humanizedName (util.getClient (index));
}
}
return cr::move (String ("unknown"));
};
// teammate alive
case 't':
m_chatBuffer.replace ("%t", getPlayerAlive (false));
break;
// found one, let's do replace
switch (message[pos + 1]) {
// the highest frag player
case 'f':
m_chatBuffer.replace ("%f", getHighfragPlayer ());
break;
// current map name
case 'm':
m_chatBuffer.replace ("%m", game.getMapName ());
break;
// round time
case 'r':
m_chatBuffer.replace ("%r", getRoundTime ());
break;
// chat reply
case 's':
if (m_sayTextBuffer.entityIndex != -1) {
m_chatBuffer.replace ("%s", humanizedName (util.getClient (m_sayTextBuffer.entityIndex)));
}
else {
m_chatBuffer.replace ("%s", getHighfragPlayer ());
}
break;
// last bot victim
case 'v':
m_chatBuffer.replace ("%v", getMyVictim ());
break;
// game name
case 'd':
m_chatBuffer.replace ("%d", getGameName ());
break;
// teammate alive
case 't':
m_chatBuffer.replace ("%t", getPlayerAlive (false));
break;
// enemy alive
case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true));
break;
};
// enemy alive
case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true));
break;
};
replaceCounter++;
}
finishPreparation ();
}
@ -327,18 +296,17 @@ bool Bot::checkChatKeywords (String &reply) {
return util.checkKeywords (message.uppercase (), reply);
}
bool Bot::isReplyingToChat (void) {
bool Bot::isReplyingToChat () {
// this function sends reply to a player
if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) {
// check is time to chat is good
if (m_sayTextBuffer.timeNextChat < game.timebase ()) {
if (m_sayTextBuffer.timeNextChat < game.timebase () + rg.float_ (m_sayTextBuffer.chatDelay / 2, m_sayTextBuffer.chatDelay)) {
String replyText;
if (rng.chance (m_sayTextBuffer.chatProbability + rng.getInt (25, 45)) && checkChatKeywords (replyText)) {
if (rg.chance (m_sayTextBuffer.chatProbability + rg.int_ (20, 50)) && checkChatKeywords (replyText)) {
prepareChatMessage (replyText);
pushMsgQueue (GAME_MSG_SAY_CMD);
pushMsgQueue (BotMsg::Say);
m_sayTextBuffer.entityIndex = -1;
m_sayTextBuffer.timeNextChat = game.timebase () + m_sayTextBuffer.chatDelay;
@ -353,19 +321,17 @@ bool Bot::isReplyingToChat (void) {
return false;
}
void Bot::checkForChat (void) {
void Bot::checkForChat () {
// say a text every now and then
if (rng.chance (35) || m_notKilled || !yb_chat.boolean ()) {
if (rg.chance (30) || m_notKilled || !yb_chat.bool_ ()) {
return;
}
// bot chatting turned on?
if (m_lastChatTime + 10.0 < game.timebase () && bots.getLastChatTimestamp () + 5.0f < game.timebase () && !isReplyingToChat ()) {
auto &chat = conf.getChat ();
if (!chat[CHAT_DEAD].empty ()) {
const String &phrase = chat[CHAT_DEAD].random ();
if (m_lastChatTime + rg.float_ (6.0f, 10.0f) < game.timebase () && bots.getLastChatTimestamp () + rg.float_ (2.5f, 5.0f) < game.timebase () && !isReplyingToChat ()) {
if (conf.hasChatBank (Chat::Dead)) {
const auto &phrase = conf.pickRandomFromChatBank (Chat::Dead);
bool sayBufferExists = false;
// search for last messages, sayed
@ -375,9 +341,10 @@ void Bot::checkForChat (void) {
break;
}
}
if (!sayBufferExists) {
prepareChatMessage (phrase);
pushMsgQueue (GAME_MSG_SAY_CMD);
pushMsgQueue (BotMsg::Say);
m_lastChatTime = game.timebase ();
bots.setLastChatTimestamp (game.timebase ());
@ -388,7 +355,7 @@ void Bot::checkForChat (void) {
}
// clear the used line buffer every now and then
if (static_cast <int> (m_sayTextBuffer.lastUsedSentences.length ()) > rng.getInt (4, 6)) {
if (static_cast <int> (m_sayTextBuffer.lastUsedSentences.length ()) > rg.int_ (4, 6)) {
m_sayTextBuffer.lastUsedSentences.clear ();
}
}
@ -397,17 +364,17 @@ void Bot::checkForChat (void) {
void Bot::say (const char *text) {
// this function prints saytext message to all players
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
return;
}
game.execBotCmd (ent (), "say \"%s\"", text);
game.botCommand (ent (), "say \"%s\"", text);
}
void Bot::sayTeam (const char *text) {
// this function prints saytext message only for teammates
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
return;
}
game.execBotCmd (ent (), "say_team \"%s\"", text);
game.botCommand (ent (), "say_team \"%s\"", text);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2940
source/graph.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@
#include <yapb.h>
ConVar yb_version ("yb_version", PRODUCT_VERSION, VT_READONLY);
ConVar yb_version ("yb_version", PRODUCT_VERSION, Var::ReadOnly);
gamefuncs_t dllapi;
enginefuncs_t engfuncs;
@ -33,7 +33,7 @@ plugin_info_t Plugin_info = {
PT_ANYTIME, // when unloadable
};
namespace VariadicCallbacks {
namespace variadic {
void clientCommand (edict_t *ent, char const *format, ...) {
// this function forces the client whose player entity is ent to issue a client command.
// How it works is that clients all have a argv global string in their client DLL that
@ -50,31 +50,31 @@ namespace VariadicCallbacks {
// case it's a bot asking for a client command, we handle it like we do for bot commands
va_list ap;
char buffer[MAX_PRINT_BUFFER];
auto buffer = strings.chars ();
va_start (ap, format);
_vsnprintf (buffer, cr::bufsize (buffer), format, ap);
_vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
va_end (ap);
if (ent && (ent->v.flags & (FL_FAKECLIENT | FL_DORMANT))) {
if (bots.getBot (ent)) {
game.execBotCmd (ent, buffer);
if (bots[ent]) {
game.botCommand (ent, buffer);
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands
}
return;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnClientCommand (ent, buffer);
}
}
SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or
// what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can
// be called by the engine, into a memory block pointed to by the functionTable pointer
@ -87,13 +87,12 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
memset (functionTable, 0, sizeof (gamefuncs_t));
if (!(game.is (GAME_METAMOD))) {
auto api_GetEntityAPI = game.getLib ().resolve <int (*) (gamefuncs_t *, int)> ("GetEntityAPI");
if (!(game.is (GameFlags::Metamod))) {
auto api_GetEntityAPI = game.lib ().resolve <int (*) (gamefuncs_t *, int)> ("GetEntityAPI");
// pass other DLLs engine callbacks to function table...
if (api_GetEntityAPI (&dllapi, INTERFACE_VERSION) == 0) {
util.logEntry (true, LL_FATAL, "GetEntityAPI2: ERROR - Not Initialized.");
return FALSE; // error initializing function table!!!
if (!api_GetEntityAPI || api_GetEntityAPI (&dllapi, INTERFACE_VERSION) == 0) {
logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", "GetEntityAPI");
}
dllfuncs.dllapi_table = &dllapi;
gpGamedllFuncs = &dllfuncs;
@ -101,7 +100,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
memcpy (functionTable, &dllapi, sizeof (gamefuncs_t));
}
functionTable->pfnGameInit = [] (void) {
functionTable->pfnGameInit = [] () {
// this function is a one-time call, and appears to be the second function called in the
// DLL after GiveFntprsToDll() has been called. Its purpose is to tell the MOD DLL to
// initialize the game before the engine actually hooks into it with its video frames and
@ -110,6 +109,14 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// server is enabled. Here is a good place to do our own game session initialization, and
// to register by the engine side the server commands we need to administrate our bots.
// register bot cvars
game.registerCvars ();
// register logger
logger.initialize (strings.format ("%slogs/yapb.log", graph.getDataDirectory (false)), [] (const char *msg) {
game.print (msg);
});
conf.initWeapons ();
// register server command(s)
@ -117,20 +124,20 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
game.registerCmd ("yb", BotControl::handleEngineCommands);
// set correct version string
yb_version.set (util.format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, util.buildNumber ()));
yb_version.set (strings.format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, util.buildNumber ()));
// execute main config
conf.load (true);
conf.loadMainConfig ();
// register fake metamod command handler if we not! under mm
if (!(game.is (GAME_METAMOD))) {
game.registerCmd ("meta", [] (void) {
if (!(game.is (GameFlags::Metamod))) {
game.registerCmd ("meta", [] () {
game.print ("You're launched standalone version of yapb. Metamod is not installed or not enabled!");
});
}
conf.adjustWeaponPrices ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnGameInit ();
@ -144,7 +151,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
game.precache ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
int result = dllapi.pfnSpawn (ent); // get result
@ -168,7 +175,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// is called twice, once for each entity moving.
if (!game.isNullEntity (pentTouched) && pentOther != game.getStartEntity ()) {
Bot *bot = bots.getBot (pentTouched);
auto bot = bots[pentTouched];
if (bot != nullptr && pentOther != bot->ent ()) {
@ -181,7 +188,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnTouch (pentTouched, pentOther);
@ -212,13 +219,13 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
if (strcmp (addr, "loopback") == 0) {
game.setLocalEntity (ent); // save the edict of the listen server client...
// if not dedicated set the default editor for waypoints
// if not dedicated set the default editor for graph
if (!game.isDedicated ()) {
waypoints.setEditor (ent);
graph.setEditor (ent);
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
return dllapi.pfnClientConnect (ent, name, addr, rejectReason);
@ -236,18 +243,18 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// to reset his entity pointer for safety. There are still a few server frames to go once a
// listen server client disconnects, and we don't want to send him any sort of message then.
int index = game.indexOfEntity (ent) - 1;
if (index >= 0 && index < MAX_ENGINE_PLAYERS) {
auto bot = bots.getBot (index);
// check if its a bot
if (bot != nullptr && bot->pev == &ent->v) {
for (auto &bot : bots) {
if (bot->pev == &ent->v) {
bot->showChaterIcon (false);
bots.destroy (index);
conf.clearUsedName (bot.get ()); // clear the bot name
bots.erase (bot.get ()); // remove the bot from bots array
break;
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientDisconnect (ent);
@ -261,7 +268,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
ctrl.assignAdminRights (ent, infobuffer);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientUserInfoChanged (ent, infobuffer);
@ -282,14 +289,14 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// clients. Hence it can lack of commenting a bit, since this code is very subject to change.
if (ctrl.handleClientCommands (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
}
else if (ctrl.handleMenuCommands (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
@ -298,7 +305,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// record stuff about radio and chat
bots.captureChatRadio (engfuncs.pfnCmd_Argv (0), engfuncs.pfnCmd_Argv (1), ent);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientCommand (ent);
@ -312,8 +319,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// loading the bot profiles, and drawing the world map (ie, filling the navigation hashtable).
// Once this function has been called, the server can be considered as "running".
bots.destroy ();
conf.load (false); // initialize all config files
conf.loadConfigs (); // initialize all config files
// do a level initialization
game.levelInitialize (pentEdictList, edictCount);
@ -322,27 +328,26 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
illum.resetWorldModel ();
// do level initialization stuff here...
waypoints.init ();
waypoints.load ();
graph.loadGraphData ();
// execute main config
conf.load (true);
conf.loadMainConfig ();
if (File::exists (util.format ("%s/maps/%s_yapb.cfg", game.getModName (), game.getMapName ()))) {
game.execCmd ("exec maps/%s_yapb.cfg", game.getMapName ());
if (File::exists (strings.format ("%s/maps/%s_yapb.cfg", game.getModName (), game.getMapName ()))) {
game.serverCommand ("exec maps/%s_yapb.cfg", game.getMapName ());
game.print ("Executing Map-Specific config file");
}
bots.initQuota ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnServerActivate (pentEdictList, edictCount, clientMax);
waypoints.rebuildVisibility ();
graph.rebuildVisibility ();
};
functionTable->pfnServerDeactivate = [] (void) {
functionTable->pfnServerDeactivate = [] () {
// this function is called when the server is shutting down. A particular note about map
// changes: changing the map means shutting down the server and starting a new one. Of course
// this process is transparent to the user, but either in single player when the hero reaches
@ -354,8 +359,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// the loading of new bots and the new BSP data parsing there.
// save collected experience on shutdown
waypoints.saveExperience ();
waypoints.saveVisibility ();
graph.savePractice ();
// destroy global killer entity
bots.destroyKillerEntity ();
@ -370,19 +374,21 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
util.setNeedForWelcome (false);
// xash is not kicking fakeclients on changelevel
if (game.is (GAME_XASH_ENGINE)) {
if (game.is (GameFlags::Xash3D)) {
bots.kickEveryone (true, false);
bots.destroy ();
}
waypoints.init ();
graph.initGraph ();
if (game.is (GAME_METAMOD)) {
// clear all the bots
bots.destroy ();
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnServerDeactivate ();
};
functionTable->pfnStartFrame = [] (void) {
functionTable->pfnStartFrame = [] () {
// this function starts a video frame. It is called once per video frame by the game. If
// you run Half-Life at 90 fps, this function will then be called 90 times per second. By
// placing a hook on it, we have a good place to do things that should be done continuously
@ -400,10 +406,9 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// update some stats for clients
util.updateClients ();
if (waypoints.hasEditFlag (WS_EDIT_ENABLED) && waypoints.hasEditor ()) {
waypoints.frame ();
if (graph.hasEditFlag (GraphEdit::On) && graph.hasEditor ()) {
graph.frame ();
}
bots.updateDeathMsgState (false);
// run stuff periodically
game.slowFrame ();
@ -419,7 +424,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// keep bot number up to date
bots.maintainQuota ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnStartFrame ();
@ -428,17 +433,24 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
bots.slowFrame ();
};
functionTable->pfnUpdateClientData = [] (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd) {
extern ConVar yb_latency_display;
functionTable->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) {
auto ent = const_cast <edict_t *> (player);
if (game.is (GAME_SUPPORT_SVC_PINGS) && yb_latency_display.integer () == 2 && bots.hasBotsOnline ()) {
bots.sendPingOffsets (const_cast <edict_t *> (ent));
// if we're handle pings for bots and clients, clear IN_SCORE button so SV_ShouldUpdatePing engine function return false
// and SV_EmitPings will not overwrite our results
if (game.is (GameFlags::HasFakePings) && yb_show_latency.int_ () == 2) {
if ((cmd->buttons & IN_SCORE) || (ent->v.oldbuttons & IN_SCORE)) {
cmd->buttons &= ~IN_SCORE;
// send our version of pings
util.sendPings (ent);
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnUpdateClientData (ent, sendweapons, cd);
dllapi.pfnCmdStart (player, cmd, random_seed);
};
functionTable->pfnPM_Move = [] (playermove_t *playerMove, int server) {
@ -449,7 +461,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
illum.setWorldModel (playerMove->physents[0].model);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnPM_Move (playerMove, server);
@ -457,8 +469,8 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *) {
// this function is called right after FuncPointers_t() by the engine in the game DLL (or
CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) {
// this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or
// what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can
// be called by the engine, into a memory block pointed to by the functionTable pointer
// that is passed into this function (explanation comes straight from botman). This allows
@ -468,9 +480,9 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
// engine, and then calls the MOD DLL's version of GetEntityAPI to get the REAL gamedll
// functions this time (to use in the bot code). Post version, called only by metamod.
memset (functionTable, 0, sizeof (gamefuncs_t));
memset (table, 0, sizeof (gamefuncs_t));
functionTable->pfnSpawn = [] (edict_t *ent) {
table->pfnSpawn = [] (edict_t *ent) {
// this function asks the game DLL to spawn (i.e, give a physical existence in the virtual
// world, in other words to 'display') the entity pointed to by ent in the game. The
// Spawn() function is one of the functions any entity is supposed to have in the game DLL,
@ -484,7 +496,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
RETURN_META_VALUE (MRES_IGNORED, 0);
};
functionTable->pfnStartFrame = [] (void) {
table->pfnStartFrame = [] () {
// this function starts a video frame. It is called once per video frame by the game. If
// you run Half-Life at 90 fps, this function will then be called 90 times per second. By
// placing a hook on it, we have a good place to do things that should be done continuously
@ -496,7 +508,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
RETURN_META (MRES_IGNORED);
};
functionTable->pfnServerActivate = [] (edict_t *, int, int) {
table->pfnServerActivate = [] (edict_t *, int, int) {
// this function is called when the server has fully loaded and is about to manifest itself
// on the network as such. Since a mapchange is actually a server shutdown followed by a
// restart, this function is also called when a new map is being loaded. Hence it's the
@ -505,7 +517,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
// Once this function has been called, the server can be considered as "running". Post version
// called only by metamod.
waypoints.rebuildVisibility ();
graph.rebuildVisibility ();
RETURN_META (MRES_IGNORED);
};
@ -513,21 +525,21 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) {
CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) {
// it appears that an extra function table has been added in the engine to gamedll interface
// since the date where the first enginefuncs table standard was frozen. These ones are
// facultative and we don't hook them, but since some MODs might be featuring it, we have to
// pass them too, else the DLL interfacing wouldn't be complete and the game possibly wouldn't
// run properly.
auto api_GetNewDLLFunctions = game.getLib ().resolve <int (*) (newgamefuncs_t *, int *)> ("GetNewDLLFunctions");
auto api_GetNewDLLFunctions = game.lib ().resolve <int (*) (newgamefuncs_t *, int *)> (__FUNCTION__);
if (api_GetNewDLLFunctions == nullptr) {
return FALSE;
}
if (!api_GetNewDLLFunctions (functionTable, interfaceVersion)) {
util.logEntry (true, LL_ERROR, "GetNewDLLFunctions: ERROR - Not Initialized.");
if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (functionTable, interfaceVersion)) {
logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__);
return FALSE;
}
@ -535,8 +547,8 @@ SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, in
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
if (game.is (GAME_METAMOD)) {
CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
if (game.is (GameFlags::Metamod)) {
memset (functionTable, 0, sizeof (enginefuncs_t));
}
@ -551,10 +563,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// spawn point named "tr_2lm".
// save collected experience on map change
waypoints.saveExperience ();
waypoints.saveVisibility ();
graph.savePractice ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnChangeLevel (s1, s2);
@ -565,7 +576,7 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
illum.updateLight (style, val);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnLightStyle (style, val);
@ -573,11 +584,11 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) {
// round starts in counter-strike 1.5
if ((game.is (GAME_LEGACY)) && strcmp (value, "info_map_parameters") == 0) {
if ((game.is (GameFlags::Legacy)) && strcmp (value, "info_map_parameters") == 0) {
bots.initRound ();
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <edict_t *> (nullptr));
}
return engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value);
@ -596,7 +607,7 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
util.attachSoundsToClients (entity, sample, volume);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnEmitSound (entity, channel, sample, volume, attenuation, flags, pitch);
@ -607,29 +618,26 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
game.beginMessage (ed, msgDest, msgType);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnMessageBegin (msgDest, msgType, origin, ed);
};
functionTable->pfnMessageEnd = [] (void) {
functionTable->pfnMessageEnd = [] () {
game.resetMessages ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnMessageEnd ();
// send latency fix
bots.sendDeathMsgFix ();
};
functionTable->pfnWriteByte = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteByte (value);
@ -637,9 +645,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteChar = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteChar (value);
@ -647,9 +655,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteShort = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteShort (value);
@ -657,9 +665,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteLong = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteLong (value);
@ -667,9 +675,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteAngle = [] (float value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteAngle (value);
@ -677,9 +685,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteCoord = [] (float value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteCoord (value);
@ -687,9 +695,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteString = [] (const char *sz) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) sz);
game.processMessages (reinterpret_cast <void *> (const_cast <char *> (sz)));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteString (sz);
@ -697,9 +705,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteEntity = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteEntity (value);
@ -716,76 +724,76 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// using pfnMessageBegin (), it will know what message ID number to send, and the engine will
// know what to do, only for non-metamod version
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
int message = engfuncs.pfnRegUserMsg (name, size);
if (strcmp (name, "VGUIMenu") == 0) {
game.setMessageId (NETMSG_VGUI, message);
game.setMessageId (NetMsg::VGUI, message);
}
else if (strcmp (name, "ShowMenu") == 0) {
game.setMessageId (NETMSG_SHOWMENU, message);
game.setMessageId (NetMsg::ShowMenu, message);
}
else if (strcmp (name, "WeaponList") == 0) {
game.setMessageId (NETMSG_WEAPONLIST, message);
game.setMessageId (NetMsg::WeaponList, message);
}
else if (strcmp (name, "CurWeapon") == 0) {
game.setMessageId (NETMSG_CURWEAPON, message);
game.setMessageId (NetMsg::CurWeapon, message);
}
else if (strcmp (name, "AmmoX") == 0) {
game.setMessageId (NETMSG_AMMOX, message);
game.setMessageId (NetMsg::AmmoX, message);
}
else if (strcmp (name, "AmmoPickup") == 0) {
game.setMessageId (NETMSG_AMMOPICKUP, message);
game.setMessageId (NetMsg::AmmoPickup, message);
}
else if (strcmp (name, "Damage") == 0) {
game.setMessageId (NETMSG_DAMAGE, message);
game.setMessageId (NetMsg::Damage, message);
}
else if (strcmp (name, "Money") == 0) {
game.setMessageId (NETMSG_MONEY, message);
game.setMessageId (NetMsg::Money, message);
}
else if (strcmp (name, "StatusIcon") == 0) {
game.setMessageId (NETMSG_STATUSICON, message);
game.setMessageId (NetMsg::StatusIcon, message);
}
else if (strcmp (name, "DeathMsg") == 0) {
game.setMessageId (NETMSG_DEATH, message);
game.setMessageId (NetMsg::DeathMsg, message);
}
else if (strcmp (name, "ScreenFade") == 0) {
game.setMessageId (NETMSG_SCREENFADE, message);
game.setMessageId (NetMsg::ScreenFade, message);
}
else if (strcmp (name, "HLTV") == 0) {
game.setMessageId (NETMSG_HLTV, message);
game.setMessageId (NetMsg::HLTV, message);
}
else if (strcmp (name, "TextMsg") == 0) {
game.setMessageId (NETMSG_TEXTMSG, message);
game.setMessageId (NetMsg::TextMsg, message);
}
else if (strcmp (name, "TeamInfo") == 0) {
game.setMessageId (NETMSG_TEAMINFO, message);
game.setMessageId (NetMsg::TeamInfo, message);
}
else if (strcmp (name, "BarTime") == 0) {
game.setMessageId (NETMSG_BARTIME, message);
game.setMessageId (NetMsg::BarTime, message);
}
else if (strcmp (name, "SendAudio") == 0) {
game.setMessageId (NETMSG_SENDAUDIO, message);
game.setMessageId (NetMsg::SendAudio, message);
}
else if (strcmp (name, "SayText") == 0) {
game.setMessageId (NETMSG_SAYTEXT, message);
game.setMessageId (NetMsg::SayText, message);
}
else if (strcmp (name, "BotVoice") == 0) {
game.setMessageId (NETMSG_BOTVOICE, message);
game.setMessageId (NetMsg::BotVoice, message);
}
else if (strcmp (name, "NVGToggle") == 0) {
game.setMessageId (NETMSG_NVGTOGGLE, message);
game.setMessageId (NetMsg::NVGToggle, message);
}
else if (strcmp (name, "FlashBat") == 0) {
game.setMessageId (NETMSG_FLASHBAT, message);
game.setMessageId (NetMsg::FlashBat, message);
}
else if (strcmp (name, "Flashlight") == 0) {
game.setMessageId (NETMSG_FLASHLIGHT, message);
game.setMessageId (NetMsg::Fashlight, message);
}
else if (strcmp (name, "ItemStatus") == 0) {
game.setMessageId (NETMSG_ITEMSTATUS, message);
game.setMessageId (NetMsg::ItemStatus, message);
}
return message;
};
@ -798,19 +806,19 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// we know, right ? But since stupidity rules this world, we do a preventive check :)
if (util.isFakeClient (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnClientPrintf (ent, printType, message);
};
functionTable->pfnCmd_Args = [] (void) {
functionTable->pfnCmd_Args = [] () {
// this function returns a pointer to the whole current client command string. Since bots
// have no client DLL and we may want a bot to execute a client command, we had to implement
// a argv string in the bot DLL for holding the bots' commands, and also keep track of the
@ -820,13 +828,13 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgs ());
}
return game.botArgs (); // else return the whole bot client command string we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <const char *> (nullptr));
}
return engfuncs.pfnCmd_Args (); // ask the client command string to the engine
@ -842,19 +850,19 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgv (argc));
}
return game.botArgv (argc); // if so, then return the wanted argument we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <const char *> (nullptr));
}
return engfuncs.pfnCmd_Argv (argc); // ask the argument number "argc" to the engine
};
functionTable->pfnCmd_Argc = [] (void) {
functionTable->pfnCmd_Argc = [] () {
// this function returns the number of arguments the current client command string has. Since
// bots have no client DLL and we may want a bot to execute a client command, we had to
// implement a argv string in the bot DLL for holding the bots' commands, and also keep
@ -864,61 +872,52 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgc ());
}
return game.botArgc (); // if so, then return the argument count we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
return engfuncs.pfnCmd_Argc (); // ask the engine how many arguments there are
};
functionTable->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) {
Bot *bot = bots.getBot (const_cast <edict_t *> (ent));
auto bot = bots[const_cast <edict_t *> (ent)];
// check wether it's not a bot
if (bot != nullptr) {
bot->pev->maxspeed = newMaxspeed;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnSetClientMaxspeed (ent, newMaxspeed);
};
functionTable->pfnClientCommand = variadic::clientCommand;
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEngineFunctions_Post (enginefuncs_t *functionTable, int *) {
memset (functionTable, 0, sizeof (enginefuncs_t));
functionTable->pfnMessageEnd = [] (void) {
// send latency fix
bots.sendDeathMsgFix ();
RETURN_META (MRES_IGNORED);
};
return TRUE;
}
SHARED_LIBRARAY_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) {
CR_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) {
// this function synchronizes the studio model animation blending interface (i.e, what parts
// of the body move, which bones, which hitboxes and how) between the server and the game DLL.
// some MODs can be using a different hitbox scheme than the standard one.
auto api_GetBlendingInterface = game.getLib ().resolve <int (*) (int, void **, void *, float(*)[3][4], float(*)[128][3][4])> ("Server_GetBlendingInterface");
auto api_GetBlendingInterface = game.lib ().resolve <int (*) (int, void **, void *, float(*)[3][4], float(*)[128][3][4])> (__FUNCTION__);
if (api_GetBlendingInterface == nullptr) {
if (!api_GetBlendingInterface) {
logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__);
return FALSE;
}
return api_GetBlendingInterface (version, ppinterface, pstudio, rotationmatrix, bonetransform);
}
SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) {
CR_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) {
// this function is the first function ever called by metamod in the plugin DLL. Its purpose
// is for metamod to retrieve basic information about the plugin, such as its meta-interface
// version, for ensuring compatibility with the current version of the running metamod.
@ -929,7 +928,7 @@ SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_
return TRUE; // tell metamod this plugin looks safe
}
SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
CR_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
// this function is called when metamod attempts to load the plugin. Since it's the place
// where we can tell if the plugin will be allowed to run or not, we wait until here to make
// our initialization stuff, like registering CVARs and dedicated server commands.
@ -943,7 +942,7 @@ SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *function
nullptr, // pfnGetNewDLLFunctions ()
nullptr, // pfnGetNewDLLFunctions_Post ()
GetEngineFunctions, // pfnGetEngineFunctions ()
GetEngineFunctions_Post, // pfnGetEngineFunctions_Post ()
nullptr, // pfnGetEngineFunctions_Post ()
};
// keep track of the pointers to engine function tables metamod gives us
@ -954,23 +953,44 @@ SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *function
return TRUE; // returning true enables metamod to attach this plugin
}
SHARED_LIBRARAY_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
CR_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
// this function is called when metamod unloads the plugin. A basic check is made in order
// to prevent unloading the plugin if its processing should not be interrupted.
bots.kickEveryone (true); // kick all bots off this server
waypoints.init ();
// save collected experience on shutdown
graph.savePractice ();
return TRUE;
}
SHARED_LIBRARAY_EXPORT void Meta_Init (void) {
CR_EXPORT void Meta_Init () {
// this function is called by metamod, before any other interface functions. Purpose of this
// function to give plugin a chance to determine is plugin running under metamod or not.
game.addGameFlag (GAME_METAMOD);
game.addGameFlag (GameFlags::Metamod);
}
// games GiveFnptrsToDll is a bit tricky
#if defined(CR_WINDOWS)
# if defined(CR_CXX_MSVC) || defined (CR_CXX_MSVC)
# if defined (CR_ARCH_X86)
# pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1")
# endif
# pragma comment(linker, "/SECTION:.data,RW")
# endif
# define DLL_STDCALL __stdcall
# if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL extern "C" void DLL_STDCALL
# elif defined(CR_CXX_CLANG) || defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void DLL_STDCALL
# endif
#elif defined(CR_LINUX) || defined (CR_OSX) || defined (CR_ANDROID)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void
# define DLL_STDCALL
#endif
DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) {
// this is the very first function that is called in the game DLL by the game. Its purpose
// is to set the functions interfacing up, by exchanging the functionTable function list
@ -990,30 +1010,22 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t
if (game.postload ()) {
return;
}
auto api_GiveFnptrsToDll = game.getLib ().resolve <void (STD_CALL *) (enginefuncs_t *, globalvars_t *)> ("GiveFnptrsToDll");
auto api_GiveFnptrsToDll = game.lib ().resolve <void (DLL_STDCALL *) (enginefuncs_t *, globalvars_t *)> (__FUNCTION__);
assert (api_GiveFnptrsToDll != nullptr);
if (!api_GiveFnptrsToDll) {
logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", __FUNCTION__);
}
GetEngineFunctions (functionTable, nullptr);
// give the engine functions to the other DLL...
api_GiveFnptrsToDll (functionTable, pGlobals);
}
DLL_ENTRYPOINT {
// dynamic library entry point, can be used for uninitialization stuff. NOT for initializing
// anything because if you ever attempt to wander outside the scope of this function on a
// DLL attach, LoadLibrary() will simply fail. And you can't do I/Os here either.
// dynamic library detaching ??
if (DLL_DETACHING) {
waypoints.init (); // free everything that's freeable
if (api_GiveFnptrsToDll) {
api_GiveFnptrsToDll (functionTable, pGlobals);
}
DLL_RETENTRY; // the return data type is OS specific too
}
void helper_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) {
if (addr == nullptr) {
addr = game.getLib ().resolve <EntityFunction> (name);
addr = game.lib ().resolve <EntityFunction> (name);
}
if (addr == nullptr) {
@ -1022,10 +1034,10 @@ void helper_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev)
addr (pev);
}
#define LINK_ENTITY(entityName) \
SHARED_LIBRARAY_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
helper_LinkEntity (addr, #entityName, pev); \
#define LINK_ENTITY(entityName) \
CR_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
helper_LinkEntity (addr, #entityName, pev); \
}
// entities in counter-strike...

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
BotUtils::BotUtils (void) {
BotUtils::BotUtils () {
m_needToSendWelcome = false;
m_welcomeReceiveTime = 0.0f;
@ -32,47 +32,30 @@ BotUtils::BotUtils (void) {
m_sentences.push ("attention, expect experimental armed hostile presence");
m_sentences.push ("warning, medical attention required");
m_tags.push ({ "[[", "]]" });
m_tags.push ({ "-=", "=-" });
m_tags.push ({ "-[", "]-" });
m_tags.push ({ "-]", "[-" });
m_tags.push ({ "-}", "{-" });
m_tags.push ({ "-{", "}-" });
m_tags.push ({ "<[", "]>" });
m_tags.push ({ "<]", "[>" });
m_tags.push ({ "[-", "-]" });
m_tags.push ({ "]-", "-[" });
m_tags.push ({ "{-", "-}" });
m_tags.push ({ "}-", "-{" });
m_tags.push ({ "[", "]" });
m_tags.push ({ "{", "}" });
m_tags.push ({ "<", "[" });
m_tags.push ({ ">", "<" });
m_tags.push ({ "-", "-" });
m_tags.push ({ "|", "|" });
m_tags.push ({ "=", "=" });
m_tags.push ({ "+", "+" });
m_tags.push ({ "(", ")" });
m_tags.push ({ ")", "(" });
m_tags.emplace ("[[", "]]");
m_tags.emplace ("-=", "=-");
m_tags.emplace ("-[", "]-");
m_tags.emplace ("-]", "[-");
m_tags.emplace ("-}", "{-");
m_tags.emplace ("-{", "}-");
m_tags.emplace ("<[", "]>");
m_tags.emplace ("<]", "[>");
m_tags.emplace ("[-", "-]");
m_tags.emplace ("]-", "-[");
m_tags.emplace ("{-", "-}");
m_tags.emplace ("}-", "-{");
m_tags.emplace ("[", "]");
m_tags.emplace ("{", "}");
m_tags.emplace ("<", "[");
m_tags.emplace (">", "<");
m_tags.emplace ("-", "-");
m_tags.emplace ("|", "|");
m_tags.emplace ("=", "=");
m_tags.emplace ("+", "+");
m_tags.emplace ("(", ")");
m_tags.emplace (")", "(");
m_clients.resize (MAX_ENGINE_PLAYERS + 1);
}
const char *BotUtils::format (const char *format, ...) {
static char strBuffer[2][MAX_PRINT_BUFFER];
static int rotator = 0;
if (format == nullptr) {
return strBuffer[rotator];
}
static char *ptr = strBuffer[rotator ^= 1];
va_list ap;
va_start (ap, format);
vsnprintf (ptr, MAX_PRINT_BUFFER - 1, format, ap);
va_end (ap);
return ptr;
m_clients.resize (kGameMaxPlayers + 1);
}
bool BotUtils::isAlive (edict_t *ent) {
@ -90,7 +73,7 @@ float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
}
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
return getShootingCone (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
return getShootingCone (ent, origin) >= cr::cosf (cr::degreesToRadians ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
}
bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
@ -98,7 +81,7 @@ bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
return false;
}
TraceResult tr;
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TraceIgnore::Everything, ent, &tr);
if (tr.flFraction != 1.0f) {
return false;
@ -109,13 +92,10 @@ bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
// this function draw spraypaint depending on the tracing results.
static StringArray logotypes;
auto logo = conf.getRandomLogoName (logotypeIndex);
if (logotypes.empty ()) {
logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";");
}
int entityIndex = -1, message = TE_DECAL;
int decalIndex = engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
int decalIndex = engfuncs.pfnDecalIndex (logo.chars ());
if (decalIndex < 0) {
decalIndex = engfuncs.pfnDecalIndex ("{lambda06");
@ -151,7 +131,7 @@ void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeInde
}
}
if (logotypes[logotypeIndex].contains ("{")) {
if (logo.startsWith ("{")) {
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
.writeByte (TE_PLAYERDECAL)
.writeByte (game.indexOfEntity (pev->pContainingEntity))
@ -187,14 +167,14 @@ bool BotUtils::isPlayer (edict_t *ent) {
return false;
}
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots.getBot (ent) != nullptr) {
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots[ent] != nullptr) {
return !isEmptyStr (STRING (ent->v.netname));
}
return false;
}
bool BotUtils::isPlayerVIP (edict_t *ent) {
if (!game.mapIs (MAP_AS)) {
if (!game.mapIs (MapFlags::Assassination)) {
return false;
}
@ -205,14 +185,14 @@ bool BotUtils::isPlayerVIP (edict_t *ent) {
}
bool BotUtils::isFakeClient (edict_t *ent) {
if (bots.getBot (ent) != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
if (bots[ent] != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
return true;
}
return false;
}
bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
if (outFile->isValid ()) {
if (*outFile) {
outFile->close ();
}
@ -225,165 +205,75 @@ bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, M
if (strcmp (fileName, "lang.cfg") == 0 && strcmp (yb_language.str (), "en") == 0) {
return false;
}
const char *langConfig = format ("%s/lang/%s_%s", configDir, yb_language.str (), fileName);
// check file existence
int size = 0;
uint8 *buffer = nullptr;
auto langConfig = strings.format ("%s/lang/%s_%s", configDir, yb_language.str (), fileName);
// check is file is exists for this language
if ((buffer = MemoryLoader::ref ().load (langConfig, &size)) != nullptr) {
MemoryLoader::ref ().unload (buffer);
// unload and reopen file using MemoryFile
outFile->open (langConfig);
}
else {
outFile->open (format ("%s/lang/en_%s", configDir, fileName));
if (!outFile->open (langConfig)) {
outFile->open (strings.format ("%s/lang/en_%s", configDir, fileName));
}
}
else {
outFile->open (format ("%s/%s", configDir, fileName));
outFile->open (strings.format ("%s/%s", configDir, fileName));
}
if (!outFile->isValid ()) {
logEntry (true, LL_ERROR, errorIfNotExists);
if (!*outFile) {
logger.error (errorIfNotExists);
return false;
}
return true;
}
void BotUtils::checkWelcome (void) {
void BotUtils::checkWelcome () {
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
if (game.isDedicated () || !yb_display_welcome_text.boolean () || !m_needToSendWelcome) {
if (game.isDedicated () || !yb_display_welcome_text.bool_ () || !m_needToSendWelcome) {
return;
}
m_welcomeReceiveTime = 0.0f;
if (game.is (GAME_LEGACY)) {
m_needToSendWelcome = true;
return;
}
bool needToSendMsg = (waypoints.length () > 0 ? m_needToSendWelcome : true);
if (isAlive (game.getLocalEntity ()) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
bool needToSendMsg = (graph.length () > 0 ? m_needToSendWelcome : true);
auto receiveEntity = game.getLocalEntity ();
if (isAlive (receiveEntity) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
m_welcomeReceiveTime = game.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
}
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
if (!game.is (GAME_MOBILITY | GAME_XASH_ENGINE)) {
game.execCmd ("speak \"%s\"", m_sentences.random ().chars ());
}
game.chatPrint ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL);
MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), game.getLocalEntity ())
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
if (!game.is (GameFlags::Mobility | GameFlags::Xash3D)) {
game.serverCommand ("speak \"%s\"", m_sentences.random ().chars ());
}
MessageWriter (MSG_ONE, game.getMessageId (NetMsg::TextMsg), nullvec, receiveEntity)
.writeByte (HUD_PRINTTALK)
.writeString (strings.format ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL));
MessageWriter (MSG_ONE, SVC_TEMPENTITY, nullvec, receiveEntity)
.writeByte (TE_TEXTMESSAGE)
.writeByte (1)
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
.writeShort (MessageWriter::fs16 (-1.0f, 13.0f))
.writeShort (MessageWriter::fs16 (-1.0f, 13.0f))
.writeByte (2)
.writeByte (rng.getInt (33, 255))
.writeByte (rng.getInt (33, 255))
.writeByte (rng.getInt (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (0)
.writeByte (rng.getInt (230, 255))
.writeByte (rng.getInt (230, 255))
.writeByte (rng.getInt (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (200)
.writeShort (MessageWriter::fu16 (0.0078125f, 1 << 8))
.writeShort (MessageWriter::fu16 (2.0f, 1 << 8))
.writeShort (MessageWriter::fu16 (6.0f, 1 << 8))
.writeShort (MessageWriter::fu16 (0.1f, 1 << 8))
.writeString (format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ()));
.writeShort (MessageWriter::fu16 (0.0078125f, 8.0f))
.writeShort (MessageWriter::fu16 (2.0f, 8.0f))
.writeShort (MessageWriter::fu16 (6.0f, 8.0f))
.writeShort (MessageWriter::fu16 (0.1f, 8.0f))
.writeString (strings.format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, graph.getAuthor ()));
m_welcomeReceiveTime = 0.0f;
m_needToSendWelcome = false;
}
}
void BotUtils::logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
// this function logs a message to the message log file root directory.
va_list ap;
char buffer[MAX_PRINT_BUFFER] = { 0, }, levelString[32] = { 0, };
va_start (ap, format);
vsnprintf (buffer, cr::bufsize (buffer), format, ap);
va_end (ap);
switch (logLevel) {
case LL_DEFAULT:
strcpy (levelString, "LOG: ");
break;
case LL_WARNING:
strcpy (levelString, "WARN: ");
break;
case LL_ERROR:
strcpy (levelString, "ERROR: ");
break;
case LL_FATAL:
strcpy (levelString, "FATAL: ");
break;
}
if (outputToConsole) {
game.print ("%s%s", levelString, buffer);
}
// now check if logging disabled
if (!(logLevel & LL_IGNORE)) {
extern ConVar yb_debug;
if (logLevel == LL_DEFAULT && yb_debug.integer () < 3) {
return; // no log, default logging is disabled
}
if (logLevel == LL_WARNING && yb_debug.integer () < 2) {
return; // no log, warning logging is disabled
}
if (logLevel == LL_ERROR && yb_debug.integer () < 1) {
return; // no log, error logging is disabled
}
}
// open file in a standard stream
File fp ("yapb.txt", "at");
// check if we got a valid handle
if (!fp.isValid ()) {
return;
}
time_t tickTime = time (&tickTime);
tm *time = localtime (&tickTime);
fp.writeFormat ("%02d:%02d:%02d --> %s%s\n", time->tm_hour, time->tm_min, time->tm_sec, levelString, buffer);
fp.close ();
if (logLevel == LL_FATAL) {
bots.kickEveryone (true);
waypoints.init ();
#if defined(PLATFORM_WIN32)
DestroyWindow (GetForegroundWindow ());
MessageBoxA (GetActiveWindow (), buffer, "YaPB Error", MB_ICONSTOP);
#else
printf ("%s\n", buffer);
#endif
#if defined(PLATFORM_WIN32)
_exit (1);
#else
exit (1);
#endif
}
}
bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool needAlive, bool needDrawn, bool needBotWithC4) {
// this function finds nearest to to, player with set of parameters, like his
// team, live status, search distance etc. if needBot is true, then pvHolder, will
@ -395,11 +285,11 @@ bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDist
int toTeam = game.getTeam (to);
for (const auto &client : m_clients) {
if (!(client.flags & CF_USED) || client.ent == to) {
if (!(client.flags & ClientFlags::Used) || client.ent == to) {
continue;
}
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & ClientFlags::Alive)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & Weapon::C4))) {
continue; // filter players with parameters
}
float distance = (client.ent->v.origin - to->v.origin).length ();
@ -416,7 +306,7 @@ bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDist
// fill the holder
if (needBot) {
*pvHolder = reinterpret_cast <void *> (bots.getBot (survive));
*pvHolder = reinterpret_cast <void *> (bots[survive]);
}
else {
*pvHolder = reinterpret_cast <void *> (survive);
@ -436,16 +326,16 @@ void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float vo
if (origin.empty ()) {
return;
}
int index = game.indexOfEntity (ent) - 1;
int index = game.indexOfPlayer (ent);
if (index < 0 || index >= game.maxClients ()) {
float nearestDistance = 99999.0f;
// loop through all players
for (int i = 0; i < game.maxClients (); i++) {
for (int i = 0; i < game.maxClients (); ++i) {
const Client &client = m_clients[i];
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
continue;
}
float distance = (client.origin - origin).length ();
@ -545,9 +435,9 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
else {
extern ConVar mp_footsteps;
if (mp_footsteps.boolean ()) {
if (mp_footsteps.bool_ ()) {
// moves fast enough?
hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f);
hearDistance = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
timeSound = game.timebase () + 0.3f;
}
}
@ -573,36 +463,129 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
}
}
void BotUtils::updateClients (void) {
void BotUtils::updateClients () {
// record some stats of all players on the server
for (int i = 0; i < game.maxClients (); i++) {
edict_t *player = game.entityOfIndex (i + 1);
for (int i = 0; i < game.maxClients (); ++i) {
edict_t *player = game.playerOfIndex (i);
Client &client = m_clients[i];
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT)) {
client.ent = player;
client.flags |= CF_USED;
client.flags |= ClientFlags::Used;
if (util.isAlive (player)) {
client.flags |= CF_ALIVE;
client.flags |= ClientFlags::Alive;
}
else {
client.flags &= ~CF_ALIVE;
client.flags &= ~ClientFlags::Alive;
}
if (client.flags & CF_ALIVE) {
if (client.flags & ClientFlags::Alive) {
client.origin = player->v.origin;
simulateSoundUpdates (i);
}
}
else {
client.flags &= ~(CF_USED | CF_ALIVE);
client.flags &= ~(ClientFlags::Used | ClientFlags::Alive);
client.ent = nullptr;
}
}
}
int BotUtils::buildNumber (void) {
int BotUtils::getPingBitmask (edict_t *ent, int loss, int ping) {
// this function generats bitmask for SVC_PINGS engine message. See SV_EmitPings from engine for details
const auto emit = [] (int s0, int s1, int s2) {
return (s0 & (cr::bit (s1) - 1)) << s2;
};
return emit (loss, 7, 18) | emit (ping, 12, 6) | emit (game.indexOfPlayer (ent), 5, 1) | 1;
}
void BotUtils::calculatePings () {
if (!game.is (GameFlags::HasFakePings) || yb_show_latency.int_ () != 2) {
return;
}
Twin <int, int> average { 0, 0 };
int numHumans = 0;
// first get average ping on server, and store real client pings
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used) || isFakeClient (client.ent)) {
continue;
}
int ping, loss;
engfuncs.pfnGetPlayerStats (client.ent, &ping, &loss);
// store normal client ping
client.ping = getPingBitmask (client.ent, loss, ping > 0 ? ping / 2 : rg.int_ (8, 16)); // getting player ping sometimes fails
client.pingUpdate = true; // force resend ping
numHumans++;
average.first += ping;
average.second += loss;
}
if (numHumans > 0) {
average.first /= numHumans;
average.second /= numHumans;
}
else {
average.first = rg.int_ (30, 40);
average.second = rg.int_ (5, 10);
}
// now calculate bot ping based on average from players
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used)) {
continue;
}
auto bot = bots[client.ent];
// we're only intrested in bots here
if (!bot) {
continue;
}
int part = static_cast <int> (average.first * 0.2f);
int botPing = bot->m_basePing + rg.int_ (average.first - part, average.first + part) + rg.int_ (bot->m_difficulty / 2, bot->m_difficulty);
int botLoss = rg.int_ (average.second / 2, average.second);
client.ping = getPingBitmask (client.ent, botLoss, botPing);
client.pingUpdate = true; // force resend ping
}
}
void BotUtils::sendPings (edict_t *to) {
MessageWriter msg;
// missing from sdk
constexpr int kGamePingSVC = 17;
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used)) {
continue;
}
if (!client.pingUpdate) {
continue;
}
client.pingUpdate = false;
// no ping, no fun
if (!client.ping) {
client.ping = getPingBitmask (client.ent, rg.int_ (5, 10), rg.int_ (15, 40));
}
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullvec, to)
.writeLong (client.ping)
.end ();
}
return;
}
int BotUtils::buildNumber () {
// this function generates build number from the compiler date macros
static int buildNumber = 0;
@ -624,7 +607,7 @@ int BotUtils::buildNumber (void) {
int i = 0;
// go through all months, and calculate, days since year start
for (i = 0; i < 11; i++) {
for (i = 0; i < 11; ++i) {
if (strncmp (&date[0], months[i], 3) == 0) {
break; // found current month break
}
@ -655,38 +638,38 @@ int BotUtils::getWeaponAlias (bool needString, const char *weaponAlias, int weap
// weapon enumeration
WeaponTab_t weaponTab[] = {
{WEAPON_USP, "usp"}, // HK USP .45 Tactical
{WEAPON_GLOCK, "glock"}, // Glock18 Select Fire
{WEAPON_DEAGLE, "deagle"}, // Desert Eagle .50AE
{WEAPON_P228, "p228"}, // SIG P228
{WEAPON_ELITE, "elite"}, // Dual Beretta 96G Elite
{WEAPON_FIVESEVEN, "fn57"}, // FN Five-Seven
{WEAPON_M3, "m3"}, // Benelli M3 Super90
{WEAPON_XM1014, "xm1014"}, // Benelli XM1014
{WEAPON_MP5, "mp5"}, // HK MP5-Navy
{WEAPON_TMP, "tmp"}, // Steyr Tactical Machine Pistol
{WEAPON_P90, "p90"}, // FN P90
{WEAPON_MAC10, "mac10"}, // Ingram MAC-10
{WEAPON_UMP45, "ump45"}, // HK UMP45
{WEAPON_AK47, "ak47"}, // Automat Kalashnikov AK-47
{WEAPON_GALIL, "galil"}, // IMI Galil
{WEAPON_FAMAS, "famas"}, // GIAT FAMAS
{WEAPON_SG552, "sg552"}, // Sig SG-552 Commando
{WEAPON_M4A1, "m4a1"}, // Colt M4A1 Carbine
{WEAPON_AUG, "aug"}, // Steyr Aug
{WEAPON_SCOUT, "scout"}, // Steyr Scout
{WEAPON_AWP, "awp"}, // AI Arctic Warfare/Magnum
{WEAPON_G3SG1, "g3sg1"}, // HK G3/SG-1 Sniper Rifle
{WEAPON_SG550, "sg550"}, // Sig SG-550 Sniper
{WEAPON_M249, "m249"}, // FN M249 Para
{WEAPON_FLASHBANG, "flash"}, // Concussion Grenade
{WEAPON_EXPLOSIVE, "hegren"}, // High-Explosive Grenade
{WEAPON_SMOKE, "sgren"}, // Smoke Grenade
{WEAPON_ARMOR, "vest"}, // Kevlar Vest
{WEAPON_ARMORHELM, "vesthelm"}, // Kevlar Vest and Helmet
{WEAPON_DEFUSER, "defuser"}, // Defuser Kit
{WEAPON_SHIELD, "shield"}, // Tactical Shield
{WEAPON_KNIFE, "knife"} // Knife
{Weapon::USP, "usp"}, // HK USP .45 Tactical
{Weapon::Glock18, "glock"}, // Glock18 Select Fire
{Weapon::Deagle, "deagle"}, // Desert Eagle .50AE
{Weapon::P228, "p228"}, // SIG P228
{Weapon::Elite, "elite"}, // Dual Beretta 96G Elite
{Weapon::FiveSeven, "fn57"}, // FN Five-Seven
{Weapon::M3, "m3"}, // Benelli M3 Super90
{Weapon::XM1014, "xm1014"}, // Benelli XM1014
{Weapon::MP5, "mp5"}, // HK MP5-Navy
{Weapon::TMP, "tmp"}, // Steyr Tactical Machine Pistol
{Weapon::P90, "p90"}, // FN P90
{Weapon::MAC10, "mac10"}, // Ingram MAC-10
{Weapon::UMP45, "ump45"}, // HK UMP45
{Weapon::AK47, "ak47"}, // Automat Kalashnikov AK-47
{Weapon::Galil, "galil"}, // IMI Galil
{Weapon::Famas, "famas"}, // GIAT FAMAS
{Weapon::SG552, "sg552"}, // Sig SG-552 Commando
{Weapon::M4A1, "m4a1"}, // Colt M4A1 Carbine
{Weapon::AUG, "aug"}, // Steyr Aug
{Weapon::Scout, "scout"}, // Steyr Scout
{Weapon::AWP, "awp"}, // AI Arctic Warfare/Magnum
{Weapon::G3SG1, "g3sg1"}, // HK G3/SG-1 Sniper Rifle
{Weapon::SG550, "sg550"}, // Sig SG-550 Sniper
{Weapon::M249, "m249"}, // FN M249 Para
{Weapon::Flashbang, "flash"}, // Concussion Grenade
{Weapon::Explosive, "hegren"}, // High-Explosive Grenade
{Weapon::Smoke, "sgren"}, // Smoke Grenade
{Weapon::Armor, "vest"}, // Kevlar Vest
{Weapon::ArmorHelm, "vesthelm"}, // Kevlar Vest and Helmet
{Weapon::Defuser, "defuser"}, // Defuser Kit
{Weapon::Shield, "shield"}, // Tactical Shield
{Weapon::Knife, "knife"} // Knife
};
// if we need to return the string, find by weapon id

File diff suppressed because it is too large Load diff