v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
161
attic/old_source/StarAccessControl.cpp
Normal file
161
attic/old_source/StarAccessControl.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
#include "StarAccessControl.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarLexicalCast.hpp"
|
||||
#include "StarFile.hpp"
|
||||
#include "StarVariantExtra.hpp"
|
||||
#include "StarAuthentication.hpp"
|
||||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarConfiguration.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
AccessControlClient::AccessControlClient(AccessControl* control) {
|
||||
m_control = control;
|
||||
m_rounds = 0;
|
||||
m_intialized = false;
|
||||
}
|
||||
|
||||
bool AccessControlClient::setAccountName(String const& account) {
|
||||
// check if either the account has been banned
|
||||
if (m_control->isBannedAccountName(account))
|
||||
return false;
|
||||
m_account = account;
|
||||
auto sr = m_control->passwordSaltAndRounds(m_account);
|
||||
m_salty = sr.first;
|
||||
m_rounds = sr.second;
|
||||
m_intialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AccessControlClient::setPlayerName(String const& playerName) {
|
||||
if (m_control->isBannedPlayerName(playerName))
|
||||
return false;
|
||||
m_playerName = playerName;
|
||||
return true;
|
||||
}
|
||||
|
||||
String AccessControlClient::passwordSalt() {
|
||||
if (!m_intialized)
|
||||
throw StarException();
|
||||
return m_salty;
|
||||
}
|
||||
|
||||
int AccessControlClient::passwordRounds() {
|
||||
if (!m_intialized)
|
||||
throw StarException();
|
||||
return m_rounds;
|
||||
}
|
||||
|
||||
bool AccessControlClient::validate(String const& passwordHash) {
|
||||
return m_control->validate(m_account, passwordHash, passwordRounds(), passwordSalt());
|
||||
}
|
||||
|
||||
String AccessControlClient::processCommand(String const& command, StringList const& arguments) {
|
||||
return m_control->processCommand(m_account, command, arguments);
|
||||
}
|
||||
|
||||
AccessControl::AccessControl(String const& storageDirectory, UniverseServer* universeServer) {
|
||||
m_universeServer = universeServer;
|
||||
m_storageDirectory = storageDirectory;
|
||||
load();
|
||||
}
|
||||
|
||||
void AccessControl::load() {
|
||||
if (!File::isFile(m_storageDirectory + "/accesscontrol.config")) {
|
||||
Logger::warn("AccessControl: Failed to read accesscontrol file, creating a new file");
|
||||
VariantMap vm;
|
||||
vm.add("accounts", VariantMap());
|
||||
vm.add("bannedAddresses", VariantList());
|
||||
vm.add("bannedAccountNames", VariantList());
|
||||
vm.add("bannedPlayerNames", VariantList());
|
||||
{
|
||||
MutexLocker locked(m_mutex);
|
||||
m_state = vm;
|
||||
}
|
||||
save();
|
||||
}
|
||||
Variant newState = Variant::parseJson(File::readFileString(m_storageDirectory+"/accesscontrol.config"));
|
||||
MutexLocker locked(m_mutex);
|
||||
m_state = newState;
|
||||
|
||||
m_bannedAddresses = variantToStringSet(m_state.get("bannedAddresses"));
|
||||
m_bannedAccountNames = variantToStringSet(m_state.get("bannedAccountNames"));
|
||||
m_bannedPlayerNames = variantToStringSet(m_state.get("bannedPlayerNames"));
|
||||
}
|
||||
|
||||
void AccessControl::save() {
|
||||
MutexLocker locked(m_mutex);
|
||||
File::overwriteFileWithRename(m_state.repr(1, true), m_storageDirectory+"/accesscontrol.config", ".new");
|
||||
}
|
||||
|
||||
bool AccessControl::isBannedAddress(String const& address) {
|
||||
MutexLocker locked(m_mutex);
|
||||
return m_bannedAddresses.contains(address);
|
||||
}
|
||||
|
||||
bool AccessControl::isBannedAccountName(String const& account) {
|
||||
MutexLocker locked(m_mutex);
|
||||
return m_bannedAccountNames.contains(account);
|
||||
}
|
||||
|
||||
bool AccessControl::isBannedPlayerName(String const& playerName) {
|
||||
MutexLocker locked(m_mutex);
|
||||
return m_bannedPlayerNames.contains(playerName);
|
||||
}
|
||||
|
||||
AccessControlClientPtr AccessControl::clientConnected() {
|
||||
return make_shared<AccessControlClient>(this);
|
||||
}
|
||||
|
||||
pair<String, int> AccessControl::passwordSaltAndRounds(String const& account) {
|
||||
_unused(account);
|
||||
return { Star::Auth::generateKey(), Root::singleton().configuration()->get("bcryptRounds").toInt()};
|
||||
}
|
||||
|
||||
Variant AccessControl::accountValues(String const& account) {
|
||||
MutexLocker locked(m_mutex);
|
||||
if (m_state.get("accounts").contains(account))
|
||||
return m_state.get("accounts").get(account);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AccessControl::validate(String const& account, String const& passwordHash, int rounds, String const& passwordSalt) {
|
||||
if (account.empty()) {
|
||||
for (auto password : Root::singleton().configuration()->get("serverPasswords").toList()) {
|
||||
// forward bcrypt validate, kind of cheating, normally you'd store the hashed variants
|
||||
if (Star::Auth::bcryptValidate(password.toString(), passwordSalt, passwordHash, rounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto accountSalt = account + passwordSalt;
|
||||
|
||||
Variant accountEntry = accountValues(account);
|
||||
|
||||
if (!accountEntry.isNull()) {
|
||||
auto password = accountEntry.getString("password");
|
||||
// forward bcrypt validate, kind of cheating, normally you'd store the hashed variants
|
||||
if (Star::Auth::bcryptValidate(password, accountSalt, passwordHash, rounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String AccessControl::processCommand(String const& account, String const& command, StringList const& arguments) {
|
||||
_unused(account);
|
||||
_unused(arguments);
|
||||
// ac-help
|
||||
// ac-kick
|
||||
// ac-banhard
|
||||
// ac-banaccount
|
||||
// ac-banip
|
||||
// ac-players
|
||||
|
||||
return strf("Command '%' not recognized", command);
|
||||
}
|
||||
|
||||
}
|
70
attic/old_source/StarAccessControl.hpp
Normal file
70
attic/old_source/StarAccessControl.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#ifndef STAR_ACCESS_CONTROL_HPP
|
||||
#define STAR_ACCESS_CONTROL_HPP
|
||||
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarGameTypes.hpp"
|
||||
#include "StarThread.hpp"
|
||||
#include "StarSet.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(UniverseServer);
|
||||
STAR_CLASS(AccessControlClient);
|
||||
STAR_CLASS(AccessControl);
|
||||
|
||||
class AccessControlClient {
|
||||
public:
|
||||
AccessControlClient(AccessControl* control);
|
||||
|
||||
bool setAccountName(String const& account);
|
||||
bool setPlayerName(String const& playerName);
|
||||
|
||||
String passwordSalt();
|
||||
int passwordRounds();
|
||||
bool validate(String const& passwordHash);
|
||||
|
||||
String processCommand(String const& command, StringList const& arguments);
|
||||
|
||||
private:
|
||||
AccessControl* m_control;
|
||||
String m_account;
|
||||
String m_playerName;
|
||||
String m_salty;
|
||||
int m_rounds;
|
||||
bool m_intialized;
|
||||
};
|
||||
|
||||
class AccessControl {
|
||||
public:
|
||||
AccessControl(String const& storageDirectory, UniverseServer* universeServer);
|
||||
|
||||
pair<String, int> passwordSaltAndRounds(String const& account);
|
||||
bool validate(String const& account, String const& passwordHash, int rounds, String const& passwordSalt);
|
||||
|
||||
bool isBannedAddress(String const& connectionString);
|
||||
bool isBannedAccountName(String const& account);
|
||||
bool isBannedPlayerName(String const& playerName);
|
||||
|
||||
Variant accountValues(String const& account);
|
||||
|
||||
AccessControlClientPtr clientConnected();
|
||||
|
||||
String processCommand(String const& account, String const& command, StringList const& arguments);
|
||||
|
||||
private:
|
||||
UniverseServer* m_universeServer;
|
||||
String m_storageDirectory;
|
||||
Variant m_state;
|
||||
Mutex m_mutex;
|
||||
|
||||
Set<String> m_bannedAddresses;
|
||||
Set<String> m_bannedAccountNames;
|
||||
Set<String> m_bannedPlayerNames;
|
||||
|
||||
void load();
|
||||
void save();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
270
attic/old_source/StarAnimation.cpp
Normal file
270
attic/old_source/StarAnimation.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include "StarAnimation.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
AnimationControl::~AnimationControl() { }
|
||||
|
||||
AnimationState::~AnimationState() { }
|
||||
|
||||
namespace Animation {
|
||||
|
||||
class Frame;
|
||||
typedef std::shared_ptr<Frame> FramePtr;
|
||||
class Rule;
|
||||
typedef std::shared_ptr<Rule> RulePtr;
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
virtual ~Frame() { }
|
||||
virtual void apply(AnimationControlPtr controller) = 0;
|
||||
static FramePtr construct(Variant const& config);
|
||||
};
|
||||
|
||||
class StopSound: public Frame {
|
||||
public:
|
||||
StopSound(Variant const& config) {
|
||||
}
|
||||
|
||||
virtual void apply(AnimationControlPtr controller) {
|
||||
controller->stopSound();
|
||||
}
|
||||
};
|
||||
|
||||
class PlaySound: public Frame {
|
||||
public:
|
||||
PlaySound(Variant const& config) {
|
||||
m_sound = config.getString(1);
|
||||
}
|
||||
virtual void apply(AnimationControlPtr controller) {
|
||||
controller->playSound(m_sound);
|
||||
}
|
||||
|
||||
private:
|
||||
String m_sound;
|
||||
};
|
||||
|
||||
class GraphicsFrame: public Frame {
|
||||
public:
|
||||
GraphicsFrame(Variant const& config) {
|
||||
m_frame = config.getString(1);
|
||||
}
|
||||
virtual void apply(AnimationControlPtr controller) {
|
||||
controller->setGraphic(m_frame);
|
||||
}
|
||||
|
||||
private:
|
||||
String m_frame;
|
||||
};
|
||||
|
||||
class WireFrame: public Frame {
|
||||
public:
|
||||
WireFrame(Variant const& config) {
|
||||
m_wire = config.getInt(1);
|
||||
m_level = config.getBool(2);
|
||||
}
|
||||
|
||||
virtual void apply(AnimationControlPtr controller) {
|
||||
controller->setWireLevel(m_wire, m_level);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_wire;
|
||||
bool m_level;
|
||||
};
|
||||
|
||||
class ChildTransitionFrame: public Frame {
|
||||
public:
|
||||
ChildTransitionFrame(Variant const& config) {
|
||||
m_child = config.getString(1);
|
||||
m_state = config.getString(2);
|
||||
}
|
||||
virtual void apply(AnimationControlPtr controller) {
|
||||
controller->requestChildTransition(m_child, m_state);
|
||||
}
|
||||
|
||||
private:
|
||||
String m_child;
|
||||
String m_state;
|
||||
};
|
||||
|
||||
FramePtr Frame::construct(Variant const& config) {
|
||||
String code;
|
||||
VariantList arguments;
|
||||
if (config.type() == Variant::Type::LIST) {
|
||||
arguments = config.list();
|
||||
code = config.getString(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
code = "graphic";
|
||||
arguments = {code, config};
|
||||
}
|
||||
|
||||
if (code == "play")
|
||||
return make_shared < PlaySound > (arguments);
|
||||
if (code == "stopSound")
|
||||
return make_shared < StopSound > (arguments);
|
||||
if (code == "graphic")
|
||||
return make_shared < GraphicsFrame > (arguments);
|
||||
if (code == "wire")
|
||||
return make_shared < WireFrame > (arguments);
|
||||
if (code == "transition")
|
||||
return make_shared < ChildTransitionFrame > (arguments);
|
||||
|
||||
throw StarException("Unknown animation frame kind '" + code + "'");
|
||||
return {};
|
||||
}
|
||||
|
||||
class Rule {
|
||||
public:
|
||||
virtual ~Rule() { }
|
||||
virtual bool check(AnimationStatePtr state) = 0;
|
||||
static RulePtr construct(String const& transition, Variant const& config);
|
||||
};
|
||||
|
||||
class RequestedRule: public Rule {
|
||||
public:
|
||||
RequestedRule(Variant const& config);
|
||||
virtual bool check(AnimationStatePtr state);
|
||||
|
||||
private:
|
||||
String m_state;
|
||||
};
|
||||
|
||||
RequestedRule::RequestedRule(Variant const& config) {
|
||||
m_state = config.getString(1);
|
||||
}
|
||||
|
||||
bool RequestedRule::check(AnimationStatePtr state) {
|
||||
if (!state->hasRequestedFrame())
|
||||
return false;
|
||||
return state->requestedFrame() == m_state;
|
||||
}
|
||||
|
||||
class CompletedRule: public Rule {
|
||||
public:
|
||||
CompletedRule(Variant const& config) {
|
||||
}
|
||||
virtual bool check(AnimationStatePtr state) {
|
||||
return state->sequenceCompleted();
|
||||
}
|
||||
};
|
||||
|
||||
class StateRule: public Rule {
|
||||
public:
|
||||
StateRule(Variant const& config) {
|
||||
m_child = config.getString(1);
|
||||
m_state = config.getString(2);
|
||||
}
|
||||
|
||||
virtual bool check(AnimationStatePtr state) {
|
||||
return state->childState(m_child)->currentFrame() == m_state;
|
||||
}
|
||||
|
||||
private:
|
||||
String m_child;
|
||||
String m_state;
|
||||
};
|
||||
|
||||
RulePtr Rule::construct(String const& transition, Variant const& config) {
|
||||
String ruleName;
|
||||
VariantList arguments;
|
||||
if (config.type() == Variant::Type::LIST) {
|
||||
arguments = config.list();
|
||||
ruleName = config.getString(0);
|
||||
}
|
||||
else {
|
||||
ruleName = config.toString();
|
||||
arguments = {ruleName, transition};
|
||||
}
|
||||
|
||||
if (ruleName == "requested")
|
||||
return make_shared < RequestedRule > (arguments);
|
||||
else if (ruleName == "completed")
|
||||
return make_shared < CompletedRule > (arguments);
|
||||
else if (ruleName == "state")
|
||||
return make_shared < StateRule > (arguments);
|
||||
throw StarException("Unknown animation transition rule '" + ruleName + "'");
|
||||
return {};
|
||||
}
|
||||
|
||||
class Transition {
|
||||
public:
|
||||
Transition(Variant const& config);
|
||||
|
||||
String target;
|
||||
List<RulePtr> rules;
|
||||
};
|
||||
|
||||
Transition::Transition(Variant const& config) {
|
||||
auto list = config.list();
|
||||
target = config.getString(0);
|
||||
for (size_t i = 1; i < list.size(); i++)
|
||||
rules.append(Rule::construct(target, list[i]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AnimationSequence {
|
||||
public:
|
||||
AnimationSequence(Variant const& config);
|
||||
void step(float timeStep);
|
||||
|
||||
private:
|
||||
List<Animation::FramePtr> m_frames;
|
||||
List<float> m_frameDuration;
|
||||
List<Animation::Transition> m_transitions;
|
||||
};
|
||||
|
||||
AnimationSequence::AnimationSequence(Variant const& config) {
|
||||
for (auto frame : config.getList(0))
|
||||
m_frames.append(Animation::Frame::construct(frame));
|
||||
for (auto transition : config.getList(1))
|
||||
m_transitions.append(Animation::Transition(transition));
|
||||
if (config.size() == 3)
|
||||
{
|
||||
if (config.list()[2].type() != Variant::Type::LIST) {
|
||||
for (size_t i = 0; i < m_frames.size(); i++)
|
||||
m_frameDuration.append(config.getFloat(2));
|
||||
}
|
||||
else {
|
||||
for (auto duration : config.getList(2))
|
||||
m_frameDuration.append(duration.toFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationSequence::step(float timeStep) {
|
||||
//tooodles
|
||||
}
|
||||
|
||||
class AnimationDefinition {
|
||||
public:
|
||||
AnimationDefinition(Variant const& config);
|
||||
|
||||
private:
|
||||
Map<String, AnimationSequencePtr> m_sequnces;
|
||||
Map<String, AnimationDefinitionPtr> m_nestedDefinitions;
|
||||
};
|
||||
|
||||
AnimationDefinition::AnimationDefinition(Variant const& config) {
|
||||
for (auto nested : config.getMap("nested")) {
|
||||
m_nestedDefinitions.add(nested.first, make_shared < AnimationDefinition > (nested.second));
|
||||
}
|
||||
for (auto sequence : config.getMap("sequences")) {
|
||||
m_sequnces.add(sequence.first, make_shared < AnimationSequence > (sequence.second));
|
||||
}
|
||||
}
|
||||
|
||||
AnimationDriver::AnimationDriver(AnimationControlPtr control, AnimationStatePtr state, Variant const& config, AnimationDriverMode mode) {
|
||||
m_definition = make_shared < AnimationDefinition > (config);
|
||||
m_control = control;
|
||||
m_state = state;
|
||||
m_mode = mode;
|
||||
}
|
||||
|
||||
void AnimationDriver::step(float timeStep) {
|
||||
/// 1 magic 2 ... 3 profit
|
||||
}
|
||||
|
||||
}
|
68
attic/old_source/StarAnimation.hpp
Normal file
68
attic/old_source/StarAnimation.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#ifndef STAR_ANIMATION_HPP
|
||||
#define STAR_ANIMATION_HPP
|
||||
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarColor.hpp"
|
||||
#include "StarSet.hpp"
|
||||
#include "StarAudio.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
class AnimationControl;
|
||||
typedef std::shared_ptr<AnimationControl> AnimationControlPtr;
|
||||
class AnimationState;
|
||||
typedef std::shared_ptr<AnimationState> AnimationStatePtr;
|
||||
class AnimationDefinition;
|
||||
typedef std::shared_ptr<AnimationDefinition> AnimationDefinitionPtr;
|
||||
class AnimationDriver;
|
||||
typedef std::shared_ptr<AnimationDriver> AnimationDriverPtr;
|
||||
class AnimationSequence;
|
||||
typedef std::shared_ptr<AnimationSequence> AnimationSequencePtr;
|
||||
|
||||
class AnimationControl {
|
||||
public:
|
||||
virtual ~AnimationControl();
|
||||
virtual void stopSound() = 0;
|
||||
virtual void playSound(String const& resource) = 0;
|
||||
virtual void setGraphic(String const& resource) = 0;
|
||||
virtual void setWireLevel(int wire, bool level) = 0;
|
||||
virtual void requestChildTransition(String const& child, String const& state) = 0;
|
||||
|
||||
virtual void bind(AnimationDefinitionPtr definition) = 0;
|
||||
};
|
||||
|
||||
class AnimationState {
|
||||
public:
|
||||
virtual ~AnimationState();
|
||||
virtual String currentFrame() = 0;
|
||||
virtual int frameSequence() = 0;
|
||||
virtual bool sequenceCompleted() = 0;
|
||||
virtual bool soundPlaying() = 0;
|
||||
virtual bool hasRequestedFrame() = 0;
|
||||
virtual String requestedFrame() = 0;
|
||||
virtual bool getWireLevel(int wire) = 0;
|
||||
virtual AnimationStatePtr childState(String const& child) = 0;
|
||||
|
||||
virtual void bind(AnimationDefinitionPtr definition) = 0;
|
||||
};
|
||||
|
||||
enum class AnimationDriverMode {
|
||||
Server,
|
||||
Client
|
||||
};
|
||||
|
||||
class AnimationDriver {
|
||||
public:
|
||||
AnimationDriver(AnimationControlPtr control, AnimationStatePtr state, Variant const& config, AnimationDriverMode mode);
|
||||
void step(float timeStep);
|
||||
|
||||
private:
|
||||
AnimationDefinitionPtr m_definition;
|
||||
AnimationControlPtr m_control;
|
||||
AnimationStatePtr m_state;
|
||||
AnimationDriverMode m_mode;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
290
attic/old_source/StarAuthentication.cpp
Normal file
290
attic/old_source/StarAuthentication.cpp
Normal file
|
@ -0,0 +1,290 @@
|
|||
#include "StarAuthentication.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarConfiguration.hpp"
|
||||
#include "StarClientAuthentication.hpp"
|
||||
#include "StarAuthenticationConnection.hpp"
|
||||
#include "StarAuthenticationService.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarFile.hpp"
|
||||
|
||||
using namespace Star::Auth;
|
||||
|
||||
namespace Star {
|
||||
|
||||
class AuthenticationImpl {
|
||||
public:
|
||||
AuthenticationImpl(Configuration const* configuration) {
|
||||
m_rootKey = make_shared<Key>();
|
||||
m_clientKey = make_shared<Key>();
|
||||
m_rootKey->loadPublicKey(configuration->getString("rootKey"));
|
||||
m_authenticator = make_shared<ClientAuthentication>(m_rootKey);
|
||||
m_connector = make_shared<AuthenticationConnection>(configuration->getString("authHostname"), configuration->getInt("authPort"));
|
||||
m_claimFile = Root::singleton().toStoragePath(configuration->getString("claimFile"));
|
||||
m_hasClaim = false;
|
||||
m_lazyLoadClaim = true;
|
||||
m_remoteValidated = false;
|
||||
}
|
||||
|
||||
bool validateClaim(bool remoteValidate) {
|
||||
if (m_lazyLoadClaim)
|
||||
loadClaim();
|
||||
if (validClaim()) {
|
||||
if (remoteValidate && !m_remoteValidated) {
|
||||
remoteValidateClaim();
|
||||
}
|
||||
}
|
||||
if (!m_hasClaim) {
|
||||
deleteClaim();
|
||||
}
|
||||
return m_hasClaim;
|
||||
}
|
||||
|
||||
Variant claim() {
|
||||
if (!m_hasClaim)
|
||||
throw StarException("No valid claim.");
|
||||
return m_claim;
|
||||
}
|
||||
|
||||
String decrypt(String const& message) {
|
||||
if (!m_hasClaim)
|
||||
throw StarException("No valid claim.");
|
||||
return m_clientKey->decryptMessage(message);
|
||||
}
|
||||
|
||||
String sign(String const& message) {
|
||||
if (!m_hasClaim)
|
||||
throw StarException("No valid claim.");
|
||||
return m_clientKey->signMessage(message);
|
||||
}
|
||||
|
||||
bool logon(String const& username, String const& passwordHash) {
|
||||
m_lazyLoadClaim = false;
|
||||
m_hasClaim = false;
|
||||
m_remoteValidated = false;
|
||||
try {
|
||||
Variant authPubKeyRequest = VariantMap{
|
||||
{"command", "getAuthKey"}
|
||||
};
|
||||
auto authSignatureString = m_connector->query(authPubKeyRequest.repr(0, true));
|
||||
Variant authSignature = Variant::parse(authSignatureString);
|
||||
if (!AuthenticationService::validateAuthSignature(*m_rootKey, authSignature))
|
||||
throw StarException("Auth server key failure.");
|
||||
|
||||
m_clientKey->regenerate();
|
||||
|
||||
auto authsvrRequest = m_authenticator->authsvrRequest(username, passwordHash, authSignatureString, *m_clientKey);
|
||||
|
||||
Variant authorizeClientRequest = VariantMap{
|
||||
{"command", "authorizeClient"},
|
||||
{"body", authsvrRequest}
|
||||
};
|
||||
auto authsvrResponse = m_connector->query(authorizeClientRequest.repr(0, true));
|
||||
auto logonResult = m_authenticator->authsvrResponse(authsvrResponse, *m_clientKey);
|
||||
|
||||
if (!AuthenticationService::validateClientClaim(*m_rootKey, logonResult))
|
||||
throw StarException("Logon failure.");
|
||||
|
||||
m_claim = logonResult;
|
||||
m_hasClaim = true;
|
||||
m_remoteValidated = true;
|
||||
|
||||
saveClaim();
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("Exception while validating claim. %s", e.what());
|
||||
}
|
||||
return m_hasClaim;
|
||||
}
|
||||
|
||||
bool remoteValidateClaim() {
|
||||
if (!m_hasClaim)
|
||||
return m_hasClaim;
|
||||
try {
|
||||
Variant authPubKeyRequest = VariantMap{
|
||||
{"command", "getAuthKey"}
|
||||
};
|
||||
auto authSignatureString = m_connector->query(authPubKeyRequest.repr(0, true));
|
||||
Variant authSignature = Variant::parse(authSignatureString);
|
||||
if (!AuthenticationService::validateAuthSignature(*m_rootKey, authSignature))
|
||||
throw StarException("Auth server key failure.");
|
||||
|
||||
auto authsvrRequest = m_authenticator->authsvrValidateRequest(m_claim, authSignatureString, *m_clientKey);
|
||||
Variant authorizeClientRequest = VariantMap{
|
||||
{"command", "validateClient"},
|
||||
{"body", authsvrRequest}
|
||||
};
|
||||
auto authsvrResponse = m_connector->query(authorizeClientRequest.repr(0, true));
|
||||
auto validateResult = m_authenticator->authsvrValidateResponse(authsvrResponse, *m_clientKey);
|
||||
if (!validateResult) {
|
||||
m_hasClaim = false;
|
||||
m_remoteValidated = false;
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
// validation failure will not cause an exception btw
|
||||
Logger::error("Exception while validating claim. %s", e.what());
|
||||
}
|
||||
return m_hasClaim;
|
||||
}
|
||||
|
||||
bool validClaim() {
|
||||
if (!m_hasClaim)
|
||||
return false;
|
||||
m_hasClaim = AuthenticationService::validateClientClaim(*m_rootKey, m_claim);
|
||||
if (!m_hasClaim)
|
||||
m_remoteValidated = false;
|
||||
return m_hasClaim;
|
||||
}
|
||||
|
||||
void loadClaim() {
|
||||
m_lazyLoadClaim = false;
|
||||
try {
|
||||
m_hasClaim = false;
|
||||
m_remoteValidated = false;
|
||||
if (m_claimFile.empty())
|
||||
return;
|
||||
if (!File::isFile(m_claimFile))
|
||||
return;
|
||||
VariantMap file = Variant::parseJson(File::readFileString(m_claimFile)).toMap();
|
||||
m_claim = file["claim"];
|
||||
m_hasClaim = true;
|
||||
m_clientKey->loadPrivateKey(file["key"].toString());
|
||||
m_hasClaim = validClaim();
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("Exception while loading claim. %s", e.what());
|
||||
}
|
||||
if (!m_hasClaim) {
|
||||
deleteClaim();
|
||||
}
|
||||
}
|
||||
|
||||
void saveClaim() {
|
||||
m_lazyLoadClaim = false;
|
||||
m_hasClaim = validClaim();
|
||||
if (!m_hasClaim) {
|
||||
deleteClaim();
|
||||
return;
|
||||
}
|
||||
if (m_claimFile.empty())
|
||||
return;
|
||||
try {
|
||||
VariantMap file;
|
||||
file.insert("key", m_clientKey->privateKey());
|
||||
file.insert("claim", m_claim);
|
||||
File::overwriteFileWithRename(Variant(file).toJson(1), m_claimFile);
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("Exception while saving claim. %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void deleteClaim() {
|
||||
m_lazyLoadClaim = false;
|
||||
m_hasClaim = false;
|
||||
m_remoteValidated = false;
|
||||
m_claim = {};
|
||||
try {
|
||||
if (m_claimFile.empty())
|
||||
return;
|
||||
if (File::isFile(m_claimFile)) {
|
||||
if (File::isFile(m_claimFile + ".old"))
|
||||
File::remove(m_claimFile + ".old");
|
||||
File::rename(m_claimFile, m_claimFile + ".old");
|
||||
File::remove(m_claimFile + ".old");
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
Logger::error("Exception while removing claim. %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<ClientAuthentication> m_authenticator;
|
||||
shared_ptr<AuthenticationConnection> m_connector;
|
||||
String m_claimFile;
|
||||
bool m_lazyLoadClaim;
|
||||
bool m_hasClaim;
|
||||
bool m_remoteValidated;
|
||||
Variant m_claim;
|
||||
shared_ptr<Key> m_rootKey;
|
||||
shared_ptr<Key> m_clientKey;
|
||||
};
|
||||
|
||||
class SharedClaimImpl: public SharedClaim {
|
||||
public:
|
||||
SharedClaimImpl(AuthenticationImpl* parent, Variant const& claim) {
|
||||
m_parent = parent;
|
||||
m_claim = claim;
|
||||
m_clientKey = make_shared<Key>();
|
||||
m_clientKey->loadPublicKey(m_claim.get("claim").get("metadata").get("clientPublicKey").toString());
|
||||
}
|
||||
|
||||
bool validateClaim() {
|
||||
return AuthenticationService::validateClientClaim(*(m_parent->m_rootKey), m_claim);
|
||||
}
|
||||
|
||||
Variant claim() {
|
||||
return m_claim;
|
||||
}
|
||||
|
||||
String encrypt(String const& message) {
|
||||
return m_clientKey->encryptMessage(message);
|
||||
}
|
||||
|
||||
bool verify(String const& message, String const& signature) {
|
||||
return m_clientKey->verifyMessage(message, signature);
|
||||
}
|
||||
|
||||
String username() {
|
||||
return m_claim.get("claim").get("metadata").getString("username");
|
||||
}
|
||||
|
||||
AuthenticationImpl* m_parent;
|
||||
Variant m_claim;
|
||||
shared_ptr<Key> m_clientKey;
|
||||
};
|
||||
|
||||
void Authentication::load() {
|
||||
MutexLocker locker(m_mutex);
|
||||
if (!m_impl)
|
||||
m_impl = make_shared<AuthenticationImpl>(Root::singleton().configuration());
|
||||
}
|
||||
|
||||
bool Authentication::validateClaim(bool remoteValidate) {
|
||||
if (remoteValidate) {
|
||||
auto remotevalidationimpl = make_shared<AuthenticationImpl>(Root::singleton().configuration());
|
||||
bool result = remotevalidationimpl->validateClaim(true);
|
||||
{
|
||||
MutexLocker locker(m_mutex);
|
||||
m_impl = remotevalidationimpl;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
MutexLocker locker(m_mutex);
|
||||
return m_impl->validateClaim(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Authentication::logon(String const& username, String const& passwordHash) {
|
||||
MutexLocker locker(m_mutex);
|
||||
return m_impl->logon(username, passwordHash);
|
||||
}
|
||||
|
||||
Variant Authentication::claim() {
|
||||
MutexLocker locker(m_mutex);
|
||||
return m_impl->claim();
|
||||
}
|
||||
|
||||
String Authentication::decrypt(String const& message) {
|
||||
MutexLocker locker(m_mutex);
|
||||
return m_impl->decrypt(message);
|
||||
}
|
||||
|
||||
String Authentication::sign(String const& message) {
|
||||
MutexLocker locker(m_mutex);
|
||||
return m_impl->sign(message);
|
||||
}
|
||||
|
||||
SharedClaimPtr Authentication::sharedClaim(Variant const& data) {
|
||||
MutexLocker locker(m_mutex);
|
||||
if (!m_impl)
|
||||
throw StarException("Invalid state");
|
||||
return make_shared<SharedClaimImpl>(m_impl.get(), data);
|
||||
}
|
||||
|
||||
}
|
46
attic/old_source/StarAuthentication.hpp
Normal file
46
attic/old_source/StarAuthentication.hpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef STAR_AUTHENTICATION_HPP
|
||||
#define STAR_AUTHENTICATION_HPP
|
||||
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(AuthenticationImpl);
|
||||
STAR_CLASS(SharedClaim);
|
||||
STAR_CLASS(Authentication);
|
||||
|
||||
class SharedClaim {
|
||||
public:
|
||||
virtual ~SharedClaim() {};
|
||||
virtual bool validateClaim() = 0;
|
||||
|
||||
virtual Variant claim() = 0;
|
||||
virtual String encrypt(String const& message) = 0;
|
||||
virtual bool verify(String const& message, String const& signature) = 0;
|
||||
|
||||
virtual String username() = 0;
|
||||
};
|
||||
|
||||
class Authentication {
|
||||
public:
|
||||
void load();
|
||||
|
||||
bool validateClaim(bool remoteValidate);
|
||||
bool logon(String const& username, String const& passwordHash);
|
||||
|
||||
Variant claim();
|
||||
|
||||
String decrypt(String const& message);
|
||||
String sign(String const& message);
|
||||
|
||||
SharedClaimPtr sharedClaim(Variant const& data);
|
||||
|
||||
private:
|
||||
AuthenticationImplPtr m_impl;
|
||||
Mutex m_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
64
attic/old_source/StarCircle.hpp
Normal file
64
attic/old_source/StarCircle.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef STAR_CIRCLE_HPP
|
||||
#define STAR_CIRCLE_HPP
|
||||
|
||||
#include "StarVector.hpp"
|
||||
#include "StarLine.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
template<typename T>
|
||||
class Circle {
|
||||
public:
|
||||
typedef Vector<T, 2> Vec2T;
|
||||
typedef Line<T, 2> LineT;
|
||||
Circle();
|
||||
Circle(Vec2T const& pos, T radius);
|
||||
|
||||
bool intersects(Vec2T const& point);
|
||||
Vec2T pointAtAngle(T angle);
|
||||
T angleNearestTo(Vec2T const& point);
|
||||
Vec2T pointNearestTo(Vec2T const& point);
|
||||
|
||||
private:
|
||||
T m_radius;
|
||||
Vec2T m_position;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Circle<T>::Circle()
|
||||
: m_radius(0) { }
|
||||
|
||||
template<typename T>
|
||||
Circle<T>::Circle(Vec2T const& pos, T radius)
|
||||
: m_radius(radius),
|
||||
m_position(pos) { }
|
||||
|
||||
template<typename T>
|
||||
bool Circle<T>::intersects(Vec2T const& point) {
|
||||
T distSqu = (point - m_position).magnitudeSquared();
|
||||
return distSqu <= m_radius * m_radius;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto Circle<T>::pointAtAngle(T angle) -> Vec2T {
|
||||
auto fromCenter = Vec2T::withAngle(angle, m_radius);
|
||||
return fromCenter + m_position;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto Circle<T>::angleNearestTo(Vec2T const& point) -> T {
|
||||
return (point - m_position).angle();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto Circle<T>::pointNearestTo(Vec2T const& point) -> Vec2T {
|
||||
T angle = angleNearestTo(point);
|
||||
return pointAtAngle(angle);
|
||||
}
|
||||
|
||||
typedef Circle<float> CirF;
|
||||
typedef Circle<double> CirD;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
303
attic/old_source/StarFlowingLiquidAgent.cpp
Normal file
303
attic/old_source/StarFlowingLiquidAgent.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
#include "StarFlowingLiquidAgent.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarLiquidsDatabase.hpp"
|
||||
#include "StarLivingWorldAgent.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
FlowingLiquidAgent::FlowingLiquidAgent() {
|
||||
m_livingWorld = {};
|
||||
}
|
||||
|
||||
void FlowingLiquidAgent::bind(LivingWorldFacadePtr world, LivingWorldAgent* livingWorld) {
|
||||
this->m_world = world;
|
||||
m_livingWorld = livingWorld;
|
||||
}
|
||||
|
||||
void FlowingLiquidAgent::processLocation(Vec2I const& c) {
|
||||
auto level = getLiquidLevel(c.x(), c.y());
|
||||
int fountainPressure = 0;
|
||||
|
||||
if (level.liquid != Liquid::SolidTileLiquidPseudoId) {
|
||||
if (!hasBackground(c.x(), c.y()) && isOcean(c.x(), c.y())) {
|
||||
auto liquid = oceanLiquid(c.x(), c.y());
|
||||
auto pressure = oceanLiquidPressure(c.x(), c.y());
|
||||
if ((level.liquid != liquid)||(level.level != pressure)) {
|
||||
setLiquidLevel(c.x(), c.y(), LiquidLevel{liquid, pressure});
|
||||
level = getLiquidLevel(c.x(), c.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (level.level >= Liquid::OverflowFullLiquidLevel)
|
||||
if (moveLiquidUpwards(c, level)) {
|
||||
auto l = getLiquidLevel(c.x(), c.y());
|
||||
fountainPressure = (int) level.level - (int) l.level;
|
||||
level = l;
|
||||
}
|
||||
if (level.level > 0)
|
||||
if (moveLiquidDown(c, level, true)) {
|
||||
level = getLiquidLevel(c.x(), c.y());
|
||||
fountainPressure = 0;
|
||||
}
|
||||
if (level.level >= Liquid::TrivialLevelThreshold) {
|
||||
moveLiquidSideWays(c, fountainPressure);
|
||||
}
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::moveLiquidDown(Vec2I c, LiquidLevel above, bool attemptMoveOut) {
|
||||
auto liquidsDatabase = Root::singleton().liquidsDatabase();
|
||||
|
||||
if (above.liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
return false;
|
||||
auto below = getLiquidLevel(c.x(), c.y() - 1);
|
||||
if (below.liquid == Liquid::SolidTileLiquidPseudoId) {
|
||||
if (attemptMoveOut)
|
||||
moveLiquidOut(c);
|
||||
return false;
|
||||
}
|
||||
if ((c.y() <= 0) && liquidsDatabase->liquidSettings(above.liquid)->endless)
|
||||
return false;
|
||||
|
||||
int move = std::max(0,
|
||||
std::min(std::min((int) above.level, (Liquid::MaxLiquidLevel - 1) - (int) below.level),
|
||||
std::max((Liquid::OverflowFullLiquidLevel - 1) - (int) below.level,
|
||||
std::min((Liquid::PressurePerLevel - ((int) below.level - (int) above.level)) / 2,
|
||||
(int) above.level - (Liquid::TrivialLevelThreshold - 1)))));
|
||||
|
||||
move *= std::min(liquidsDatabase->liquidSettings(below.liquid)->downwardsSpeedModifier, liquidsDatabase->liquidSettings(above.liquid)->downwardsSpeedModifier);
|
||||
|
||||
above.level -= move;
|
||||
below.level += move;
|
||||
below.liquid = above.liquid;
|
||||
|
||||
if (move > 0) {
|
||||
moveLiquid(c.x(), c.y(), 0, -1, above, below);
|
||||
return true;
|
||||
} else {
|
||||
if (attemptMoveOut)
|
||||
moveLiquidOut(c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::moveLiquidSideWays(Vec2I c, int fountain) {
|
||||
fountain /= Liquid::PressurePerLevel;
|
||||
auto below = getLiquidLevel(c.x(), c.y() - 1);
|
||||
if ((below.liquid != Liquid::SolidTileLiquidPseudoId) && (below.level < Liquid::FullLiquidLevel))
|
||||
return false;
|
||||
bool result = false;
|
||||
int move = 0;
|
||||
while (true) {
|
||||
move++;
|
||||
int bdx = 0;
|
||||
int bl = 0;
|
||||
int l;
|
||||
auto level = getLiquidLevel(c.x(), c.y());
|
||||
if (level.liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
break;
|
||||
int idx = 0;
|
||||
int preferredDirection = m_random.randInt(1);
|
||||
|
||||
for (int dx = -1; dx <= 1; dx += 2) {
|
||||
l = checkMoveLiquidSideways(c.x() + dx, c.y(), level);
|
||||
if ((l > bl) || ((l == bl) && (idx == preferredDirection))) {
|
||||
bl = l;
|
||||
bdx = dx;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (bl == 0)
|
||||
break;
|
||||
|
||||
auto source = getLiquidLevel(c.x(), c.y());
|
||||
auto target = getLiquidLevel(c.x() + bdx, c.y());
|
||||
|
||||
if ((source.level > bl) && ((int) source.level - bl > (int) target.level + bl))
|
||||
bl++;
|
||||
|
||||
if ((fountain > 0) && (m_random.randInt(5 * fountain) != 0))
|
||||
bl /= fountain * 10;
|
||||
|
||||
if ((bl >= Liquid::TrivialSidewaysMoveThreshold)
|
||||
&& (((int) source.level - bl) >= Liquid::TrivialLevelThreshold)
|
||||
&& (((int) target.level + bl) >= Liquid::TrivialLevelThreshold)) {
|
||||
|
||||
source.level -= bl;
|
||||
target.level += bl;
|
||||
target.liquid = source.liquid;
|
||||
moveLiquid(c.x(), c.y(), bdx, 0, source, target);
|
||||
moveLiquidDown(Vec2I(c.x() + bdx, c.y()), getLiquidLevel(c.x() + bdx, c.y()), false);
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
if (target.level <= Liquid::IdleLevelEvaporateThreshold) {
|
||||
source.level = std::max(0, source.level - Liquid::IdleLevelEvaporateThreshold);
|
||||
setLiquidLevel(c.x(), c.y(), source);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int FlowingLiquidAgent::checkMoveLiquidSideways(int x, int y, LiquidLevel level) {
|
||||
if (level.liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
return 0;
|
||||
auto beside = getLiquidLevel(x, y);
|
||||
if (beside.liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
return 0;
|
||||
int result = (((int) level.level - (int) beside.level) * 1.9) / 2;
|
||||
auto liquidsDatabase = Root::singleton().liquidsDatabase();
|
||||
result *= std::min(liquidsDatabase->liquidSettings(level.liquid)->sidewaysSpeedModifier, liquidsDatabase->liquidSettings(beside.liquid)->sidewaysSpeedModifier);
|
||||
if (result > level.level)
|
||||
result = level.level;
|
||||
if (result <= 0)
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::moveLiquidUpwards(Vec2I c, LiquidLevel level) {
|
||||
if (level.liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
return false;
|
||||
if (level.level < Liquid::OverflowFullLiquidLevel) {
|
||||
return false;
|
||||
}
|
||||
auto above = getLiquidLevel(c.x(), c.y() + 1);
|
||||
if (above.liquid == Liquid::SolidTileLiquidPseudoId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int move = std::max(0,
|
||||
std::min((int) level.level - (Liquid::OverflowFullLiquidLevel -1),
|
||||
(((int) level.level - (int) above.level) - (Liquid::PressurePerLevel)) / 2));
|
||||
|
||||
if (above.liquid == NullLiquidId)
|
||||
above.liquid = level.liquid;
|
||||
|
||||
auto liquidsDatabase = Root::singleton().liquidsDatabase();
|
||||
move *= std::min(liquidsDatabase->liquidSettings(level.liquid)->upwardsSpeedModifier, liquidsDatabase->liquidSettings(above.liquid)->upwardsSpeedModifier);
|
||||
move = std::min(move, std::min((int) level.level, (Liquid::MaxLiquidLevel - 1) - (int) above.level));
|
||||
|
||||
above.level += move;
|
||||
level.level -= move;
|
||||
above.liquid = level.liquid;
|
||||
|
||||
if ((move >= Liquid::TrivialUpwardsMoveThreshold)
|
||||
&& (above.level >= Liquid::TrivialLevelThreshold)) {
|
||||
moveLiquid(c.x(), c.y(), 0, 1, level, above);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::moveLiquidOut(Vec2I c) {
|
||||
if (!hasBackground(c.x(), c.y()) && !isOcean(c.x(), c.y())) {
|
||||
auto current = getLiquidLevel(c.x(), c.y());
|
||||
auto liquidsDatabase = Root::singleton().liquidsDatabase();
|
||||
auto setting = liquidsDatabase->liquidSettings(current.liquid);
|
||||
if (setting->endless)
|
||||
return false;
|
||||
// Move Liquid into the background (disappear) at the BackgroundFlowPercentage
|
||||
current.level *= 1.0f - setting->backgroundFlowPercentage;
|
||||
setLiquidLevel(c.x(), c.y(), current);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::sanityCheckLiquid(LiquidLevel const& level) const {
|
||||
if (((level.liquid != Liquid::SolidTileLiquidPseudoId)&&(level.liquid != NullLiquidId)) == (level.level == 0)) {
|
||||
Logger::warn("Inconsistent liquid data. Liquid:%s Level:%s", level.liquid, level.level);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LiquidLevel FlowingLiquidAgent::getLiquidLevel(int x, int y) {
|
||||
LiquidLevel level = m_world->readTileLiquid(x, y);
|
||||
#ifndef NDEBUG
|
||||
sanityCheckLiquid(level);
|
||||
#endif
|
||||
return level;
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::hasBackground(int x, int y) {
|
||||
return m_world->hasBackground(x, y);
|
||||
}
|
||||
|
||||
bool FlowingLiquidAgent::isOcean(int x, int y) {
|
||||
return m_world->isOcean(x, y);
|
||||
}
|
||||
|
||||
LiquidId FlowingLiquidAgent::oceanLiquid(int x, int y) {
|
||||
return m_world->oceanLiquid(x, y);
|
||||
}
|
||||
|
||||
uint16_t FlowingLiquidAgent::oceanLiquidPressure(int x, int y) {
|
||||
return m_world->oceanLiquidPressure(x, y);
|
||||
}
|
||||
|
||||
void FlowingLiquidAgent::moveLiquid(int x, int y, int dx, int dy,
|
||||
LiquidLevel proposedSourceLevel, LiquidLevel proposedTargetLevel) {
|
||||
setLiquidLevel(x, y, proposedSourceLevel);
|
||||
setLiquidLevel(x + dx, y + dy, proposedTargetLevel);
|
||||
}
|
||||
|
||||
void FlowingLiquidAgent::setLiquidLevel(int x, int y, LiquidLevel level) {
|
||||
if (level.level <= Liquid::TrivialLevelThreshold)
|
||||
level.level = 0;
|
||||
if (level.level == 0)
|
||||
level.liquid = NullLiquidId;
|
||||
auto prevLevel = getLiquidLevel(x, y);
|
||||
auto liquidsDatabase = Root::singleton().liquidsDatabase();
|
||||
auto prevLevelSetting = liquidsDatabase->liquidSettings(prevLevel.liquid);
|
||||
if (prevLevelSetting->endless)
|
||||
return; // can never update a endlesswater tile after generation
|
||||
if ((prevLevel.liquid == level.liquid) && (prevLevel.level == level.level))
|
||||
return;
|
||||
starAssert(prevLevel.liquid != Liquid::SolidTileLiquidPseudoId);
|
||||
|
||||
MaterialId blockId = NullMaterialId;
|
||||
MaterialHue blockHueShift = MaterialHue();
|
||||
|
||||
float flow = fabs((float) level.level - (float) prevLevel.level);
|
||||
auto effect = liquidsDatabase->liquidSettings(level.liquid);
|
||||
level.liquid = effect->flowsAs;
|
||||
float chance = effect->blockGenerationChance * fmin((float)flow / (float)Liquid::FullLiquidLevel, 1.0f);
|
||||
if (Random::randf() < chance) {
|
||||
if (!effect->connectedOnly
|
||||
||(getLiquidLevel(x-1, y).liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
||(getLiquidLevel(x+1, y).liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
||(getLiquidLevel(x, y-1).liquid == Liquid::SolidTileLiquidPseudoId)
|
||||
||(getLiquidLevel(x, y+1).liquid == Liquid::SolidTileLiquidPseudoId)) {
|
||||
blockId = Random::randFrom(effect->blockOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if ((level.liquid != NullLiquidId) && (level.liquid != Liquid::SolidTileLiquidPseudoId) && (prevLevel.liquid != NullLiquidId) && (prevLevel.liquid != Liquid::SolidTileLiquidPseudoId)
|
||||
&& (level.liquid != prevLevel.liquid)) {
|
||||
auto& interaction = effect->liquidInteraction[prevLevel.liquid];
|
||||
if (Random::randf() < interaction.blockGenerationChance) {
|
||||
blockId = Random::randFrom(interaction.blockOptions);
|
||||
} else {
|
||||
float chance = flow / (flow + prevLevel.level);
|
||||
if (Random::randf() >= chance)
|
||||
level.liquid = prevLevel.liquid;
|
||||
}
|
||||
}
|
||||
|
||||
if ((blockId != NullMaterialId) && (blockId != EmptyMaterialId)) {
|
||||
m_world->writeTileMaterial(x, y, blockId, blockHueShift, true);
|
||||
} else {
|
||||
if (!sanityCheckLiquid(level))
|
||||
return;
|
||||
m_world->writeTileLiquid(x, y, level);
|
||||
}
|
||||
m_livingWorld->visitRegion(x, y, true);
|
||||
}
|
||||
|
||||
}
|
47
attic/old_source/StarFlowingLiquidAgent.hpp
Normal file
47
attic/old_source/StarFlowingLiquidAgent.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef STAR_FLOWING_LIQUID_AGENT_HPP
|
||||
#define STAR_FLOWING_LIQUID_AGENT_HPP
|
||||
|
||||
#include "StarRandom.hpp"
|
||||
#include "StarVector.hpp"
|
||||
#include "StarLiquidTypes.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(LivingWorldAgent);
|
||||
STAR_CLASS(LivingWorldFacade);
|
||||
STAR_CLASS(FlowingLiquidAgent);
|
||||
|
||||
class FlowingLiquidAgent {
|
||||
public:
|
||||
FlowingLiquidAgent();
|
||||
|
||||
void bind(LivingWorldFacadePtr world, LivingWorldAgent* livingWorld);
|
||||
void processLocation(Vec2I const& location);
|
||||
|
||||
private:
|
||||
LiquidLevel getLiquidLevel(int x, int y);
|
||||
bool hasBackground(int x, int y);
|
||||
bool isOcean(int x, int y);
|
||||
LiquidId oceanLiquid(int x, int y);
|
||||
uint16_t oceanLiquidPressure(int x, int y);
|
||||
|
||||
|
||||
void moveLiquid(int x, int y, int dx, int dy, LiquidLevel proposedSourceLevel, LiquidLevel proposedTargetLevel);
|
||||
void setLiquidLevel(int x, int y, LiquidLevel level);
|
||||
|
||||
bool moveLiquidDown(Vec2I c, LiquidLevel above, bool attemptMoveOut);
|
||||
bool moveLiquidSideWays(Vec2I c, int fountain);
|
||||
int checkMoveLiquidSideways(int x, int y, LiquidLevel level);
|
||||
bool moveLiquidUpwards(Vec2I c, LiquidLevel level);
|
||||
bool moveLiquidOut(Vec2I c);
|
||||
|
||||
bool sanityCheckLiquid(LiquidLevel const& level) const;
|
||||
|
||||
LivingWorldAgent* m_livingWorld;
|
||||
RandomSource m_random;
|
||||
LivingWorldFacadePtr m_world;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
39
attic/old_source/StarLiveCounter.cpp
Normal file
39
attic/old_source/StarLiveCounter.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "StarLiveCounter.hpp"
|
||||
#include "StarMap.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
struct LiveCounterData {
|
||||
Mutex lock;
|
||||
HashMap<std::type_index, unique_ptr<LiveAtomicCounter>> counters;
|
||||
};
|
||||
|
||||
LiveCounterData& liveCounterData() {
|
||||
static LiveCounterData data;
|
||||
return data;
|
||||
};
|
||||
|
||||
void bindLiveCounter(std::type_index const& typeIndex, LiveAtomicCounter*& counter) {
|
||||
if (!counter) {
|
||||
auto& data = liveCounterData();
|
||||
MutexLocker locker(data.lock);
|
||||
auto& ptr = data.counters[typeIndex];
|
||||
if (!ptr)
|
||||
ptr = make_unique<LiveAtomicCounter>();
|
||||
counter = ptr.get();
|
||||
}
|
||||
}
|
||||
|
||||
void dumpLiveCounters() {
|
||||
#ifdef STAR_ENABLE_LIVECOUNTER
|
||||
auto& data = liveCounterData();
|
||||
Logger::info("LiveCounters");
|
||||
MutexLocker locker(data.lock);
|
||||
for (auto const& e : data.counters)
|
||||
Logger::info(" %s %s", e.first.name(), e.second->load());
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
54
attic/old_source/StarLiveCounter.hpp
Normal file
54
attic/old_source/StarLiveCounter.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef STAR_LIVE_COUNTER_HPP
|
||||
#define STAR_LIVE_COUNTER_HPP
|
||||
|
||||
#include "StarConfig.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
namespace Star {
|
||||
|
||||
typedef atomic<int64_t> LiveAtomicCounter;
|
||||
|
||||
void bindLiveCounter(std::type_index const& typeIndex, LiveAtomicCounter*& counter);
|
||||
void dumpLiveCounters();
|
||||
|
||||
// Use as class MyClass : LiveCounter<MyClass> { ... }
|
||||
template<typename T>
|
||||
class LiveCounter {
|
||||
public:
|
||||
#ifdef STAR_ENABLE_LIVECOUNTER
|
||||
LiveCounter() {
|
||||
bindLiveCounter(typeid(T), s_liveCounter);
|
||||
++(*s_liveCounter);
|
||||
}
|
||||
|
||||
LiveCounter(LiveCounter const&) {
|
||||
bindLiveCounter(typeid(T), s_liveCounter);
|
||||
++(*s_liveCounter);
|
||||
}
|
||||
|
||||
LiveCounter(LiveCounter&&) {
|
||||
bindLiveCounter(typeid(T), s_liveCounter);
|
||||
++(*s_liveCounter);
|
||||
}
|
||||
|
||||
void operator=(LiveCounter const&) {}
|
||||
|
||||
void operator=(LiveCounter&&) {}
|
||||
|
||||
~LiveCounter() {
|
||||
--(*s_liveCounter);
|
||||
}
|
||||
private:
|
||||
static LiveAtomicCounter* s_liveCounter;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef STAR_ENABLE_LIVECOUNTER
|
||||
template<typename T>
|
||||
LiveAtomicCounter* LiveCounter<T>::s_liveCounter = nullptr;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
705
attic/old_source/StarNetStates.cpp
Normal file
705
attic/old_source/StarNetStates.cpp
Normal file
|
@ -0,0 +1,705 @@
|
|||
#include "StarNetStates.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
bool NetStatesField::pullUpdated() const {
|
||||
return m_field->pullUpdated();
|
||||
}
|
||||
|
||||
bool NetStatesField::connected() const {
|
||||
return !m_field.unique();
|
||||
}
|
||||
|
||||
NetStatesField::NetStatesField(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value)
|
||||
: m_field(make_shared<NetStatesDetail::Field>(move(typeInfo), move(value))) {}
|
||||
|
||||
NetStatesInt NetStatesInt::makeInt8(int64_t initialValue) {
|
||||
return NetStatesInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Int8, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesInt NetStatesInt::makeInt16(int64_t initialValue) {
|
||||
return NetStatesInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Int16, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesInt NetStatesInt::makeVarInt(int64_t initialValue) {
|
||||
return NetStatesInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::VarInt, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesInt::NetStatesInt(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value)
|
||||
: NetStatesField(move(typeInfo), move(value)) {}
|
||||
|
||||
int64_t NetStatesInt::get() const {
|
||||
return m_field->getInt();
|
||||
}
|
||||
|
||||
void NetStatesInt::set(int64_t value) {
|
||||
m_field->setInt(value);
|
||||
}
|
||||
|
||||
NetStatesUInt NetStatesUInt::makeUInt8(uint64_t initialValue) {
|
||||
return NetStatesUInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::UInt8, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesUInt NetStatesUInt::makeUInt16(uint64_t initialValue) {
|
||||
return NetStatesUInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::UInt16, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesUInt NetStatesUInt::makeVarUInt(uint64_t initialValue) {
|
||||
return NetStatesUInt(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::VarUInt, {}, {}, {}}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesUInt::NetStatesUInt(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value)
|
||||
: NetStatesField(move(typeInfo), move(value)) {}
|
||||
|
||||
uint64_t NetStatesUInt::get() const {
|
||||
return m_field->getUInt();
|
||||
}
|
||||
|
||||
void NetStatesUInt::set(uint64_t value) {
|
||||
m_field->setUInt(value);
|
||||
}
|
||||
|
||||
NetStatesSize::NetStatesSize(size_t initialValue)
|
||||
: NetStatesField(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Size, {}, {}, {}}, initialValue) {}
|
||||
|
||||
size_t NetStatesSize::get() const {
|
||||
return m_field->getSize();
|
||||
}
|
||||
|
||||
void NetStatesSize::set(size_t value) {
|
||||
m_field->setSize(value);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeFloat(double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Float, {}, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeDouble(double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Double, {}, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeNormalizedFloat8(double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::NFloat8, {}, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeNormalizedFloat16(double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::NFloat16, {}, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeFixedPoint8(double base, double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Fixed8, base, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeFixedPoint16(double base, double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Fixed16, base, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
NetStatesFloat NetStatesFloat::makeFixedPoint(double base, double initialValue, NetStatesInterpolator interpolator) {
|
||||
return NetStatesFloat(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::VarFixed, base, {}, move(interpolator)}, initialValue);
|
||||
}
|
||||
|
||||
double NetStatesFloat::get() const {
|
||||
return m_field->getFloat();
|
||||
}
|
||||
|
||||
void NetStatesFloat::set(double value) {
|
||||
m_field->setFloat(value);
|
||||
}
|
||||
|
||||
void NetStatesFloat::setInterpolator(NetStatesInterpolator interpolator) {
|
||||
m_field->setFloatInterpolator(move(interpolator));
|
||||
}
|
||||
|
||||
NetStatesFloat::NetStatesFloat(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value)
|
||||
: NetStatesField(move(typeInfo), move(value)) {}
|
||||
|
||||
NetStatesBool::NetStatesBool(bool initialValue)
|
||||
: NetStatesField(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Bool, {}, {}, {}}, initialValue) {}
|
||||
|
||||
bool NetStatesBool::get() const {
|
||||
return m_field->getBool();
|
||||
}
|
||||
|
||||
void NetStatesBool::set(bool value) {
|
||||
m_field->setBool(value);
|
||||
}
|
||||
|
||||
NetStatesEvent::NetStatesEvent()
|
||||
: NetStatesField(NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::Event, {}, {}, {}}, (uint64_t)0) {}
|
||||
|
||||
void NetStatesEvent::trigger() {
|
||||
m_field->triggerEvent();
|
||||
}
|
||||
|
||||
uint64_t NetStatesEvent::pullOccurrences() {
|
||||
return m_field->pullOccurrences();
|
||||
}
|
||||
|
||||
bool NetStatesEvent::pullOccurred() {
|
||||
return pullOccurrences() != 0;
|
||||
}
|
||||
|
||||
void NetStatesEvent::ignoreOccurrences() {
|
||||
m_field->ignoreOccurrences();
|
||||
}
|
||||
|
||||
NetStepStates::NetStepStates() {
|
||||
clearFields();
|
||||
}
|
||||
|
||||
void NetStepStates::addField(NetStatesField const& nsfield) {
|
||||
if (nsfield.connected())
|
||||
throw NetStatesException("Field added in NetStepStates is already connected");
|
||||
|
||||
auto field = nsfield.m_field;
|
||||
field->updateStep(m_currentStep);
|
||||
if (m_interpolationEnabled)
|
||||
field->enableInterpolation(m_extrapolationSteps);
|
||||
m_fields.append(field);
|
||||
}
|
||||
|
||||
void NetStepStates::clearFields() {
|
||||
for (auto const& field : m_fields) {
|
||||
field->disableInterpolation();
|
||||
field->resetStep();
|
||||
}
|
||||
|
||||
m_fields.clear();
|
||||
m_currentStep = 0;
|
||||
m_interpolationEnabled = false;
|
||||
m_extrapolationSteps = 0;
|
||||
}
|
||||
|
||||
uint32_t NetStepStates::fieldDigest() const {
|
||||
uint32_t digest;
|
||||
fnv_hash32_init(digest);
|
||||
for (auto const& field : m_fields) {
|
||||
auto const& typeInfo = field->typeInfo();
|
||||
uint8_t type = (uint8_t)typeInfo.type;
|
||||
float base = typeInfo.fixedPointBase.value(0.0f);
|
||||
toBigEndian(type);
|
||||
toBigEndian(base);
|
||||
fnv_hash32_bytes(digest, (char const*)&type, sizeof(type));
|
||||
fnv_hash32_bytes(digest, (char const*)&base, sizeof(base));
|
||||
}
|
||||
return digest;
|
||||
}
|
||||
|
||||
bool NetStepStates::updateStep(uint64_t step) {
|
||||
if (step < m_currentStep) {
|
||||
throw NetStatesException("step decreased in NetStepStates::updateStep");
|
||||
} else if (step > m_currentStep) {
|
||||
m_currentStep = step;
|
||||
bool updated = false;
|
||||
for (auto const& field : m_fields)
|
||||
updated |= field->updateStep(m_currentStep);
|
||||
return updated;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t NetStepStates::currentStep() const {
|
||||
return m_currentStep;
|
||||
}
|
||||
|
||||
void NetStepStates::resetStep() {
|
||||
m_currentStep = 0;
|
||||
for (auto& field : m_fields)
|
||||
field->resetStep();
|
||||
}
|
||||
|
||||
void NetStepStates::enableInterpolation(uint64_t extrapolationSteps) {
|
||||
m_interpolationEnabled = true;
|
||||
m_extrapolationSteps = extrapolationSteps;
|
||||
for (auto& field : m_fields)
|
||||
field->enableInterpolation(extrapolationSteps);
|
||||
}
|
||||
|
||||
void NetStepStates::disableInterpolation() {
|
||||
m_interpolationEnabled = false;
|
||||
m_extrapolationSteps = 0.0;
|
||||
for (auto& field : m_fields)
|
||||
field->disableInterpolation();
|
||||
}
|
||||
|
||||
bool NetStepStates::interpolationEnabled() const {
|
||||
return m_interpolationEnabled;
|
||||
}
|
||||
|
||||
bool NetStepStates::hasDelta(uint64_t fromStep) const {
|
||||
for (auto const& field : m_fields) {
|
||||
if (field->hasDelta(fromStep))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetStepStates::writeDelta(DataStream& ds, uint64_t fromStep) const {
|
||||
for (size_t i = 0; i < m_fields.size(); ++i) {
|
||||
auto const& field = m_fields[i];
|
||||
if (field->hasDelta(fromStep)) {
|
||||
ds.writeVlqU(i + 1);
|
||||
field->writeField(ds);
|
||||
}
|
||||
}
|
||||
ds.writeVlqU(0);
|
||||
}
|
||||
|
||||
bool NetStepStates::readDelta(DataStream& ds, double predictedDeltaStep) {
|
||||
bool updated = false;
|
||||
while (true) {
|
||||
uint64_t index = ds.readVlqU();
|
||||
if (index == 0)
|
||||
break;
|
||||
--index;
|
||||
updated |= m_fields.at(index)->readField(ds, predictedDeltaStep);
|
||||
}
|
||||
|
||||
interpolationHeartbeat(predictedDeltaStep);
|
||||
return updated;
|
||||
}
|
||||
|
||||
void NetStepStates::writeFull(DataStream& ds) const {
|
||||
ds.write(fieldDigest());
|
||||
for (auto const& field : m_fields)
|
||||
field->writeField(ds);
|
||||
}
|
||||
|
||||
void NetStepStates::readFull(DataStream& ds, double predictedDeltaStep) {
|
||||
auto digest = ds.read<uint32_t>();
|
||||
if (digest != fieldDigest())
|
||||
throw NetStatesException("Type mismatch in NetStepStates::readFull");
|
||||
|
||||
for (auto const& field : m_fields)
|
||||
field->readField(ds, predictedDeltaStep);
|
||||
}
|
||||
|
||||
ByteArray NetStepStates::writeDeltaPacket(uint64_t fromStep) const {
|
||||
DataStreamBuffer ds;
|
||||
|
||||
for (size_t i = 0; i < m_fields.size(); ++i) {
|
||||
auto const& field = m_fields[i];
|
||||
if (field->hasDelta(fromStep)) {
|
||||
ds.writeVlqU(i);
|
||||
field->writeField(ds);
|
||||
}
|
||||
}
|
||||
|
||||
return ds.takeData();
|
||||
}
|
||||
|
||||
bool NetStepStates::readDeltaPacket(ByteArray packet, double predictedDeltaStep) {
|
||||
DataStreamBuffer ds(move(packet));
|
||||
|
||||
bool updated = false;
|
||||
while (!ds.atEnd()) {
|
||||
uint64_t index = ds.readVlqU();
|
||||
updated |= m_fields.at(index)->readField(ds, predictedDeltaStep);
|
||||
}
|
||||
|
||||
interpolationHeartbeat(predictedDeltaStep);
|
||||
return updated;
|
||||
}
|
||||
|
||||
ByteArray NetStepStates::writeFullPacket() const {
|
||||
DataStreamBuffer ds;
|
||||
writeFull(ds);
|
||||
return ds.takeData();
|
||||
}
|
||||
|
||||
void NetStepStates::readFullPacket(ByteArray packet, double predictedDeltaStep) {
|
||||
DataStreamBuffer ds(move(packet));
|
||||
readFull(ds, predictedDeltaStep);
|
||||
}
|
||||
|
||||
void NetStepStates::interpolationHeartbeat(double predictedDeltaStep) {
|
||||
if (m_interpolationEnabled) {
|
||||
for (auto const& field : m_fields)
|
||||
field->interpolationHeartbeat(predictedDeltaStep);
|
||||
}
|
||||
}
|
||||
|
||||
NetSyncStates::NetSyncStates() {}
|
||||
|
||||
bool NetSyncStates::hasDelta() const {
|
||||
return NetStepStates::hasDelta(currentStep());
|
||||
}
|
||||
|
||||
void NetSyncStates::writeDelta(DataStream& ds) {
|
||||
NetStepStates::writeDelta(ds, currentStep());
|
||||
updateStep(currentStep() + 1);
|
||||
}
|
||||
|
||||
bool NetSyncStates::readDelta(DataStream& ds) {
|
||||
updateStep(currentStep() + 1);
|
||||
return NetStepStates::readDelta(ds, currentStep());
|
||||
}
|
||||
|
||||
ByteArray NetSyncStates::writeDeltaPacket() {
|
||||
auto packet = NetStepStates::writeDeltaPacket(currentStep());
|
||||
updateStep(currentStep() + 1);
|
||||
return packet;
|
||||
}
|
||||
|
||||
bool NetSyncStates::readDeltaPacket(ByteArray packet) {
|
||||
updateStep(currentStep() + 1);
|
||||
return NetStepStates::readDeltaPacket(move(packet), currentStep());
|
||||
}
|
||||
|
||||
void NetSyncStates::reset() {
|
||||
NetStepStates::resetStep();
|
||||
}
|
||||
|
||||
NetStatesDetail::Value NetStatesDetail::TypeInfo::valueFromTransmission(NetStatesDetail::TransmissionValue const& transmission) const {
|
||||
if (type == TransmissionType::Int8) {
|
||||
return (int64_t)transmission.get<int8_t>();
|
||||
} else if (type == TransmissionType::UInt8) {
|
||||
return (uint64_t)transmission.get<uint8_t>();
|
||||
} else if (type == TransmissionType::Int16) {
|
||||
return (int64_t)transmission.get<int16_t>();
|
||||
} else if (type == TransmissionType::UInt16) {
|
||||
return (uint64_t)transmission.get<uint16_t>();
|
||||
} else if (type == TransmissionType::VarInt) {
|
||||
return (int64_t)transmission.get<int64_t>();
|
||||
} else if (type == TransmissionType::VarUInt) {
|
||||
return (uint64_t)transmission.get<uint64_t>();
|
||||
} else if (type == TransmissionType::Size) {
|
||||
uint64_t u = transmission.get<uint64_t>();
|
||||
if (u == 0)
|
||||
return NPos;
|
||||
return (size_t)(u - 1);
|
||||
} else if (type == TransmissionType::Float) {
|
||||
return (double)transmission.get<float>();
|
||||
} else if (type == TransmissionType::Double) {
|
||||
return transmission.get<double>();
|
||||
} else if (type == TransmissionType::NFloat8) {
|
||||
return transmission.get<uint8_t>() / 255.0;
|
||||
} else if (type == TransmissionType::NFloat16) {
|
||||
return transmission.get<uint16_t>() / 65535.0;
|
||||
} if (type == TransmissionType::Fixed8) {
|
||||
return transmission.get<int8_t>() * *fixedPointBase;
|
||||
} else if (type == TransmissionType::Fixed16) {
|
||||
return transmission.get<int16_t>() * *fixedPointBase;
|
||||
} else if (type == TransmissionType::VarFixed) {
|
||||
return transmission.get<int64_t>() * *fixedPointBase;
|
||||
} else if (type == TransmissionType::Bool) {
|
||||
return transmission.get<bool>();
|
||||
} else if (type == TransmissionType::Event) {
|
||||
return transmission.get<uint64_t>();
|
||||
} else {
|
||||
return transmission.get<shared_ptr<void>>();
|
||||
}
|
||||
}
|
||||
|
||||
NetStatesDetail::TransmissionValue NetStatesDetail::TypeInfo::transmissionFromValue(NetStatesDetail::Value const& value) const {
|
||||
if (type == TransmissionType::Int8) {
|
||||
return (int8_t)value.get<int64_t>();
|
||||
} else if (type == TransmissionType::UInt8) {
|
||||
return (uint8_t)value.get<uint64_t>();
|
||||
} else if (type == TransmissionType::Int16) {
|
||||
return (int16_t)value.get<int64_t>();
|
||||
} else if (type == TransmissionType::UInt16) {
|
||||
return (uint16_t)value.get<uint64_t>();
|
||||
} else if (type == TransmissionType::VarInt) {
|
||||
return (int64_t)value.get<int64_t>();
|
||||
} else if (type == TransmissionType::VarUInt) {
|
||||
return (uint64_t)value.get<uint64_t>();
|
||||
} else if (type == TransmissionType::Size) {
|
||||
size_t s = value.get<size_t>();
|
||||
if (s == NPos)
|
||||
return (uint64_t)0;
|
||||
return (uint64_t)(s + 1);
|
||||
} else if (type == TransmissionType::Float) {
|
||||
return (float)value.get<double>();
|
||||
} else if (type == TransmissionType::Double) {
|
||||
return (double)value.get<double>();
|
||||
} else if (type == TransmissionType::NFloat8) {
|
||||
return (uint8_t)round(clamp(value.get<double>(), 0.0, 1.0) * 255.0);
|
||||
} else if (type == TransmissionType::NFloat16) {
|
||||
return (uint16_t)round(clamp(value.get<double>(), 0.0, 1.0) * 65535.0);
|
||||
} else if (type == TransmissionType::Fixed8) {
|
||||
return (int8_t)clamp<int64_t>(round(value.get<double>() / *fixedPointBase), -128, 127);
|
||||
} else if (type == TransmissionType::Fixed16) {
|
||||
return (int16_t)clamp<int64_t>(round(value.get<double>() / *fixedPointBase), -32768, 32767);
|
||||
} else if (type == TransmissionType::VarFixed) {
|
||||
return (int64_t)round(value.get<double>() / *fixedPointBase);
|
||||
} else if (type == TransmissionType::Bool) {
|
||||
return value.get<bool>();
|
||||
} else if (type == TransmissionType::Event) {
|
||||
return value.get<uint64_t>();
|
||||
} else {
|
||||
return value.get<shared_ptr<void>>();
|
||||
}
|
||||
}
|
||||
|
||||
NetStatesDetail::Field::Field(TypeInfo typeInfo, Value value)
|
||||
: m_typeInfo(move(typeInfo)), m_currentStep(0), m_value(move(value)),
|
||||
m_unpulledUpdate(true), m_pulledOccurrences(0), m_transmissionDirty(true),
|
||||
m_latestTransmissionStep(0) {
|
||||
freshenTransmission();
|
||||
}
|
||||
|
||||
NetStatesDetail::TypeInfo const& NetStatesDetail::Field::typeInfo() const {
|
||||
return m_typeInfo;
|
||||
}
|
||||
|
||||
int64_t NetStatesDetail::Field::getInt() const {
|
||||
return m_value.get<int64_t>();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setInt(int64_t value) {
|
||||
if (m_typeInfo.type == TransmissionType::Int8)
|
||||
value = clamp<int64_t>(value, -128, 127);
|
||||
else if (m_typeInfo.type == TransmissionType::Int16)
|
||||
value = clamp<int64_t>(value, -32768, 32767);
|
||||
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t NetStatesDetail::Field::getUInt() const {
|
||||
return m_value.get<uint64_t>();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setUInt(uint64_t value) {
|
||||
if (m_typeInfo.type == TransmissionType::UInt8)
|
||||
value = clamp<uint64_t>(value, 0, 255);
|
||||
else if (m_typeInfo.type == TransmissionType::UInt16)
|
||||
value = clamp<uint64_t>(value, 0, 65535);
|
||||
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
size_t NetStatesDetail::Field::getSize() const {
|
||||
return m_value.get<size_t>();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setSize(size_t value) {
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
double NetStatesDetail::Field::getFloat() const {
|
||||
return m_value.get<double>();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setFloat(double value) {
|
||||
if (m_typeInfo.type == TransmissionType::NFloat8 || m_typeInfo.type == TransmissionType::NFloat16)
|
||||
value = clamp<double>(value, 0.0, 1.0);
|
||||
else if (m_typeInfo.type == TransmissionType::Fixed8)
|
||||
value = clamp<double>(value, -128.0 * *m_typeInfo.fixedPointBase, 127.0 * *m_typeInfo.fixedPointBase);
|
||||
else if (m_typeInfo.type == TransmissionType::Fixed16)
|
||||
value = clamp<double>(value, -32768.0 * *m_typeInfo.fixedPointBase, 32767.0 * *m_typeInfo.fixedPointBase);
|
||||
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setFloatInterpolator(NetStatesInterpolator interpolator) {
|
||||
m_typeInfo.interpolator = move(interpolator);
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::getBool() const {
|
||||
return m_value.get<bool>();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::setBool(bool value) {
|
||||
if (m_value != value) {
|
||||
m_value = value;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::triggerEvent() {
|
||||
++m_value.get<uint64_t>();
|
||||
markValueUpdated();
|
||||
}
|
||||
|
||||
uint64_t NetStatesDetail::Field::pullOccurrences() {
|
||||
uint64_t occurrences = m_value.get<uint64_t>();
|
||||
starAssert(occurrences >= m_pulledOccurrences);
|
||||
uint64_t unchecked = occurrences - m_pulledOccurrences;
|
||||
m_pulledOccurrences = occurrences;
|
||||
return unchecked;
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::ignoreOccurrences() {
|
||||
m_pulledOccurrences = m_value.get<uint64_t>();
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::pullUpdated() {
|
||||
return take(m_unpulledUpdate);
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::updateStep(uint64_t step) {
|
||||
if (m_interpolatedTransmissions) {
|
||||
freshenTransmission();
|
||||
if (m_interpolatedTransmissions->updateStep(step))
|
||||
return updateValueFromTransmission();
|
||||
}
|
||||
m_currentStep = step;
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::resetStep() {
|
||||
freshenTransmission();
|
||||
m_currentStep = 0;
|
||||
m_latestTransmissionStep = 0;
|
||||
|
||||
if (m_interpolatedTransmissions) {
|
||||
m_interpolatedTransmissions->reset();
|
||||
m_interpolatedTransmissions->appendDataPoint(m_currentStep, m_latestTransmission);
|
||||
}
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::hasDelta(uint64_t fromStep) const {
|
||||
const_cast<Field*>(this)->freshenTransmission();
|
||||
return m_latestTransmissionStep >= fromStep;
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::readField(DataStream& ds, double interpolationStep) {
|
||||
freshenTransmission();
|
||||
|
||||
if (m_typeInfo.type == TransmissionType::Int8 || m_typeInfo.type == TransmissionType::Fixed8) {
|
||||
ds.read(m_latestTransmission.get<int8_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::UInt8 || m_typeInfo.type == TransmissionType::NFloat8) {
|
||||
ds.read(m_latestTransmission.get<uint8_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Int16 || m_typeInfo.type == TransmissionType::Fixed16) {
|
||||
ds.read(m_latestTransmission.get<int16_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::UInt16 || m_typeInfo.type == TransmissionType::NFloat16) {
|
||||
ds.read(m_latestTransmission.get<uint16_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::VarInt || m_typeInfo.type == TransmissionType::VarFixed) {
|
||||
m_latestTransmission.get<int64_t>() = ds.readVlqI();
|
||||
} else if (m_typeInfo.type == TransmissionType::VarUInt || m_typeInfo.type == TransmissionType::Size || m_typeInfo.type == TransmissionType::Event) {
|
||||
m_latestTransmission.get<uint64_t>() = ds.readVlqU();
|
||||
} else if (m_typeInfo.type == TransmissionType::Float) {
|
||||
ds.read(m_latestTransmission.get<float>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Double) {
|
||||
ds.read(m_latestTransmission.get<double>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Bool) {
|
||||
ds.read(m_latestTransmission.get<bool>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Generic) {
|
||||
m_latestTransmission = shared_ptr<void>(m_typeInfo.genericSerializer->read(ds));
|
||||
}
|
||||
|
||||
m_latestTransmissionStep = m_currentStep;
|
||||
|
||||
bool updated = true;
|
||||
if (m_interpolatedTransmissions)
|
||||
updated = m_interpolatedTransmissions->appendDataPoint(interpolationStep, m_latestTransmission);
|
||||
|
||||
if (updated)
|
||||
updateValueFromTransmission();
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::writeField(DataStream& ds) const {
|
||||
const_cast<Field*>(this)->freshenTransmission();
|
||||
|
||||
if (m_typeInfo.type == TransmissionType::Int8 || m_typeInfo.type == TransmissionType::Fixed8) {
|
||||
ds.write(m_latestTransmission.get<int8_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::UInt8 || m_typeInfo.type == TransmissionType::NFloat8) {
|
||||
ds.write(m_latestTransmission.get<uint8_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Int16 || m_typeInfo.type == TransmissionType::Fixed16) {
|
||||
ds.write(m_latestTransmission.get<int16_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::UInt16 || m_typeInfo.type == TransmissionType::NFloat16) {
|
||||
ds.write(m_latestTransmission.get<uint16_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::VarInt || m_typeInfo.type == TransmissionType::VarFixed) {
|
||||
ds.writeVlqI(m_latestTransmission.get<int64_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::VarUInt || m_typeInfo.type == TransmissionType::Size || m_typeInfo.type == TransmissionType::Event) {
|
||||
ds.writeVlqU(m_latestTransmission.get<uint64_t>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Float) {
|
||||
ds.write(m_latestTransmission.get<float>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Double) {
|
||||
ds.write(m_latestTransmission.get<double>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Bool) {
|
||||
ds.write(m_latestTransmission.get<bool>());
|
||||
} else if (m_typeInfo.type == TransmissionType::Generic) {
|
||||
m_typeInfo.genericSerializer->write(ds, m_latestTransmission.get<shared_ptr<void>>());
|
||||
}
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::enableInterpolation(uint64_t extrapolationSteps) {
|
||||
freshenTransmission();
|
||||
m_interpolatedTransmissions.emplace();
|
||||
m_interpolatedTransmissions->setExtrapolation(extrapolationSteps);
|
||||
m_interpolatedTransmissions->appendDataPoint(m_currentStep, m_latestTransmission);
|
||||
m_interpolatedTransmissions->updateStep(m_latestTransmissionStep);
|
||||
m_interpolatedTransmissions->heartbeat(m_currentStep);
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::disableInterpolation() {
|
||||
freshenTransmission();
|
||||
m_interpolatedTransmissions.reset();
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::interpolationHeartbeat(double predictedDeltaStep) {
|
||||
freshenTransmission();
|
||||
if (m_interpolatedTransmissions)
|
||||
m_interpolatedTransmissions->heartbeat(predictedDeltaStep);
|
||||
}
|
||||
|
||||
bool NetStatesDetail::Field::updateValueFromTransmission() {
|
||||
freshenTransmission();
|
||||
|
||||
Value newValue;
|
||||
TransmissionValue storeExactValue;
|
||||
if (m_interpolatedTransmissions) {
|
||||
if (m_typeInfo.interpolator &&
|
||||
(m_typeInfo.type == TransmissionType::Float || m_typeInfo.type == TransmissionType::Double ||
|
||||
m_typeInfo.type == TransmissionType::NFloat8 || m_typeInfo.type == TransmissionType::NFloat16 ||
|
||||
m_typeInfo.type == TransmissionType::Fixed8 || m_typeInfo.type == TransmissionType::Fixed16 ||
|
||||
m_typeInfo.type == TransmissionType::VarFixed)) {
|
||||
auto weighting = m_interpolatedTransmissions->weighting();
|
||||
double min = m_typeInfo.valueFromTransmission(weighting.pointMin).get<double>();
|
||||
double max = m_typeInfo.valueFromTransmission(weighting.pointMax).get<double>();
|
||||
newValue = m_typeInfo.interpolator(weighting.offset, min, max);
|
||||
} else {
|
||||
newValue = m_typeInfo.valueFromTransmission(m_interpolatedTransmissions->exact());
|
||||
}
|
||||
} else {
|
||||
newValue = m_typeInfo.valueFromTransmission(m_latestTransmission);
|
||||
}
|
||||
|
||||
if (m_value != newValue) {
|
||||
m_value = move(newValue);
|
||||
m_unpulledUpdate = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::markValueUpdated() {
|
||||
m_unpulledUpdate = true;
|
||||
m_transmissionDirty = true;
|
||||
}
|
||||
|
||||
void NetStatesDetail::Field::freshenTransmission() {
|
||||
if (m_transmissionDirty) {
|
||||
auto newTransmission = m_typeInfo.transmissionFromValue(m_value);
|
||||
if (newTransmission.is<shared_ptr<void>>() || newTransmission != m_latestTransmission) {
|
||||
m_latestTransmissionStep = m_currentStep;
|
||||
m_latestTransmission = move(newTransmission);
|
||||
}
|
||||
|
||||
if (m_interpolatedTransmissions) {
|
||||
m_interpolatedTransmissions->reset();
|
||||
m_interpolatedTransmissions->appendDataPoint(m_currentStep, m_latestTransmission);
|
||||
}
|
||||
m_transmissionDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
611
attic/old_source/StarNetStates.hpp
Normal file
611
attic/old_source/StarNetStates.hpp
Normal file
|
@ -0,0 +1,611 @@
|
|||
#ifndef STAR_NET_STATES_HPP
|
||||
#define STAR_NET_STATES_HPP
|
||||
|
||||
#include "StarAlgorithm.hpp"
|
||||
#include "StarAny.hpp"
|
||||
#include "StarDynamicArray.hpp"
|
||||
#include "StarDataStream.hpp"
|
||||
#include "StarStepInterpolation.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_EXCEPTION(NetStatesException, StarException);
|
||||
|
||||
STAR_CLASS(NetStepStates);
|
||||
|
||||
// NetStates values that can be interpolated can be interpolated using the following methods:
|
||||
//
|
||||
// - "exact" interpolation, meaning that the interpolator simply does not look
|
||||
// into the future at all and does no interpolation, which is the default.
|
||||
// This is the same method that is used for values that cannot be
|
||||
// interpolated at all.
|
||||
//
|
||||
// - "lerp" interpolation, or simple linear interpolation.
|
||||
//
|
||||
// - A custom interpolation function, for values that where it may not make
|
||||
// sense to use linear interpolation, such as circular values.
|
||||
typedef function<double(double, double, double)> NetStatesInterpolator;
|
||||
NetStatesInterpolator const NetStatesExactInterpolator = NetStatesInterpolator();
|
||||
NetStatesInterpolator const NetStatesLerpInterpolator = lerp<double, double>;
|
||||
NetStatesInterpolator const NetStatesAngleInterpolator = angleLerp<double, double>;
|
||||
|
||||
namespace NetStatesDetail {
|
||||
typedef Any<int64_t, uint64_t, size_t, double, bool, shared_ptr<void>> Value;
|
||||
|
||||
STAR_STRUCT(TypeInfo);
|
||||
STAR_CLASS(Field);
|
||||
|
||||
struct GenericSerializer {
|
||||
virtual ~GenericSerializer() = default;
|
||||
|
||||
virtual shared_ptr<void> read(DataStream& ds) const = 0;
|
||||
virtual void write(DataStream& ds, shared_ptr<void> const& data) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
// A handle to a single field in a NetStates object. Fields are designed to be
|
||||
// very fast to set and get, so they can generally be used in place of a
|
||||
// regular variable without much cost.
|
||||
class NetStatesField {
|
||||
public:
|
||||
NetStatesField(NetStatesField const&) = delete;
|
||||
NetStatesField(NetStatesField&&) = default;
|
||||
|
||||
NetStatesField& operator=(NetStatesField const&) = delete;
|
||||
NetStatesField& operator=(NetStatesField&&) = default;
|
||||
|
||||
// Pull whether or not the field has been updated since the last call to
|
||||
// pullUpdated.
|
||||
bool pullUpdated() const;
|
||||
|
||||
// Is this field connected to a NetStepStates object?
|
||||
bool connected() const;
|
||||
|
||||
protected:
|
||||
friend NetStepStates;
|
||||
|
||||
NetStatesField(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value);
|
||||
|
||||
NetStatesDetail::FieldPtr m_field;
|
||||
};
|
||||
|
||||
// NetStates integers are clamped BOTH when setting and when receiving delta
|
||||
// updates to the available bit width, 8, 16, or 64 bit.
|
||||
class NetStatesInt : public NetStatesField {
|
||||
public:
|
||||
static NetStatesInt makeInt8(int64_t initialValue = 0);
|
||||
static NetStatesInt makeInt16(int64_t initialValue = 0);
|
||||
static NetStatesInt makeVarInt(int64_t initialValue = 0);
|
||||
|
||||
NetStatesInt(NetStatesInt&&) = default;
|
||||
NetStatesInt& operator=(NetStatesInt&&) = default;
|
||||
|
||||
int64_t get() const;
|
||||
void set(int64_t value);
|
||||
|
||||
private:
|
||||
NetStatesInt(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value);
|
||||
};
|
||||
|
||||
class NetStatesUInt : public NetStatesField {
|
||||
public:
|
||||
static NetStatesUInt makeUInt8(uint64_t initialValue = 0);
|
||||
static NetStatesUInt makeUInt16(uint64_t initialValue = 0);
|
||||
static NetStatesUInt makeVarUInt(uint64_t initialValue = 0);
|
||||
|
||||
NetStatesUInt(NetStatesUInt&&) = default;
|
||||
NetStatesUInt& operator=(NetStatesUInt&&) = default;
|
||||
|
||||
uint64_t get() const;
|
||||
void set(uint64_t value);
|
||||
|
||||
private:
|
||||
NetStatesUInt(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value);
|
||||
};
|
||||
|
||||
// Properly encodes NPos no matter the platform width of size_t NetStates
|
||||
// size_t values are NOT clamped when setting.
|
||||
class NetStatesSize : public NetStatesField {
|
||||
public:
|
||||
NetStatesSize(size_t initialValue = 0);
|
||||
|
||||
NetStatesSize(NetStatesSize&&) = default;
|
||||
NetStatesSize& operator=(NetStatesSize&&) = default;
|
||||
|
||||
size_t get() const;
|
||||
void set(size_t value);
|
||||
};
|
||||
|
||||
// NetStates floats are clamped to their maximum and minimum legal values when
|
||||
// calling set if the type is one of the normalized float types NFloat8 or
|
||||
// NFloat16, or one of the fixed point types Fixed8 or Fixed16 (but not
|
||||
// VarFixed)
|
||||
class NetStatesFloat : public NetStatesField {
|
||||
public:
|
||||
static NetStatesFloat makeFloat(double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
static NetStatesFloat makeDouble(double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
// Float only from 0.0 to 1.0
|
||||
static NetStatesFloat makeNormalizedFloat8(double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
static NetStatesFloat makeNormalizedFloat16(double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
// Float represented as a rational number of a fixed base
|
||||
static NetStatesFloat makeFixedPoint8(double base, double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
static NetStatesFloat makeFixedPoint16(double base, double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
static NetStatesFloat makeFixedPoint(double base, double initialValue = 0.0, NetStatesInterpolator interpolator = NetStatesExactInterpolator);
|
||||
|
||||
NetStatesFloat(NetStatesFloat&&) = default;
|
||||
NetStatesFloat& operator=(NetStatesFloat&&) = default;
|
||||
|
||||
double get() const;
|
||||
void set(double value);
|
||||
|
||||
void setInterpolator(NetStatesInterpolator interpolator);
|
||||
|
||||
private:
|
||||
NetStatesFloat(NetStatesDetail::TypeInfo typeInfo, NetStatesDetail::Value value);
|
||||
};
|
||||
|
||||
class NetStatesBool : public NetStatesField {
|
||||
public:
|
||||
NetStatesBool(bool initialValue = false);
|
||||
|
||||
NetStatesBool(NetStatesBool&&) = default;
|
||||
NetStatesBool& operator=(NetStatesBool&&) = default;
|
||||
|
||||
bool get() const;
|
||||
void set(bool value);
|
||||
};
|
||||
|
||||
// Wraps a uint64_t to give a simple event stream. Every trigger is an
|
||||
// increment to a held uint64_t value, and slaves can see how many triggers
|
||||
// have occurred since the last check.
|
||||
class NetStatesEvent : public NetStatesField {
|
||||
public:
|
||||
NetStatesEvent();
|
||||
|
||||
NetStatesEvent(NetStatesEvent&&) = default;
|
||||
NetStatesEvent& operator=(NetStatesEvent&&) = default;
|
||||
|
||||
void trigger();
|
||||
|
||||
// Returns the number of times this event has been triggered since the last
|
||||
// pullOccurrences call.
|
||||
uint64_t pullOccurrences();
|
||||
|
||||
// Pulls whether this event occurred at all, ignoring the number
|
||||
bool pullOccurred();
|
||||
|
||||
// Ignore all the existing ocurrences
|
||||
void ignoreOccurrences();
|
||||
|
||||
private:
|
||||
using NetStatesField::pullUpdated;
|
||||
};
|
||||
|
||||
// Holds an arbitrary serializable value
|
||||
template<typename T>
|
||||
class NetStatesData : public NetStatesField {
|
||||
public:
|
||||
NetStatesData(T initialValue = T());
|
||||
NetStatesData(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer, T initialValue = T());
|
||||
|
||||
NetStatesData(NetStatesData&&) = default;
|
||||
NetStatesData& operator=(NetStatesData&&) = default;
|
||||
|
||||
T const& get() const;
|
||||
|
||||
// Updates the value if the value is different than the existing value,
|
||||
// requires T have operator==
|
||||
void set(T const& value);
|
||||
|
||||
// Always updates the value and marks it as updated.
|
||||
void push(T value);
|
||||
|
||||
// Update the value in place. The mutator will be called as bool
|
||||
// mutator(T&), return true to signal that the value was updated.
|
||||
template<typename Mutator>
|
||||
void update(Mutator&& mutator);
|
||||
|
||||
private:
|
||||
struct SerializerImpl : NetStatesDetail::GenericSerializer {
|
||||
SerializerImpl(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer);
|
||||
|
||||
function<void(DataStream&, T&)> reader;
|
||||
function<void(DataStream&, T const&)> writer;
|
||||
|
||||
shared_ptr<void> read(DataStream& ds) const override;
|
||||
void write(DataStream& ds, shared_ptr<void> const& data) const override;
|
||||
};
|
||||
};
|
||||
|
||||
// Convenience around NetStatesInt to hold an enum value (avoids casting)
|
||||
template<typename T>
|
||||
class NetStatesEnum : public NetStatesField {
|
||||
public:
|
||||
NetStatesEnum(T initialValue = T());
|
||||
|
||||
T get() const;
|
||||
void set(T value);
|
||||
};
|
||||
|
||||
// Convenience for very common NetStatesData types
|
||||
typedef NetStatesData<String> NetStatesString;
|
||||
typedef NetStatesData<ByteArray> NetStatesBytes;
|
||||
|
||||
// Send and receives state using delta coding based on a always increasing step
|
||||
// value. The step is never sent over the wire, rather it is used as a way to
|
||||
// track versions of states.
|
||||
class NetStepStates {
|
||||
public:
|
||||
NetStepStates();
|
||||
template<typename... Args>
|
||||
NetStepStates(Args const&... fields);
|
||||
|
||||
NetStepStates(NetStepStates const& states) = delete;
|
||||
NetStepStates& operator=(NetStepStates const& states) = delete;
|
||||
|
||||
// Add the given unconnected field and connect it to this NetStepStates
|
||||
// instance. Individual fields in a NetStates object are identified based on
|
||||
// the order in which they are added. Both sides of a NetStates object must
|
||||
// add the same number of fields and of the same type, and in the same order,
|
||||
// for communication to work.
|
||||
void addField(NetStatesField const& nsfield);
|
||||
|
||||
template<typename... Args>
|
||||
void addFields(Args const&... fields);
|
||||
|
||||
// Removes all fields and resets back to the initial state.
|
||||
void clearFields();
|
||||
|
||||
// Produces a digest of all the field types in this NetStates object
|
||||
uint32_t fieldDigest() const;
|
||||
|
||||
// When sending, it is important *when* precisely updateStep is called in
|
||||
// order not to lose data, the order *must* be:
|
||||
// - updateStep(<increased step value>)
|
||||
// - *do things that change data*
|
||||
// - *write deltas*
|
||||
// - repeat.
|
||||
// No data should be set in between writing a delta and incrementing step, or
|
||||
// data will be lost to slaves. (Assuming no delta overlapping is done to
|
||||
// prevent it).
|
||||
//
|
||||
// An exception will be thrown if newStep ever decreases in a call to
|
||||
// updateStep, reset must be called instead.
|
||||
//
|
||||
// Returns true in the event that updating the step changed the value of any
|
||||
// fields. This will only be true when interpolation is enabled.
|
||||
bool updateStep(uint64_t newStep);
|
||||
uint64_t currentStep() const;
|
||||
|
||||
// Start the NetStepStates object back from the initial step (will all the
|
||||
// existing fields). All current field values are kept unchanged and
|
||||
// interpolation data is cleared.
|
||||
void resetStep();
|
||||
|
||||
// Enables interpolation mode. If interpolation mode is enabled, then all
|
||||
// incoming states are marked with a floating target step value, and states
|
||||
// are delayed until the interpolation step reaches the given step value for
|
||||
// that state. This also enables optional interpolation smoothing on
|
||||
// floating point values that looks ahead into the future to even out
|
||||
// changes, and can also optionally extrapolate into the future if no data is
|
||||
// available.
|
||||
//
|
||||
// While interpolating, calling a set will discard any held interpolation
|
||||
// data even into the future.
|
||||
void enableInterpolation(uint64_t extrapolationSteps = 0);
|
||||
void disableInterpolation();
|
||||
bool interpolationEnabled() const;
|
||||
|
||||
// Returns true if there is changed data since this timestep, and writeDelta
|
||||
// called with this step would write data.
|
||||
bool hasDelta(uint64_t fromStep) const;
|
||||
|
||||
// Write all the state changes that have happened since (and including)
|
||||
// fromStep. The normal way to use this would be each call to writeDelta
|
||||
// should be passed the step at the time of the *last* call to writeDelta, +
|
||||
// 1. writeDelta with fromStep = 0 writes all states, and is a larger
|
||||
// version of writeFull(). If hasDelta(fromStep) returns false, this will
|
||||
// still write some data as an end marker, it is best to call hasDelta first
|
||||
// to see if any delta is needed.
|
||||
void writeDelta(DataStream& ds, uint64_t fromStep) const;
|
||||
|
||||
// predictedDeltaStep is only necessary if interpolation is enabled,
|
||||
// otherwise the delta is assumed to apply immediately. Will return true
|
||||
// when reading this delta has changed the current state of any fields.
|
||||
bool readDelta(DataStream& ds, double predictedDeltaStep = 0);
|
||||
|
||||
// Writes / reads all values regardless of changed state. Usually a slight
|
||||
// network improvement over writeDelta(0), as it does not need to prefix
|
||||
// every field with an index. Additionall, first writes the 4 byte field
|
||||
// digest to the full state, so readFull also acts as a type consistency
|
||||
// check.
|
||||
void writeFull(DataStream& ds) const;
|
||||
void readFull(DataStream& ds, double predictedDeltaStep = 0);
|
||||
|
||||
// Write and read deltas directly to a byte array. Uses a slightly different
|
||||
// binary format that relies on the size of the array packet, so cannot be
|
||||
// mixed with normal writes and reads.
|
||||
ByteArray writeDeltaPacket(uint64_t fromStep) const;
|
||||
bool readDeltaPacket(ByteArray packet, double predictedDeltaStep = 0);
|
||||
ByteArray writeFullPacket() const;
|
||||
void readFullPacket(ByteArray packet, double predictedDeltaStep = 0);
|
||||
|
||||
// Equivalent to reading a blank delta state, simply lets receiver know that
|
||||
// no delta is needed for up to this timestep, so no extrapolation is
|
||||
// required. Not required if interpolation is not enabled or is enabled with
|
||||
// zero extrapolation steps.
|
||||
void interpolationHeartbeat(double predictedDeltaStep = 0);
|
||||
|
||||
private:
|
||||
DynamicArray<NetStatesDetail::FieldPtr> m_fields;
|
||||
uint64_t m_currentStep;
|
||||
bool m_interpolationEnabled;
|
||||
uint64_t m_extrapolationSteps;
|
||||
};
|
||||
|
||||
// NetStepStates with no concept of a step, only sends values that have been
|
||||
// changed between writes. Only works for writing to a single receiver.
|
||||
class NetSyncStates : private NetStepStates {
|
||||
public:
|
||||
NetSyncStates();
|
||||
template<typename... Args>
|
||||
NetSyncStates(Args const&... fields);
|
||||
|
||||
bool hasDelta() const;
|
||||
void writeDelta(DataStream& ds);
|
||||
bool readDelta(DataStream& ds);
|
||||
|
||||
ByteArray writeDeltaPacket();
|
||||
bool readDeltaPacket(ByteArray packet);
|
||||
|
||||
void reset();
|
||||
|
||||
using NetStepStates::addField;
|
||||
using NetStepStates::clearFields;
|
||||
using NetStepStates::fieldDigest;
|
||||
};
|
||||
|
||||
namespace NetStatesDetail {
|
||||
// Values as they are sent across the wire, converted to their compressed or
|
||||
// truncated transmission format.
|
||||
typedef Any<int8_t, uint8_t, int16_t, uint16_t, int64_t, uint64_t, float, double, bool, shared_ptr<void>> TransmissionValue;
|
||||
|
||||
enum class TransmissionType : uint8_t {
|
||||
Int8,
|
||||
UInt8,
|
||||
Int16,
|
||||
UInt16,
|
||||
VarInt,
|
||||
VarUInt,
|
||||
Size,
|
||||
Float,
|
||||
Double,
|
||||
NFloat8,
|
||||
NFloat16,
|
||||
Fixed8,
|
||||
Fixed16,
|
||||
VarFixed,
|
||||
Bool,
|
||||
Event,
|
||||
Generic
|
||||
};
|
||||
|
||||
struct TypeInfo {
|
||||
TransmissionType type;
|
||||
|
||||
// If this is a fixed point float, this is its fixed point base
|
||||
Maybe<double> fixedPointBase;
|
||||
|
||||
// If the type is Data, this holds the reader / writer for it.
|
||||
shared_ptr<GenericSerializer> genericSerializer;
|
||||
|
||||
// If this is any sort of floating type, then if value interpolation is
|
||||
// enabled this will hold the interpolator
|
||||
NetStatesInterpolator interpolator;
|
||||
|
||||
Value valueFromTransmission(TransmissionValue const& transmission) const;
|
||||
TransmissionValue transmissionFromValue(Value const& value) const;
|
||||
};
|
||||
|
||||
class Field {
|
||||
public:
|
||||
Field(TypeInfo typeInfo, Value value);
|
||||
|
||||
TypeInfo const& typeInfo() const;
|
||||
|
||||
int64_t getInt() const;
|
||||
void setInt(int64_t value);
|
||||
|
||||
uint64_t getUInt() const;
|
||||
void setUInt(uint64_t value);
|
||||
|
||||
size_t getSize() const;
|
||||
void setSize(size_t value);
|
||||
|
||||
double getFloat() const;
|
||||
void setFloat(double value);
|
||||
void setFloatInterpolator(NetStatesInterpolator interpolator);
|
||||
|
||||
bool getBool() const;
|
||||
void setBool(bool value);
|
||||
|
||||
void triggerEvent();
|
||||
uint64_t pullOccurrences();
|
||||
void ignoreOccurrences();
|
||||
|
||||
template<typename T>
|
||||
T const& getGeneric() const;
|
||||
template<typename T>
|
||||
void setGeneric(T const& data);
|
||||
template<typename T>
|
||||
void pushGeneric(T data);
|
||||
template<typename T, typename Mutator>
|
||||
void updateGeneric(Mutator&& mutator);
|
||||
|
||||
bool pullUpdated();
|
||||
|
||||
// Returns true if field value may have changed
|
||||
bool updateStep(uint64_t step);
|
||||
void resetStep();
|
||||
|
||||
bool hasDelta(uint64_t fromStep) const;
|
||||
bool readField(DataStream& ds, double interpolationStep);
|
||||
void writeField(DataStream& ds) const;
|
||||
|
||||
void enableInterpolation(uint64_t extrapolationSteps);
|
||||
void disableInterpolation();
|
||||
void interpolationHeartbeat(double predictedDeltaStep);
|
||||
|
||||
private:
|
||||
bool updateValueFromTransmission();
|
||||
|
||||
// Marks the value as updated by setting the unpulledUpdate flag and the
|
||||
// transmissionDirty flag
|
||||
void markValueUpdated();
|
||||
|
||||
// To support fast setting of values, transmission values are updated
|
||||
// lazily. Once the transmissionDirty flag is set, call
|
||||
// freshenTransmission() to (IF the transmission value has changed) update
|
||||
// the latest transmission and latest transmission step from the current
|
||||
// value.
|
||||
void freshenTransmission();
|
||||
|
||||
TypeInfo m_typeInfo;
|
||||
uint64_t m_currentStep;
|
||||
Value m_value;
|
||||
|
||||
bool m_unpulledUpdate;
|
||||
uint64_t m_pulledOccurrences;
|
||||
|
||||
bool m_transmissionDirty;
|
||||
TransmissionValue m_latestTransmission;
|
||||
uint64_t m_latestTransmissionStep;
|
||||
Maybe<StepStream<TransmissionValue>> m_interpolatedTransmissions;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
NetStatesData<T>::NetStatesData(T initialValue)
|
||||
: NetStatesData(
|
||||
[](DataStream& ds, T& t) {
|
||||
ds >> t;
|
||||
},
|
||||
[](DataStream& ds, T const& t) {
|
||||
ds << t;
|
||||
}, move(initialValue)) {}
|
||||
|
||||
template<typename T>
|
||||
NetStatesData<T>::NetStatesData(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer, T initialValue)
|
||||
: NetStatesField(NetStatesDetail::TypeInfo{
|
||||
NetStatesDetail::TransmissionType::Generic, {},
|
||||
make_shared<SerializerImpl>(move(reader), move(writer)), {}
|
||||
}, shared_ptr<void>(make_shared<T>(move(initialValue)))) {}
|
||||
|
||||
template<typename T>
|
||||
T const& NetStatesData<T>::get() const {
|
||||
return m_field->template getGeneric<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesData<T>::set(T const& value) {
|
||||
m_field->template setGeneric<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesData<T>::push(T value) {
|
||||
m_field->template pushGeneric<T>(move(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename Mutator>
|
||||
void NetStatesData<T>::update(Mutator&& mutator) {
|
||||
m_field->template updateGeneric<T>(forward<Mutator>(mutator));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
NetStatesData<T>::SerializerImpl::SerializerImpl(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer)
|
||||
: reader(move(reader)), writer(move(writer)) {}
|
||||
|
||||
template<typename T>
|
||||
shared_ptr<void> NetStatesData<T>::SerializerImpl::read(DataStream& ds) const {
|
||||
auto data = make_shared<T>();
|
||||
reader(ds, *data);
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesData<T>::SerializerImpl::write(DataStream& ds, shared_ptr<void> const& data) const {
|
||||
writer(ds, *(T*)data.get());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
NetStatesEnum<T>::NetStatesEnum(T initialValue)
|
||||
: NetStatesField(
|
||||
NetStatesDetail::TypeInfo{NetStatesDetail::TransmissionType::VarInt, {}, {}, {}},
|
||||
(int64_t)initialValue) {}
|
||||
|
||||
template<typename T>
|
||||
T NetStatesEnum<T>::get() const {
|
||||
return (T)m_field->getInt();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesEnum<T>::set(T value) {
|
||||
return m_field->setInt((int64_t)value);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
NetStepStates::NetStepStates(Args const&... fields)
|
||||
: NetStepStates() {
|
||||
addFields(fields...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void NetStepStates::addFields(Args const&... fields) {
|
||||
callFunctionVariadic([this](NetStatesField const& field) {
|
||||
addField(field);
|
||||
}, fields...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
NetSyncStates::NetSyncStates(Args const&... fields)
|
||||
: NetSyncStates() {
|
||||
addFields(fields...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T const& NetStatesDetail::Field::getGeneric() const {
|
||||
starAssert(m_value.get<shared_ptr<void>>().get() != nullptr);
|
||||
return *(T const*)m_value.get<shared_ptr<void>>().get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesDetail::Field::setGeneric(T const& data) {
|
||||
auto& p = m_value.get<shared_ptr<void>>();
|
||||
if (!(*(T*)p.get() == data)) {
|
||||
*(T*)p.get() = data;
|
||||
markValueUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NetStatesDetail::Field::pushGeneric(T data) {
|
||||
if (m_value) {
|
||||
auto& p = m_value.get<shared_ptr<void>>();
|
||||
*(T*)p.get() = move(data);
|
||||
} else {
|
||||
m_value = shared_ptr<void>(make_shared<T>(move(data)));
|
||||
}
|
||||
markValueUpdated();
|
||||
}
|
||||
|
||||
template<typename T, typename Mutator>
|
||||
void NetStatesDetail::Field::updateGeneric(Mutator&& mutator) {
|
||||
auto const& p = m_value.get<shared_ptr<void>>();
|
||||
if (mutator(*(T*)p.get()))
|
||||
markValueUpdated();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
645
attic/old_source/StarNetTransmitters.cpp
Normal file
645
attic/old_source/StarNetTransmitters.cpp
Normal file
|
@ -0,0 +1,645 @@
|
|||
#include "StarNetTransmitters.hpp"
|
||||
#include "StarIterator.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
NetVersionSender::NetVersionSender() {
|
||||
m_currentVersion = 0;
|
||||
}
|
||||
|
||||
NetVersionSender::NetVersionSender(NetSchema const& schema)
|
||||
: NetVersionSender() {
|
||||
initialize(schema);
|
||||
}
|
||||
|
||||
void NetVersionSender::initialize(NetSchema const& schema) {
|
||||
if (!schema.isFinalized())
|
||||
throw NetException("schema unfinalized in NetVersionSender::initialize");
|
||||
|
||||
m_currentVersion = 0;
|
||||
|
||||
m_valuesByName.clear();
|
||||
m_values.clear();
|
||||
m_schema = schema;
|
||||
|
||||
for (auto id : m_schema.ids()) {
|
||||
ValuePtr value = std::make_shared<Value>();
|
||||
value->lastUpdated = m_currentVersion;
|
||||
|
||||
// Initialize with default values for each NetType, so we can error if a
|
||||
// mismatched set was called.
|
||||
auto const& fieldInfo = m_schema.fieldInfo(id);
|
||||
if (fieldInfo.fixedPointBase != 0.0) {
|
||||
value->data.set<double>(0.0);
|
||||
} else if (fieldInfo.type == NetType::Event) {
|
||||
value->data.set<uint64_t>(0);
|
||||
} else if (netTypeIsSignedInt(fieldInfo.type)) {
|
||||
value->data.set<int64_t>(0);
|
||||
} else if (netTypeIsUnsignedInt(fieldInfo.type)) {
|
||||
value->data.set<uint64_t>(0);
|
||||
} else if (fieldInfo.type == NetType::Size) {
|
||||
value->data.set<size_t>(NPos);
|
||||
} else if (netTypeIsFloat(fieldInfo.type)) {
|
||||
value->data.set<double>(0.0);
|
||||
} else if (fieldInfo.type == NetType::Bool) {
|
||||
value->data.set<bool>(false);
|
||||
} else if (fieldInfo.type == NetType::String) {
|
||||
value->data.set<String>(String());
|
||||
} else if (fieldInfo.type == NetType::Data) {
|
||||
value->data.set<ByteArray>(ByteArray());
|
||||
} else if (fieldInfo.type == NetType::Variant) {
|
||||
value->data.set<Variant>(Variant());
|
||||
}
|
||||
|
||||
m_values.insert(id, value);
|
||||
m_valuesByName.insert(m_schema.name(id), value);
|
||||
}
|
||||
}
|
||||
|
||||
NetSchema const& NetVersionSender::schema() const {
|
||||
return m_schema;
|
||||
}
|
||||
|
||||
void NetVersionSender::resetVersion() {
|
||||
m_currentVersion = 0;
|
||||
for (auto& pair : m_values)
|
||||
pair.second->lastUpdated = m_currentVersion;
|
||||
}
|
||||
|
||||
void NetVersionSender::incrementVersion(uint64_t version) {
|
||||
if (version < m_currentVersion)
|
||||
throw NetException("Version reversed in NetVersionSender::incrementVersion");
|
||||
|
||||
m_currentVersion = version;
|
||||
}
|
||||
|
||||
uint64_t NetVersionSender::currentVersion() const {
|
||||
return m_currentVersion;
|
||||
}
|
||||
|
||||
void NetVersionSender::triggerEvent(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<uint64_t>())
|
||||
throw NetException("NetSender type mismatch in triggerEvent");
|
||||
|
||||
value->data.set<uint64_t>(value->data.get<uint64_t>() + 1);
|
||||
value->lastUpdated = m_currentVersion;
|
||||
}
|
||||
|
||||
void NetVersionSender::setInt(StringRef name, int64_t v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<int64_t>())
|
||||
throw NetException("NetSender type mismatch in setInt");
|
||||
|
||||
if (value->data.cget<int64_t>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<int64_t>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setUInt(StringRef name, uint64_t v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<uint64_t>())
|
||||
throw NetException("NetSender type mismatch in setUInt");
|
||||
|
||||
if (value->data.cget<uint64_t>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<uint64_t>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setSize(StringRef name, size_t v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<size_t>())
|
||||
throw NetException("NetSender type mismatch in setSize");
|
||||
|
||||
if (value->data.cget<size_t>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<size_t>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setFloat(StringRef name, double v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<double>())
|
||||
throw NetException("NetSender type mismatch in setFloat");
|
||||
|
||||
// TODO: For right now does a straight != comparison, should we include a
|
||||
// tolerance setting?
|
||||
if (value->data.cget<double>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<double>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setBool(StringRef name, bool v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<bool>())
|
||||
throw NetException("NetSender type mismatch in setBool");
|
||||
|
||||
if (value->data.cget<bool>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<bool>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setString(StringRef name, String const& v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<String>())
|
||||
throw NetException("NetSender type mismatch in setString");
|
||||
|
||||
if (value->data.cget<String>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<String>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setData(StringRef name, ByteArray const& v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<ByteArray>())
|
||||
throw NetException("NetSender type mismatch in setData");
|
||||
|
||||
if (value->data.cget<ByteArray>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<ByteArray>(v);
|
||||
}
|
||||
}
|
||||
|
||||
void NetVersionSender::setVariant(StringRef name, Variant const& v) {
|
||||
auto const& value = getValue(name);
|
||||
if (!value->data.is<Variant>())
|
||||
throw NetException("NetSender type mismatch in setVariant");
|
||||
|
||||
if (value->data.cget<Variant>() != v) {
|
||||
value->lastUpdated = m_currentVersion;
|
||||
value->data.set<Variant>(v);
|
||||
}
|
||||
}
|
||||
|
||||
size_t NetVersionSender::writeFull(DataStream& ds) const {
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
for (auto id : m_schema.ids()) {
|
||||
auto const& value = m_values.value(id);
|
||||
auto const& field = m_schema.fieldInfo(id);
|
||||
|
||||
bytesWritten += writeValue(ds, field, value);
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
ByteArray NetVersionSender::writeFull() const {
|
||||
DataStreamBuffer buffer;
|
||||
writeFull(buffer);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
size_t NetVersionSender::writeDelta(DataStream& ds, uint64_t sinceVersion) const {
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
for (auto const& pair : m_values) {
|
||||
auto const& value = pair.second;
|
||||
if (value->lastUpdated < sinceVersion)
|
||||
continue;
|
||||
|
||||
auto const& field = m_schema.fieldInfo(pair.first);
|
||||
|
||||
bytesWritten += ds.writeVlqU(pair.first);
|
||||
bytesWritten += writeValue(ds, field, value);
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
ByteArray NetVersionSender::writeDelta(uint64_t sinceVersion) const {
|
||||
DataStreamBuffer buffer;
|
||||
writeDelta(buffer, sinceVersion);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
NetVersionSender::ValuePtr const& NetVersionSender::getValue(StringRef name) const {
|
||||
auto i = m_valuesByName.find(name);
|
||||
if (i == m_valuesByName.end())
|
||||
throw NetException(strf("No such NetSender state named '%s'", name));
|
||||
return i->second;
|
||||
}
|
||||
|
||||
size_t NetVersionSender::writeValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value) const {
|
||||
StreamOffset pos = ds.pos();
|
||||
|
||||
DataPoint data;
|
||||
if (fieldInfo.fixedPointBase != 0.0) {
|
||||
if (netTypeIsSignedInt(fieldInfo.type))
|
||||
data.set<int64_t>(value->data.get<double>() / fieldInfo.fixedPointBase);
|
||||
else
|
||||
data.set<uint64_t>(value->data.get<double>() / fieldInfo.fixedPointBase);
|
||||
} else {
|
||||
data = value->data;
|
||||
}
|
||||
|
||||
if (fieldInfo.type == NetType::Event) {
|
||||
ds.writeVlqU(data.get<uint64_t>());
|
||||
} else if (fieldInfo.type == NetType::Int8) {
|
||||
ds.write<int8_t>(data.get<int64_t>());
|
||||
} else if (fieldInfo.type == NetType::UInt8) {
|
||||
ds.write<uint8_t>(data.get<uint64_t>());
|
||||
} else if (fieldInfo.type == NetType::Int16) {
|
||||
ds.write<int16_t>(data.get<int64_t>());
|
||||
} else if (fieldInfo.type == NetType::UInt16) {
|
||||
ds.write<uint16_t>(data.get<uint64_t>());
|
||||
} else if (fieldInfo.type == NetType::VarInt) {
|
||||
ds.writeVlqI(data.get<int64_t>());
|
||||
} else if (fieldInfo.type == NetType::VarUInt) {
|
||||
ds.writeVlqU(data.get<uint64_t>());
|
||||
} else if (fieldInfo.type == NetType::Size) {
|
||||
size_t v = data.get<size_t>();
|
||||
ds.writeVlqU(v == NPos ? 0 : v + 1);
|
||||
} else if (fieldInfo.type == NetType::Float) {
|
||||
ds.write<float>(data.get<double>());
|
||||
} else if (fieldInfo.type == NetType::Double) {
|
||||
ds.write<double>(data.get<double>());
|
||||
} else if (fieldInfo.type == NetType::NFloat8) {
|
||||
ds.write<uint8_t>(std::round(data.get<double>() * 255.0f));
|
||||
} else if (fieldInfo.type == NetType::NFloat16) {
|
||||
ds.write<uint16_t>(std::round(data.get<double>() * 65535.0f));
|
||||
} else if (fieldInfo.type == NetType::Bool) {
|
||||
ds.write<bool>(data.get<bool>());
|
||||
} else if (fieldInfo.type == NetType::String) {
|
||||
ds.write(data.get<String>());
|
||||
} else if (fieldInfo.type == NetType::Data) {
|
||||
ds.write(data.get<ByteArray>());
|
||||
} else if (fieldInfo.type == NetType::Variant) {
|
||||
ds.write(data.get<Variant>());
|
||||
}
|
||||
|
||||
return ds.pos() - pos;
|
||||
}
|
||||
|
||||
NetSyncSender::NetSyncSender() {}
|
||||
|
||||
NetSyncSender::NetSyncSender(NetSchema const& schema) {
|
||||
initialize(schema);
|
||||
}
|
||||
|
||||
size_t NetSyncSender::writeDelta(DataStream& ds) {
|
||||
size_t size = NetVersionSender::writeDelta(ds, currentVersion());
|
||||
// Increment the version on write so we only write newly written states.
|
||||
incrementVersion(currentVersion() + 1);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t NetSyncSender::writeFull(DataStream& ds) {
|
||||
size_t size = NetVersionSender::writeFull(ds);
|
||||
// Increment the version on write so we only write newly written states.
|
||||
incrementVersion(currentVersion() + 1);
|
||||
return size;
|
||||
}
|
||||
|
||||
ByteArray NetSyncSender::writeFull() {
|
||||
DataStreamBuffer buffer;
|
||||
writeFull(buffer);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
ByteArray NetSyncSender::writeDelta() {
|
||||
DataStreamBuffer buffer;
|
||||
writeDelta(buffer);
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
NetReceiver::NetReceiver()
|
||||
: m_interpolationEnabled(false), m_interpolationStep(0.0) {}
|
||||
|
||||
NetReceiver::NetReceiver(NetSchema const& schema)
|
||||
: NetReceiver() {
|
||||
initialize(schema);
|
||||
}
|
||||
|
||||
void NetReceiver::initialize(NetSchema const& schema) {
|
||||
if (!schema.isFinalized())
|
||||
throw NetException("schema unfinalized in NetReceiver::initialize");
|
||||
|
||||
m_valuesByName.clear();
|
||||
m_values.clear();
|
||||
m_schema = schema;
|
||||
for (auto id : m_schema.ids()) {
|
||||
ValuePtr value = std::make_shared<Value>();
|
||||
value->needsForwarding = false;
|
||||
value->exactUpdated = false;
|
||||
|
||||
// Initialize with default values because NetSender assumes these as the
|
||||
// starting values without sending any data
|
||||
auto const& fieldInfo = m_schema.fieldInfo(id);
|
||||
if (fieldInfo.fixedPointBase != 0.0) {
|
||||
value->latestData = 0.0;
|
||||
} else if (fieldInfo.type == NetType::Event) {
|
||||
value->latestData = (uint64_t)0;
|
||||
} else if (netTypeIsSignedInt(fieldInfo.type)) {
|
||||
value->latestData = (int64_t)0;
|
||||
} else if (netTypeIsUnsignedInt(fieldInfo.type)) {
|
||||
value->latestData = (uint64_t)0;
|
||||
} else if (fieldInfo.type == NetType::Size) {
|
||||
value->latestData = (size_t)NPos;
|
||||
} else if (netTypeIsFloat(fieldInfo.type)) {
|
||||
value->latestData = 0.0;
|
||||
} else if (fieldInfo.type == NetType::Bool) {
|
||||
value->latestData = false;
|
||||
} else if (fieldInfo.type == NetType::String) {
|
||||
value->latestData = String();
|
||||
} else if (fieldInfo.type == NetType::Data) {
|
||||
value->latestData = ByteArray();
|
||||
} else if (fieldInfo.type == NetType::Variant) {
|
||||
value->latestData = Variant();
|
||||
}
|
||||
|
||||
value->exactData = value->latestData;
|
||||
value->exactUpdated = false;
|
||||
if (m_interpolationEnabled) {
|
||||
value->stream.reset();
|
||||
value->stream.appendDataPoint(m_interpolationStep, value->latestData);
|
||||
}
|
||||
|
||||
m_values.insert(id, value);
|
||||
m_valuesByName.insert(m_schema.name(id), value);
|
||||
}
|
||||
}
|
||||
|
||||
NetSchema const& NetReceiver::schema() const {
|
||||
return m_schema;
|
||||
}
|
||||
|
||||
void NetReceiver::enableInterpolation(double startStep, double extrapolation) {
|
||||
m_interpolationEnabled = true;
|
||||
m_interpolationStep = startStep;
|
||||
for (auto const& pair : m_values) {
|
||||
pair.second->stream.reset();
|
||||
pair.second->stream.setExtrapolation(extrapolation);
|
||||
pair.second->stream.appendDataPoint(startStep, pair.second->latestData);
|
||||
pair.second->stream.setStep(startStep);
|
||||
if (pair.second->exactData != pair.second->latestData) {
|
||||
pair.second->exactData = pair.second->latestData;
|
||||
pair.second->exactUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetReceiver::disableInterpolation() {
|
||||
m_interpolationEnabled = false;
|
||||
m_interpolationStep = 0.0;
|
||||
for (auto const& pair : m_values) {
|
||||
pair.second->stream.reset();
|
||||
if (pair.second->exactData != pair.second->latestData) {
|
||||
pair.second->exactData = pair.second->latestData;
|
||||
pair.second->exactUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetReceiver::setInterpolationDiffFunction(StringRef name, DifferenceFunction diffFunction) {
|
||||
getValue(name)->diffFunction = diffFunction;
|
||||
}
|
||||
|
||||
void NetReceiver::setInterpolationStep(double step) {
|
||||
if (!m_interpolationEnabled)
|
||||
return;
|
||||
|
||||
m_interpolationStep = step;
|
||||
for (auto const& pair : m_values) {
|
||||
pair.second->stream.setStep(step);
|
||||
auto data = pair.second->stream.exact();
|
||||
if (data != pair.second->exactData) {
|
||||
pair.second->exactData = data;
|
||||
pair.second->exactUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetReceiver::readFull(DataStream& ds, size_t dataSize) {
|
||||
int bytesLeft = dataSize;
|
||||
|
||||
for (unsigned id : m_schema.ids()) {
|
||||
auto const& field = m_schema.fieldInfo(id);
|
||||
bytesLeft -= readValue(ds, field, m_values[id], m_interpolationStep);
|
||||
|
||||
if (bytesLeft < 0)
|
||||
throw NetException("Read past end of dataSize in NetReceiver::readFull!");
|
||||
}
|
||||
|
||||
if (bytesLeft != 0)
|
||||
throw NetException("Extra data at end of NetReceiver::readFull!");
|
||||
}
|
||||
|
||||
void NetReceiver::readFull(ByteArray const& fullState) {
|
||||
DataStreamBuffer buffer(fullState);
|
||||
readFull(buffer, buffer.size());
|
||||
}
|
||||
|
||||
void NetReceiver::readDelta(DataStream& ds, size_t dataSize, double interpolationStep) {
|
||||
int bytesLeft = dataSize;
|
||||
|
||||
while (bytesLeft > 0) {
|
||||
uint64_t id;
|
||||
bytesLeft -= ds.readVlqU(id);
|
||||
bytesLeft -= readValue(ds, m_schema.fieldInfo(id), m_values[id], interpolationStep);
|
||||
|
||||
// TODO: This is pretty terrible, we shouldn't read extra *then* fail
|
||||
if (bytesLeft < 0)
|
||||
throw NetException("Read past end of dataSize in NetReceiver::readDelta!");
|
||||
}
|
||||
}
|
||||
|
||||
void NetReceiver::readDelta(ByteArray const& deltaState, double interpolationStep) {
|
||||
DataStreamBuffer buffer(deltaState);
|
||||
readDelta(buffer, buffer.size(), interpolationStep);
|
||||
}
|
||||
|
||||
void NetReceiver::interpolationHeartbeat(double interpolationStep) {
|
||||
if (m_interpolationEnabled) {
|
||||
for (auto const& pair : m_values)
|
||||
pair.second->stream.heartbeat(interpolationStep);
|
||||
}
|
||||
}
|
||||
|
||||
bool NetReceiver::updated(StringRef name) const {
|
||||
return getValue(name)->exactUpdated;
|
||||
}
|
||||
|
||||
bool NetReceiver::eventOccurred(StringRef eventName) {
|
||||
auto const& value = getValue(eventName);
|
||||
if (value->exactUpdated) {
|
||||
value->exactUpdated = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t NetReceiver::getInt(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<int64_t>();
|
||||
}
|
||||
|
||||
uint64_t NetReceiver::getUInt(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<uint64_t>();
|
||||
}
|
||||
|
||||
size_t NetReceiver::getSize(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<size_t>();
|
||||
}
|
||||
|
||||
double NetReceiver::getFloat(StringRef name, bool interpolateIfAvailable) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
|
||||
if (m_interpolationEnabled && interpolateIfAvailable) {
|
||||
auto weighting = value->stream.weighting();
|
||||
auto pointMin = weighting.pointMin.cget<double>();
|
||||
auto pointMax = weighting.pointMax.cget<double>();
|
||||
|
||||
if (value->diffFunction)
|
||||
return pointMin + value->diffFunction(pointMax, pointMin) * weighting.offset;
|
||||
else
|
||||
return pointMin + (pointMax - pointMin) * weighting.offset;
|
||||
} else {
|
||||
return value->exactData.cget<double>();
|
||||
}
|
||||
}
|
||||
|
||||
bool NetReceiver::getBool(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<bool>();
|
||||
}
|
||||
|
||||
String NetReceiver::getString(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<String>();
|
||||
}
|
||||
|
||||
ByteArray NetReceiver::getData(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<ByteArray>();
|
||||
}
|
||||
|
||||
Variant NetReceiver::getVariant(StringRef name) {
|
||||
auto const& value = getValue(name);
|
||||
value->exactUpdated = false;
|
||||
return value->exactData.cget<Variant>();
|
||||
}
|
||||
|
||||
void NetReceiver::forward(NetVersionSender& sender, bool forceAll) {
|
||||
for (auto id : m_schema.ids()) {
|
||||
auto const& value = m_values.get(id);
|
||||
auto const& fieldInfo = m_schema.fieldInfo(id);
|
||||
if (!forceAll && !value->needsForwarding)
|
||||
continue;
|
||||
|
||||
if (fieldInfo.fixedPointBase != 0.0) {
|
||||
sender.setFloat(fieldInfo.name, value->latestData.cget<double>());
|
||||
} else if (fieldInfo.type == NetType::Event) {
|
||||
if (value->needsForwarding)
|
||||
sender.triggerEvent(fieldInfo.name);
|
||||
} else if (netTypeIsSignedInt(fieldInfo.type)) {
|
||||
sender.setInt(fieldInfo.name, value->latestData.cget<int64_t>());
|
||||
} else if (netTypeIsUnsignedInt(fieldInfo.type)) {
|
||||
sender.setUInt(fieldInfo.name, value->latestData.cget<uint64_t>());
|
||||
} else if (fieldInfo.type == NetType::Size) {
|
||||
sender.setSize(fieldInfo.name, value->latestData.cget<size_t>());
|
||||
} else if (netTypeIsFloat(fieldInfo.type)) {
|
||||
sender.setFloat(fieldInfo.name, value->latestData.cget<double>());
|
||||
} else if (fieldInfo.type == NetType::Bool) {
|
||||
sender.setBool(fieldInfo.name, value->latestData.cget<bool>());
|
||||
} else if (fieldInfo.type == NetType::String) {
|
||||
sender.setString(fieldInfo.name, value->latestData.cget<String>());
|
||||
} else if (fieldInfo.type == NetType::Data) {
|
||||
sender.setData(fieldInfo.name, value->latestData.cget<ByteArray>());
|
||||
} else if (fieldInfo.type == NetType::Variant) {
|
||||
sender.setVariant(fieldInfo.name, value->latestData.cget<Variant>());
|
||||
}
|
||||
|
||||
value->needsForwarding = false;
|
||||
}
|
||||
}
|
||||
|
||||
NetReceiver::ValuePtr const& NetReceiver::getValue(StringRef name) const {
|
||||
auto i = m_valuesByName.find(name);
|
||||
if (i == m_valuesByName.end())
|
||||
throw NetException(strf("No such NetReceiver state named '%s'", name));
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
size_t NetReceiver::readValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value, double step) {
|
||||
StreamOffset pos = ds.pos();
|
||||
|
||||
DataPoint dataPoint;
|
||||
if (fieldInfo.type == NetType::Event) {
|
||||
dataPoint.set<uint64_t>(ds.readVlqU());
|
||||
} else if (fieldInfo.type == NetType::Int8) {
|
||||
dataPoint.set<int64_t>(ds.read<int8_t>());
|
||||
} else if (fieldInfo.type == NetType::UInt8) {
|
||||
dataPoint.set<uint64_t>(ds.read<uint8_t>());
|
||||
} else if (fieldInfo.type == NetType::Int16) {
|
||||
dataPoint.set<int64_t>(ds.read<int16_t>());
|
||||
} else if (fieldInfo.type == NetType::UInt16) {
|
||||
dataPoint.set<uint64_t>(ds.read<uint16_t>());
|
||||
} else if (fieldInfo.type == NetType::VarInt) {
|
||||
dataPoint.set<int64_t>(ds.readVlqI());
|
||||
} else if (fieldInfo.type == NetType::VarUInt) {
|
||||
dataPoint.set<uint64_t>(ds.readVlqU());
|
||||
} else if (fieldInfo.type == NetType::Size) {
|
||||
auto size = ds.readVlqU();
|
||||
dataPoint.set<size_t>(size == 0 ? NPos : size - 1);
|
||||
} else if (fieldInfo.type == NetType::Float) {
|
||||
dataPoint.set<double>(ds.read<float>());
|
||||
} else if (fieldInfo.type == NetType::NFloat8) {
|
||||
dataPoint.set<double>(ds.read<uint8_t>() / 255.0f);
|
||||
} else if (fieldInfo.type == NetType::NFloat16) {
|
||||
dataPoint.set<double>(ds.read<uint16_t>() / 65535.0f);
|
||||
} else if (fieldInfo.type == NetType::Double) {
|
||||
dataPoint.set<double>(ds.read<double>());
|
||||
} else if (fieldInfo.type == NetType::Bool) {
|
||||
dataPoint.set<bool>(ds.read<bool>());
|
||||
} else if (fieldInfo.type == NetType::String) {
|
||||
dataPoint.set<String>(ds.read<String>());
|
||||
} else if (fieldInfo.type == NetType::Data) {
|
||||
dataPoint.set<ByteArray>(ds.read<ByteArray>());
|
||||
} else if (fieldInfo.type == NetType::Variant) {
|
||||
dataPoint.set<Variant>(ds.read<Variant>());
|
||||
}
|
||||
|
||||
if (fieldInfo.fixedPointBase != 0.0) {
|
||||
if (netTypeIsSignedInt(fieldInfo.type))
|
||||
dataPoint.set<double>(dataPoint.cget<int64_t>() * fieldInfo.fixedPointBase);
|
||||
else
|
||||
dataPoint.set<double>(dataPoint.cget<uint64_t>() * fieldInfo.fixedPointBase);
|
||||
}
|
||||
|
||||
value->latestData = dataPoint;
|
||||
value->needsForwarding = true;
|
||||
|
||||
if (m_interpolationEnabled) {
|
||||
value->stream.appendDataPoint(step, dataPoint);
|
||||
auto data = value->stream.exact();
|
||||
if (value->exactData != data) {
|
||||
value->exactData = data;
|
||||
value->exactUpdated = true;
|
||||
}
|
||||
} else {
|
||||
if (value->exactData != value->latestData) {
|
||||
value->exactData = value->latestData;
|
||||
value->exactUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ds.pos() - pos;
|
||||
}
|
||||
|
||||
}
|
231
attic/old_source/StarNetTransmitters.hpp
Normal file
231
attic/old_source/StarNetTransmitters.hpp
Normal file
|
@ -0,0 +1,231 @@
|
|||
#ifndef STAR_NET_TRANSMITTERS_HPP
|
||||
#define STAR_NET_TRANSMITTERS_HPP
|
||||
|
||||
#include "StarNetSchema.hpp"
|
||||
#include "StarDataStream.hpp"
|
||||
#include "StarStepInterpolation.hpp"
|
||||
#include "StarVariant.hpp"
|
||||
#include "StarAny.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
// Send states using delta coding based on a always increasing version. The
|
||||
// version is never sent over the wire, rather it is used as a way to track
|
||||
// versions of states. Useful to send updates to several receivers. It must
|
||||
// be used in the following way to avoid lost data:
|
||||
//
|
||||
// 1. Set any changed data.
|
||||
// 2. Do any number of writeDelta calls
|
||||
// 3. Set new version
|
||||
// 4. Repeat...
|
||||
//
|
||||
// writeFull can be called at any time, but it is generally used on the 0th
|
||||
// version, so that writeDelta(0) is never called. The order is then:
|
||||
//
|
||||
// 1. Set any initial data.
|
||||
// 2. Do writeFull call.
|
||||
// 3. Set new version > 0
|
||||
// 4. Enter the loop above.
|
||||
class NetVersionSender {
|
||||
public:
|
||||
NetVersionSender();
|
||||
NetVersionSender(NetSchema const& schema);
|
||||
|
||||
NetVersionSender(NetVersionSender const& sender) = delete;
|
||||
NetVersionSender& operator=(NetVersionSender const& sender) = delete;
|
||||
|
||||
void initialize(NetSchema const& schema);
|
||||
NetSchema const& schema() const;
|
||||
|
||||
// Resets version, clears events, keeps all current values.
|
||||
void resetVersion();
|
||||
// After initialization / reset, version must always go forward.
|
||||
void incrementVersion(uint64_t version);
|
||||
uint64_t currentVersion() const;
|
||||
|
||||
// Marks this event as having occurred
|
||||
void triggerEvent(StringRef eventName);
|
||||
|
||||
// The correct set function must be called for each data type. Signed
|
||||
// integral values must be set with setInt, unsigned integral values with
|
||||
// setUInt. Any floating point type *or* an integral type if marked as a
|
||||
// "fixed point value" must be set with setFloat. Bools must be set with
|
||||
// setBool, and data values must be set with setData. Anything else results
|
||||
// in an exception.
|
||||
|
||||
void setInt(StringRef name, int64_t i);
|
||||
void setUInt(StringRef name, uint64_t u);
|
||||
void setSize(StringRef name, size_t s);
|
||||
void setFloat(StringRef name, double d);
|
||||
void setBool(StringRef name, bool b);
|
||||
void setString(StringRef name, String const& str);
|
||||
void setData(StringRef name, ByteArray const& data);
|
||||
void setVariant(StringRef name, Variant const& data);
|
||||
|
||||
// Write full set of states. Returns bytes written.
|
||||
size_t writeFull(DataStream& ds) const;
|
||||
ByteArray writeFull() const;
|
||||
|
||||
// Write all the state changes that have happened since (and including)
|
||||
// sinceVersion. Returns bytes written. The normal way to use this would be
|
||||
// each call to writeDelta should be passed the version at the time of the
|
||||
// *last* call to writeDelta, + 1. writeDelta with sinceVersion = 0 writes
|
||||
// all states, and is a larger version of writeFull().
|
||||
size_t writeDelta(DataStream& ds, uint64_t sinceVersion) const;
|
||||
ByteArray writeDelta(uint64_t sinceVersion) const;
|
||||
|
||||
private:
|
||||
typedef AnyOf<int64_t, uint64_t, size_t, double, bool, ByteArray, String, Variant> DataPoint;
|
||||
|
||||
struct Value {
|
||||
uint64_t lastUpdated;
|
||||
DataPoint data;
|
||||
};
|
||||
typedef std::shared_ptr<Value> ValuePtr;
|
||||
|
||||
ValuePtr const& getValue(StringRef name) const;
|
||||
size_t writeValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value) const;
|
||||
|
||||
NetSchema m_schema;
|
||||
|
||||
uint64_t m_currentVersion;
|
||||
|
||||
Map<unsigned, ValuePtr> m_values;
|
||||
StringMap<ValuePtr> m_valuesByName;
|
||||
};
|
||||
|
||||
// Like NetVersionSender, except no concept of version, just sends only when
|
||||
// values have been changed in between writes. Only works when writing to a
|
||||
// single receiver. Is binary compatible with NetVersionSender.
|
||||
class NetSyncSender: public NetVersionSender {
|
||||
public:
|
||||
NetSyncSender();
|
||||
NetSyncSender(NetSchema const& schema);
|
||||
|
||||
// Write all of the states.
|
||||
size_t writeFull(DataStream& ds);
|
||||
|
||||
// Write all the state changes that have occurred. It is possible to use
|
||||
// this without ever using writeFull (though it may transfer slightly more
|
||||
// data the first time).
|
||||
size_t writeDelta(DataStream& ds);
|
||||
|
||||
ByteArray writeFull();
|
||||
ByteArray writeDelta();
|
||||
|
||||
private:
|
||||
using NetVersionSender::incrementVersion;
|
||||
using NetVersionSender::currentVersion;
|
||||
using NetVersionSender::writeFull;
|
||||
using NetVersionSender::writeDelta;
|
||||
};
|
||||
|
||||
// Receive states using delta coding and optional smoothing from either a
|
||||
// NetVersionSender or a NetSyncSender
|
||||
class NetReceiver {
|
||||
public:
|
||||
typedef std::function<double(double, double)> DifferenceFunction;
|
||||
|
||||
NetReceiver();
|
||||
NetReceiver(NetSchema const& schema);
|
||||
|
||||
NetReceiver(NetReceiver const& receiver) = delete;
|
||||
NetReceiver& operator=(NetReceiver const& receiver) = delete;
|
||||
|
||||
void initialize(NetSchema const& schema);
|
||||
|
||||
NetSchema const& schema() const;
|
||||
|
||||
// Enables interpolation mode for this NetReceiver. If interpolation mode is
|
||||
// enabled, then all states are marked with a floating step value, and states
|
||||
// are delayed until the interpolation step reaches the given step value for
|
||||
// that state. This also enables optional smoothing on floating point values
|
||||
// that looks ahead into the future to even out changes, and can also
|
||||
// optionally extrapolate into the future if no data is available.
|
||||
void enableInterpolation(double startStep, double extrapolation = 0.0);
|
||||
void disableInterpolation();
|
||||
|
||||
// For certain values, simple subtraction between two values may not be
|
||||
// appropriate for finding the difference between them (such as circular
|
||||
// values). If provided, this function will be called instead.
|
||||
void setInterpolationDiffFunction(StringRef name, DifferenceFunction diffFunction = DifferenceFunction());
|
||||
|
||||
// If using interpolation, should be called every step. Throws away old
|
||||
// data, so if this is called with a future value then goes backwards, it may
|
||||
// result in different behavior (the interpolated data will be locked to the
|
||||
// highest time value given here).
|
||||
void setInterpolationStep(double step);
|
||||
|
||||
// Read full state list sent by a NetVersionSender.
|
||||
void readFull(DataStream& ds, size_t dataSize);
|
||||
void readFull(ByteArray const& fullState);
|
||||
|
||||
// Read partial state list sent by a NetVersionSender. If interpolation is
|
||||
// enabled, interpolationStep must be specified. interpolationStep is
|
||||
// assumed to monotonically increase here, and even in the event that it goes
|
||||
// backwards, the data provided most recently will still be treated as though
|
||||
// it is the best, most recent data.
|
||||
void readDelta(DataStream& ds, size_t dataSize, double interpolationStep = 0);
|
||||
void readDelta(ByteArray const& deltaState, double interpolationStep = 0);
|
||||
|
||||
// If no data for this timestep is available, treat it as though no data has
|
||||
// actually changed, thus preventing extrapolation. Only has an effect if
|
||||
// interpolation is in use.
|
||||
void interpolationHeartbeat(double interpolationStep);
|
||||
|
||||
// Has the current value of this field been read via get*? (Useful to avoid
|
||||
// a costly method on state change.) Safe to ignore completely.
|
||||
bool updated(StringRef name) const;
|
||||
|
||||
// Has the given even occurred since the last time eventOccurred was called
|
||||
// for this event? Right now, events can only be triggered as fast as
|
||||
// NetStates updates happen. Multiple calls to triggerEvent between updates
|
||||
// will only result in this returning true once.
|
||||
bool eventOccurred(StringRef eventName);
|
||||
|
||||
int64_t getInt(StringRef name);
|
||||
uint64_t getUInt(StringRef name);
|
||||
size_t getSize(StringRef name);
|
||||
double getFloat(StringRef name, bool interpolateIfAvailable = true);
|
||||
bool getBool(StringRef name);
|
||||
String getString(StringRef name);
|
||||
ByteArray getData(StringRef name);
|
||||
Variant getVariant(StringRef name);
|
||||
|
||||
// Forward all states from a receiver to another sender. This is useful to
|
||||
// tie one sender to multiple chained receivers. Data is forwarded
|
||||
// immediately as it is received, not as it is reached via interpolation.
|
||||
void forward(NetVersionSender& sender, bool forceAll = false);
|
||||
|
||||
private:
|
||||
typedef AnyOf<int64_t, uint64_t, size_t, double, bool, ByteArray, String, Variant> DataPoint;
|
||||
|
||||
struct Value {
|
||||
// Absolute Latest data.
|
||||
DataPoint latestData;
|
||||
bool needsForwarding;
|
||||
|
||||
// Best, most recent *exact* data point, without looking into the future.
|
||||
DataPoint exactData;
|
||||
bool exactUpdated;
|
||||
|
||||
// Interpolation stream
|
||||
StepStream<DataPoint> stream;
|
||||
DifferenceFunction diffFunction;
|
||||
};
|
||||
typedef std::shared_ptr<Value> ValuePtr;
|
||||
|
||||
ValuePtr const& getValue(StringRef name) const;
|
||||
size_t readValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value, double step);
|
||||
|
||||
NetSchema m_schema;
|
||||
Map<unsigned, ValuePtr> m_values;
|
||||
StringMap<ValuePtr> m_valuesByName;
|
||||
|
||||
bool m_interpolationEnabled;
|
||||
double m_interpolationStep;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
26
attic/old_source/StarServerShell.cpp
Normal file
26
attic/old_source/StarServerShell.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "StarServerShell.hpp"
|
||||
#include "StarUniverseServer.hpp"
|
||||
#include "StarLua.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
ServerShell::ServerShell(UniverseServerWeakPtr universe)
|
||||
: m_universe(universe) {}
|
||||
|
||||
void ServerShell::run() {
|
||||
|
||||
}
|
||||
|
||||
String ServerShell::help() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
UniverseServerPtr ServerShell::universe() const {
|
||||
if (auto universe = m_universe.lock()) {
|
||||
return universe;
|
||||
}
|
||||
|
||||
throw ServerShellException("Running universe not valid.");
|
||||
}
|
||||
|
||||
}
|
28
attic/old_source/StarServerShell.hpp
Normal file
28
attic/old_source/StarServerShell.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef STAR_SERVER_SHELL_HPP
|
||||
#define STAR_SERVER_SHELL_HPP
|
||||
|
||||
#include "StarException.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_EXCEPTION(ServerShellException, StarException);
|
||||
|
||||
STAR_CLASS(UniverseServer);
|
||||
STAR_CLASS(LuaContext);
|
||||
|
||||
class ServerShell {
|
||||
public:
|
||||
ServerShell(UniverseServerWeakPtr universe);
|
||||
void run();
|
||||
|
||||
private:
|
||||
String help() const;
|
||||
UniverseServerPtr universe() const;
|
||||
|
||||
UniverseServerWeakPtr m_universe;
|
||||
LuaContextPtr m_shellContext;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
71
attic/old_source/StarStreamingVideoInterface.cpp
Normal file
71
attic/old_source/StarStreamingVideoInterface.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "StarStreamingVideoInterface.hpp"
|
||||
#include "StarGuiReader.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarStreamingVideoController.hpp"
|
||||
#include "StarWrapLabelWidget.hpp"
|
||||
#include "StarButtonWidget.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
#include "StarAssets.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
StreamingVideoOptionsPane::StreamingVideoOptionsPane(StreamingVideoControllerPtr streamingVideoController) {
|
||||
m_streamingVideoController = streamingVideoController;
|
||||
GuiReader reader;
|
||||
reader.registerCallback("close", [=](WidgetPtr) -> bool {hide(); return true;});
|
||||
|
||||
reader.registerCallback("btnStart", [=](WidgetPtr) -> bool {start(); return true;});
|
||||
reader.registerCallback("btnStop", [=](WidgetPtr) -> bool {stop(); return true;});
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
reader.construct(assets->variant("/interface/windowconfig/streamingvideo.config:paneLayout"), this);
|
||||
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText("");
|
||||
fetchChild<WrapLabelWidget>("lblStatus")->setText("");
|
||||
}
|
||||
|
||||
StreamingVideoOptionsPane::~StreamingVideoOptionsPane() {
|
||||
|
||||
}
|
||||
|
||||
void StreamingVideoOptionsPane::update() {
|
||||
if (m_streamingVideoController->hasError())
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText(m_streamingVideoController->nextError());
|
||||
if (m_streamingVideoController->hasStatus())
|
||||
fetchChild<WrapLabelWidget>("lblStatus")->setText(m_streamingVideoController->nextStatus());
|
||||
|
||||
if (m_streamingVideoController->active()) {
|
||||
fetchChild<ButtonWidget>("btnStart")->disable();
|
||||
fetchChild<ButtonWidget>("btnStop")->enable();
|
||||
} else {
|
||||
fetchChild<ButtonWidget>("btnStart")->enable();
|
||||
fetchChild<ButtonWidget>("btnStop")->disable();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingVideoOptionsPane::start() {
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText("");
|
||||
fetchChild<WrapLabelWidget>("lblStatus")->setText("Start requested.");
|
||||
try {
|
||||
m_streamingVideoController->setStreamConfiguration(Root::singleton().assets()->variant("/streamingvideo.config"));
|
||||
m_streamingVideoController->start();
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
Logger::error("Error starting streamingvideo: %s", e.what());
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText(strf("Error starting streamingvideo: %s", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingVideoOptionsPane::stop() {
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText("");
|
||||
fetchChild<WrapLabelWidget>("lblStatus")->setText("Stop requested.");
|
||||
try {
|
||||
m_streamingVideoController->stop();
|
||||
}
|
||||
catch (std::exception const& e) {
|
||||
Logger::error("Error stopping streamingvideo: %s", e.what());
|
||||
fetchChild<WrapLabelWidget>("lblErrors")->setText(strf("Error stopping streamingvideo: %s", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
attic/old_source/StarStreamingVideoInterface.hpp
Normal file
27
attic/old_source/StarStreamingVideoInterface.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef STAR_STREAMINGVIDEO_INTERFACE_HPP
|
||||
#define STAR_STREAMINGVIDEO_INTERFACE_HPP
|
||||
|
||||
#include "StarPane.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(StreamingVideoController);
|
||||
|
||||
STAR_CLASS(StreamingVideoOptionsPane);
|
||||
class StreamingVideoOptionsPane : public Pane {
|
||||
public:
|
||||
StreamingVideoOptionsPane(StreamingVideoControllerPtr streamingVideoController);
|
||||
virtual ~StreamingVideoOptionsPane();
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
private:
|
||||
StreamingVideoControllerPtr m_streamingVideoController;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
117
attic/old_source/StarStringContainers.cpp
Normal file
117
attic/old_source/StarStringContainers.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "StarStringContainers.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include "pcre.h"
|
||||
|
||||
namespace Star {
|
||||
|
||||
StringList::StringList()
|
||||
: Base() {}
|
||||
|
||||
StringList::StringList(const Base& l)
|
||||
: Base(l) {}
|
||||
|
||||
StringList::StringList(Base&& l)
|
||||
: Base(std::move(l)) {}
|
||||
|
||||
StringList::StringList(size_t n, String::Char const* const * list) {
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
append(String(list[i]));
|
||||
}
|
||||
|
||||
StringList::StringList(size_t len, const String& s1)
|
||||
: Base(len, s1) {
|
||||
}
|
||||
|
||||
StringList::StringList(std::initializer_list<String> list)
|
||||
: Base(list) {
|
||||
}
|
||||
|
||||
bool StringList::contains(const String& s, String::CaseSensitivity cs) const {
|
||||
for (const_iterator i = begin(); i != end(); ++i) {
|
||||
if (s.compare(*i, cs) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
StringList StringList::trimAll(const String& pattern) const {
|
||||
StringList r;
|
||||
for (auto const& s : *this)
|
||||
r.append(s.trim(pattern));
|
||||
return r;
|
||||
}
|
||||
|
||||
String StringList::join(const String& separator) const {
|
||||
String joinedString;
|
||||
for (const_iterator i = begin(); i != end(); ++i) {
|
||||
if (i != begin())
|
||||
joinedString += separator;
|
||||
joinedString += *i;
|
||||
}
|
||||
|
||||
return joinedString;
|
||||
}
|
||||
|
||||
StringList& StringList::append(String const& e) {
|
||||
Base::append(e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::append(String&& e) {
|
||||
Base::append(std::move(e));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::prepend(String const& e) {
|
||||
Base::prepend(e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::prepend(String&& e) {
|
||||
Base::prepend(std::move(e));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::remove(String const& e) {
|
||||
Base::remove(e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::erase(size_t index) {
|
||||
Base::erase(index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::erase(size_t begin, size_t end) {
|
||||
Base::erase(begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::insert(size_t pos, String const& e) {
|
||||
Base::insert(pos, e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList& StringList::set(size_t pos, String const& e) {
|
||||
Base::set(pos, e);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringList StringList::slice(SliceIndex a, SliceIndex b, int i) const {
|
||||
return Star::slice(*this, a, b, i);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StringList& list) {
|
||||
os << "(";
|
||||
for (auto i = list.begin(); i != list.end(); ++i) {
|
||||
if (i != list.begin())
|
||||
os << ", ";
|
||||
|
||||
os << '\'' << *i << '\'';
|
||||
}
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
698
attic/old_source/StarStringContainers.hpp
Normal file
698
attic/old_source/StarStringContainers.hpp
Normal file
|
@ -0,0 +1,698 @@
|
|||
#ifndef STAR_STRING_CONTAINERS_HPP
|
||||
#define STAR_STRING_CONTAINERS_HPP
|
||||
|
||||
#include "StarString.hpp"
|
||||
#include "StarList.hpp"
|
||||
#include "StarMap.hpp"
|
||||
#include "StarSet.hpp"
|
||||
#include "StarMaybe.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
class StringList : public List<String> {
|
||||
public:
|
||||
typedef List<String> Base;
|
||||
typedef Base::iterator iterator;
|
||||
typedef Base::const_iterator const_iterator;
|
||||
|
||||
typedef Base::value_type value_type;
|
||||
|
||||
template<typename Container>
|
||||
static StringList from(Container const& m) {
|
||||
return StringList(m.begin(), m.end());
|
||||
}
|
||||
|
||||
StringList();
|
||||
StringList(Base const& l);
|
||||
StringList(Base&& l);
|
||||
StringList(size_t len, String::Char const* const* list);
|
||||
StringList(size_t len, String const& s1);
|
||||
StringList(std::initializer_list<String> list);
|
||||
|
||||
template<typename InputIterator>
|
||||
StringList(InputIterator beg, InputIterator end) : Base(beg, end) {}
|
||||
|
||||
bool contains(String const& s, String::CaseSensitivity cs = String::CaseSensitive) const;
|
||||
StringList trimAll(String const& pattern = "") const;
|
||||
String join(String const& separator = "") const;
|
||||
|
||||
StringList& append(String const& e);
|
||||
StringList& append(String&& e);
|
||||
StringList& prepend(String const& e);
|
||||
StringList& prepend(String&& e);
|
||||
|
||||
template<typename Container>
|
||||
StringList& appendAll(Container&& list);
|
||||
template<typename Container>
|
||||
StringList& prependAll(Container&& list);
|
||||
|
||||
StringList& remove(String const& e);
|
||||
|
||||
StringList& erase(size_t index);
|
||||
StringList& erase(size_t begin, size_t end);
|
||||
using Base::erase;
|
||||
|
||||
StringList& insert(size_t pos, String const& e);
|
||||
template<typename Container>
|
||||
StringList& insertAll(size_t pos, Container const& l);
|
||||
using Base::insert;
|
||||
|
||||
StringList& set(size_t pos, String const& e);
|
||||
|
||||
StringList slice(SliceIndex a = SliceIndex(), SliceIndex b = SliceIndex(), int i = 1) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringList const& list);
|
||||
|
||||
// A type that holds a reference to String/std::string/char*, or an actual held
|
||||
// String. The lifetime of the class must not be longer than that of a normal
|
||||
// const&. Is efficient for both lvalue and rvalue references. Internally to
|
||||
// StringMap, hold() will be called to ensure that the ref holds an actual
|
||||
// copy of the string.
|
||||
class StringRef {
|
||||
public:
|
||||
// All lvalue constructors make the StringRef hold only the pointer, all
|
||||
// rvalue constructors cause StringRef to hold the actual String.
|
||||
StringRef(String const& s);
|
||||
StringRef(String&& s);
|
||||
StringRef(std::string const& s);
|
||||
StringRef(std::string&& s);
|
||||
StringRef(char const* p);
|
||||
|
||||
// No copy constructor is provided to make tracking ownership sane.
|
||||
StringRef(StringRef&& sr);
|
||||
StringRef& operator=(StringRef&& sr);
|
||||
|
||||
char const* ptr() const;
|
||||
|
||||
// Cause this StringRef to always hold a String, not just a ptr. Will be
|
||||
// cheap if StringRef was constructed with an rvalue.
|
||||
void hold();
|
||||
|
||||
// Gets a const& to the held String if it is held, UB otherwise.
|
||||
String const& held() const;
|
||||
|
||||
bool operator==(StringRef const& ref) const;
|
||||
|
||||
private:
|
||||
void set(String s);
|
||||
|
||||
Maybe<String> m_held;
|
||||
char const* m_ptr;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, StringRef const& ref);
|
||||
|
||||
template<>
|
||||
struct hash<StringRef> {
|
||||
size_t operator()(StringRef const& s) const;
|
||||
CharHasher<char> impl;
|
||||
};
|
||||
|
||||
struct CaseInsensitiveStringHash {
|
||||
size_t operator()(StringRef const& s) const;
|
||||
CharHasher<char> impl;
|
||||
};
|
||||
|
||||
struct CaseInsensitiveStringCompare {
|
||||
bool operator()(StringRef const& lhs, StringRef const& rhs) const;
|
||||
};
|
||||
|
||||
// A sort of version of HashMap<String, T> that is much faster than the normal
|
||||
// one for a great many use cases. For example, normal HashMap<String, T>
|
||||
// forces the user to copy from a char* into a String to do basic things like
|
||||
// search. This class avoids that by nicely supporting char const* as a key
|
||||
// type without inducing copying.
|
||||
//
|
||||
// It is *almost*, but not *completely* compatible with a normal Map type. In
|
||||
// particular, iteration is difficult because the key type held internally
|
||||
// *cannot* be String when using std::unordered_map as the base map. As a
|
||||
// result, iteration is somewhat weird in that an iterator always returns a
|
||||
// pair of refs, so something like:
|
||||
//
|
||||
// for (auto p : map)
|
||||
// p.second.mutate();
|
||||
//
|
||||
// Will affect the copy in the map, and the second value in the pair is always
|
||||
// mutable whenever non-const iterators are used.
|
||||
template<typename MappedT, typename HashT = CharHasher<char>, typename ComparatorT = std::equal_to<StringRef>>
|
||||
class StringMap {
|
||||
private:
|
||||
public:
|
||||
typedef String key_type;
|
||||
typedef MappedT mapped_type;
|
||||
typedef pair<key_type, mapped_type> value_type;
|
||||
|
||||
typedef HashT Hash;
|
||||
typedef ComparatorT Comparator;
|
||||
|
||||
struct InternalHash {
|
||||
bool operator()(StringRef const& s) const;
|
||||
Hash hash;
|
||||
};
|
||||
|
||||
struct InternalComparator {
|
||||
bool operator()(StringRef const& a, StringRef const& b) const;
|
||||
Comparator comparator;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<StringRef, mapped_type, InternalHash, InternalComparator> InternalMap;
|
||||
typedef typename InternalMap::iterator IteratorBase;
|
||||
typedef typename InternalMap::const_iterator ConstIteratorBase;
|
||||
|
||||
template<typename T>
|
||||
struct ArrowProxy {
|
||||
T* operator->();
|
||||
T t;
|
||||
};
|
||||
|
||||
struct iterator : public IteratorBase {
|
||||
iterator();
|
||||
iterator(IteratorBase i);
|
||||
|
||||
IteratorBase const& base() const;
|
||||
|
||||
ArrowProxy<pair<key_type const&, mapped_type&> const> operator->() const;
|
||||
pair<key_type const&, mapped_type&> const operator*() const;
|
||||
};
|
||||
|
||||
struct const_iterator : public ConstIteratorBase {
|
||||
const_iterator();
|
||||
const_iterator(ConstIteratorBase i);
|
||||
|
||||
ConstIteratorBase const& base() const;
|
||||
|
||||
ArrowProxy<pair<key_type const&, mapped_type const&> const> operator->() const;
|
||||
pair<key_type const&, mapped_type const&> const operator*() const;
|
||||
};
|
||||
|
||||
template<typename Container>
|
||||
static StringMap from(Container const& m) {
|
||||
return StringMap(m.begin(), m.end());
|
||||
}
|
||||
|
||||
StringMap();
|
||||
StringMap(StringMap const& map);
|
||||
StringMap(StringMap&& map);
|
||||
template<typename InputIterator>
|
||||
StringMap(InputIterator beg, InputIterator end);
|
||||
StringMap(std::initializer_list<value_type> list);
|
||||
|
||||
StringList keys() const;
|
||||
List<mapped_type> values() const;
|
||||
List<value_type> pairs() const;
|
||||
|
||||
bool contains(StringRef const& k) const;
|
||||
|
||||
mapped_type& get(StringRef const& k);
|
||||
mapped_type const& get(StringRef const& k) const;
|
||||
|
||||
mapped_type value(StringRef const& k, mapped_type d = mapped_type()) const;
|
||||
Maybe<mapped_type const&> maybe(StringRef const& k) const;
|
||||
Maybe<mapped_type&> maybe(StringRef const& k);
|
||||
|
||||
mapped_type& operator[](StringRef k);
|
||||
|
||||
StringMap& operator=(StringMap const& map);
|
||||
StringMap& operator=(StringMap&& map);
|
||||
|
||||
pair<iterator, bool> insert(value_type v);
|
||||
|
||||
bool insert(StringRef k, mapped_type v);
|
||||
iterator insert(iterator pos, value_type v);
|
||||
|
||||
bool remove(StringRef const& k);
|
||||
|
||||
mapped_type take(StringRef const& k);
|
||||
|
||||
const_iterator cbegin() const;
|
||||
const_iterator cend() const;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
size_t size() const;
|
||||
|
||||
iterator erase(iterator i);
|
||||
size_t erase(key_type const& k);
|
||||
|
||||
iterator find(StringRef const& k);
|
||||
|
||||
const_iterator find(StringRef const& k) const;
|
||||
|
||||
void clear();
|
||||
|
||||
bool empty() const;
|
||||
|
||||
// Appends all values of given map into this map. If overwite is false, then
|
||||
// skips values that already exist in this map. Returns false if any keys
|
||||
// previously existed.
|
||||
template<typename MapType>
|
||||
bool merge(MapType const& m, bool overwrite = false);
|
||||
|
||||
mapped_type& add(StringRef k, mapped_type v);
|
||||
|
||||
bool operator==(StringMap const& m) const;
|
||||
|
||||
private:
|
||||
InternalMap m_map;
|
||||
};
|
||||
|
||||
typedef HashSet<String> StringSet;
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
std::ostream& operator<<(std::ostream& os, StringMap<MappedT, HashT, ComparatorT> const& ref);
|
||||
|
||||
inline size_t hash<StringRef>::operator()(StringRef const& s) const {
|
||||
return impl(s.ptr());
|
||||
};
|
||||
|
||||
inline size_t CaseInsensitiveStringHash::operator()(StringRef const& s) const {
|
||||
auto p = s.ptr();
|
||||
size_t h = 0;
|
||||
while (*p) {
|
||||
String::Char c;
|
||||
p += utf8DecodeChar(p, &c);
|
||||
h = h * 101 + String::toLower(c);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
inline bool CaseInsensitiveStringCompare::operator()(StringRef const& a, StringRef const& b) const {
|
||||
auto lhs = a.ptr();
|
||||
auto rhs = b.ptr();
|
||||
while (*lhs && *rhs) {
|
||||
String::Char lhsc;
|
||||
lhs += utf8DecodeChar(lhs, &lhsc);
|
||||
|
||||
String::Char rhsc;
|
||||
rhs += utf8DecodeChar(rhs, &rhsc);
|
||||
|
||||
if (String::toLower(lhsc) != String::toLower(rhsc))
|
||||
return false;
|
||||
}
|
||||
return !*lhs && !*rhs;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
StringList& StringList::appendAll(Container&& list) {
|
||||
Base::appendAll(std::forward<Container&&>(list));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
StringList& StringList::prependAll(Container&& list) {
|
||||
Base::prependAll(std::forward<Container&&>(list));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
StringList& StringList::insertAll(size_t pos, Container const& l) {
|
||||
Base::insertAll(pos, std::forward<Container&&>(l));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline StringRef::StringRef(StringRef&& sr) {
|
||||
operator=(move(sr));
|
||||
}
|
||||
|
||||
inline StringRef::StringRef(String const& s)
|
||||
: m_ptr(s.utf8Ptr()) {}
|
||||
|
||||
inline StringRef::StringRef(String&& s) {
|
||||
set(move(s));
|
||||
}
|
||||
|
||||
inline StringRef::StringRef(std::string const& s)
|
||||
: m_ptr(s.c_str()) {}
|
||||
|
||||
inline StringRef::StringRef(std::string&& s) {
|
||||
set(move(s));
|
||||
}
|
||||
|
||||
inline StringRef::StringRef(char const* p)
|
||||
: m_ptr(p) {}
|
||||
|
||||
inline StringRef& StringRef::operator=(StringRef&& sr) {
|
||||
if (sr.m_held) {
|
||||
set(sr.m_held.take());
|
||||
} else {
|
||||
m_held.reset();
|
||||
m_ptr = sr.m_ptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline char const* StringRef::ptr() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
inline void StringRef::hold() {
|
||||
if (!m_held)
|
||||
set(String(m_ptr));
|
||||
}
|
||||
|
||||
inline String const& StringRef::held() const {
|
||||
return *m_held;
|
||||
}
|
||||
|
||||
inline bool StringRef::operator==(StringRef const& ref) const {
|
||||
if (m_held) {
|
||||
if (ref.m_held)
|
||||
return *m_held == *ref.m_held;
|
||||
}
|
||||
return strcmp(ptr(), ref.ptr()) == 0;
|
||||
}
|
||||
|
||||
inline void StringRef::set(String s) {
|
||||
m_held = move(s);
|
||||
m_ptr = m_held->utf8Ptr();
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, StringRef const& ref) {
|
||||
os << ref.ptr();
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::InternalHash::operator()(StringRef const& s) const {
|
||||
return hash(s.ptr());
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::InternalComparator::operator()(StringRef const& a, StringRef const& b) const {
|
||||
return comparator(a.ptr(), b.ptr());
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
template<typename T>
|
||||
T* StringMap<MappedT, HashT, ComparatorT>::ArrowProxy<T>::operator->() {
|
||||
return &t;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::iterator::iterator() {}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::iterator::iterator(IteratorBase i)
|
||||
: IteratorBase(move(i)) {}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::iterator::base() const -> IteratorBase const& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::iterator::operator->() const -> ArrowProxy<pair<key_type const&, mapped_type&> const> {
|
||||
return {pair<key_type const&, mapped_type&>(base()->first.held(), base()->second)};
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::iterator::operator*() const -> pair<key_type const&, mapped_type&> const {
|
||||
return {base()->first.held(), base()->second};
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::const_iterator::const_iterator() {}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::const_iterator::const_iterator(ConstIteratorBase i)
|
||||
: ConstIteratorBase(move(i)) {}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::base() const -> ConstIteratorBase const& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::operator->() const -> ArrowProxy<pair<key_type const&, mapped_type const&> const> {
|
||||
return {pair<key_type const&, mapped_type const&>(base()->first.held(), base()->second)};
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::operator*() const -> pair<key_type const&, mapped_type const&> const {
|
||||
return {base()->first.held(), base()->second};
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::StringMap() {}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::StringMap(StringMap const& map) {
|
||||
for (auto const& p : map)
|
||||
insert(p);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::StringMap(StringMap&& map) {
|
||||
m_map = move(map.m_map);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
template<typename InputIterator>
|
||||
StringMap<MappedT, HashT, ComparatorT>::StringMap(InputIterator beg, InputIterator end) {
|
||||
for (auto i = beg; i != end; ++i)
|
||||
insert(*i);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>::StringMap(std::initializer_list<value_type> list) {
|
||||
for (auto const& p : list)
|
||||
insert(p);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringList StringMap<MappedT, HashT, ComparatorT>::keys() const {
|
||||
StringList keys;
|
||||
for (auto const& p : *this)
|
||||
keys.append(p.first);
|
||||
return keys;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::values() const -> List<mapped_type> {
|
||||
List<mapped_type> values;
|
||||
for (auto const& p : *this)
|
||||
values.append(p.second);
|
||||
return values;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::pairs() const -> List<value_type> {
|
||||
List<value_type> pairs;
|
||||
for (auto const& p : *this)
|
||||
pairs.append(p);
|
||||
return pairs;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::contains(StringRef const& k) const {
|
||||
return m_map.find(k) != m_map.end();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::get(StringRef const& k) -> mapped_type& {
|
||||
auto i = m_map.find(k);
|
||||
if (i == m_map.end())
|
||||
throw MapException(strf("Key '%s' not found in StringMap::get()", k.ptr()));
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::get(StringRef const& k) const -> mapped_type const& {
|
||||
return const_cast<StringMap*>(this)->get(k);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::value(StringRef const& k, mapped_type d) const -> mapped_type {
|
||||
auto i = m_map.find(k);
|
||||
if (i == m_map.end())
|
||||
return move(d);
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::maybe(StringRef const& k) const -> Maybe<mapped_type const&> {
|
||||
auto i = m_map.find(k);
|
||||
if (i == m_map.end())
|
||||
return {};
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::maybe(StringRef const& k) -> Maybe<mapped_type&> {
|
||||
auto i = m_map.find(k);
|
||||
if (i == m_map.end())
|
||||
return {};
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::operator[](StringRef k) -> mapped_type& {
|
||||
auto i = m_map.find(k);
|
||||
if (i == m_map.end()) {
|
||||
k.hold();
|
||||
i = m_map.insert(pair<StringRef, mapped_type>(move(k), mapped_type())).first;
|
||||
}
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>& StringMap<MappedT, HashT, ComparatorT>::operator=(StringMap const& map) {
|
||||
clear();
|
||||
for (auto const& p : map)
|
||||
insert(p);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
StringMap<MappedT, HashT, ComparatorT>& StringMap<MappedT, HashT, ComparatorT>::operator=(StringMap&& map) {
|
||||
m_map = move(map.m_map);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::insert(value_type v) -> std::pair<iterator, bool> {
|
||||
return m_map.insert(pair<StringRef, mapped_type>(move(v.first), move(v.second)));
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::insert(StringRef k, mapped_type v) {
|
||||
k.hold();
|
||||
return m_map.insert(pair<StringRef, mapped_type>(move(k), move(v))).second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::insert(iterator pos, value_type v) -> iterator {
|
||||
return m_map.insert(pos, pair<StringRef, mapped_type>(move(v.first), move(v.second)));
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::remove(StringRef const& k) {
|
||||
auto i = m_map.find(k);
|
||||
if (i != m_map.end()) {
|
||||
m_map.erase(i);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::take(StringRef const& k) -> mapped_type {
|
||||
auto i = m_map.find(k);
|
||||
if (i != m_map.end()) {
|
||||
MappedT v = std::move(i->second);
|
||||
m_map.erase(i);
|
||||
return std::move(v);
|
||||
} else {
|
||||
throw MapException(strf("Key '%s' not found in StringMap::take()", k));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::cbegin() const -> const_iterator {
|
||||
return m_map.cbegin();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::cend() const -> const_iterator {
|
||||
return m_map.cend();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::begin() const -> const_iterator {
|
||||
return m_map.begin();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::end() const -> const_iterator {
|
||||
return m_map.end();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::begin() -> iterator {
|
||||
return m_map.begin();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::end() -> iterator {
|
||||
return m_map.end();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
size_t StringMap<MappedT, HashT, ComparatorT>::size() const {
|
||||
return m_map.size();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::erase(iterator i) -> iterator {
|
||||
return m_map.erase(i);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
size_t StringMap<MappedT, HashT, ComparatorT>::erase(key_type const& k) {
|
||||
if (remove(k))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::find(StringRef const& k) -> iterator {
|
||||
return m_map.find(k);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::find(StringRef const& k) const -> const_iterator {
|
||||
return m_map.find(k);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
void StringMap<MappedT, HashT, ComparatorT>::clear() {
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::empty() const {
|
||||
return m_map.empty();
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
template<typename MapType>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::merge(MapType const& m, bool overwrite) {
|
||||
return mapMerge(*this, m, overwrite);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
auto StringMap<MappedT, HashT, ComparatorT>::add(StringRef k, mapped_type v) -> mapped_type& {
|
||||
k.hold();
|
||||
auto p = m_map.insert(pair<StringRef, mapped_type>(move(k), move(v)));
|
||||
if (!p.second)
|
||||
throw MapException(strf("Entry with key '%s' already present.", outputAny(k)));
|
||||
else
|
||||
return p.first->second;
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
bool StringMap<MappedT, HashT, ComparatorT>::operator==(StringMap const& m) const {
|
||||
return mapsEqual(*this, m);
|
||||
}
|
||||
|
||||
template<typename MappedT, typename HashT, typename ComparatorT>
|
||||
std::ostream& operator<<(std::ostream& os, StringMap<MappedT, HashT, ComparatorT> const& ref) {
|
||||
printMap(os, ref);
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
434
attic/old_source/StarUniverseController.cpp
Normal file
434
attic/old_source/StarUniverseController.cpp
Normal file
|
@ -0,0 +1,434 @@
|
|||
#include "StarUniverseController.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarException.hpp"
|
||||
#include "StarBuffer.hpp"
|
||||
#include "StarJsonVariant.hpp"
|
||||
#include "StarPerformance.hpp"
|
||||
#include "StarLogging.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
UniverseController::UniverseController(TcpSocketPtr socket) : Thread("UniverseController") {
|
||||
m_socket = socket;
|
||||
m_parent = 0;
|
||||
m_stop = false;
|
||||
m_closed = false;
|
||||
}
|
||||
|
||||
void UniverseController::stop() {
|
||||
MutexLocker lock(m_lock);
|
||||
m_stop = true;
|
||||
m_condition.broadcast();
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
bool UniverseController::isClosed() {
|
||||
return m_closed;
|
||||
}
|
||||
|
||||
void UniverseController::bind(UniverseControllerSet* parent) {
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
void UniverseController::receiveResponse(Variant const& response) {
|
||||
MutexLocker lock(m_lock);
|
||||
m_responses.append(response);
|
||||
m_condition.broadcast();
|
||||
}
|
||||
|
||||
void UniverseController::run() {
|
||||
try {
|
||||
Buffer buffer(1024);
|
||||
MutexLocker lock(m_lock);
|
||||
while (!m_stop && m_socket->isOpen()) {
|
||||
buffer.clear();
|
||||
List<int> modeStack;
|
||||
bool trivial = true;
|
||||
int mode = 0;
|
||||
|
||||
/*
|
||||
* Mode list
|
||||
* 0 primitive mode, accepts json or a single line of text which is interpreted as list of strings
|
||||
* 1 list mode
|
||||
* 20 object initial mode
|
||||
* 21 object next mode
|
||||
* 22 object colon mode
|
||||
* 3 string mode 31 espaced char in string mode
|
||||
* 4 value mode
|
||||
* 5 literal mode
|
||||
* 6 done, but search for newline
|
||||
* -1 complete request
|
||||
* -2 syntax error
|
||||
*
|
||||
*/
|
||||
|
||||
while (mode >= 0) {
|
||||
char c;
|
||||
if (m_socket->read(&c, 1) != 1)
|
||||
throw StarException("Unexpected result from socket read\n");
|
||||
if (c == '\r')
|
||||
continue; // hello dos
|
||||
buffer.write(&c, 1);
|
||||
revisit: if ((mode == 0) || (mode == 1) || (mode == 20) || (mode == 4) || (mode == 6)) {
|
||||
if ((c == ' ') || (c == '\t')) {
|
||||
// trim whitespace
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((mode == 1) || (mode == 20) || (mode == 4)) {
|
||||
if (c == '\n') {
|
||||
// trim newlines
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (mode) {
|
||||
case 0: { // primitive mode
|
||||
if (c == '\n') {
|
||||
mode = -1; //empty line, meh
|
||||
} else if (c == '[') {
|
||||
modeStack.push_back(6);
|
||||
modeStack.push_back(1);
|
||||
trivial = false;
|
||||
mode = 4;
|
||||
} else if (c == '{') {
|
||||
modeStack.push_back(6);
|
||||
modeStack.push_back(20);
|
||||
trivial = false;
|
||||
mode = 4;
|
||||
} else if (c == '"') {
|
||||
modeStack.push_back(6);
|
||||
mode = 3;
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: { // list mode
|
||||
if (c == ',') {
|
||||
modeStack.push_back(1);
|
||||
mode = 4;
|
||||
} else if (c == ']') {
|
||||
mode = modeStack.takeLast();
|
||||
} else
|
||||
mode = -2;
|
||||
break;
|
||||
}
|
||||
case 20: { // object
|
||||
if (c == '}') {
|
||||
mode = modeStack.takeLast();
|
||||
} else {
|
||||
modeStack.push_back(22);
|
||||
mode = 4;
|
||||
goto revisit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 21: { // continue bit of object pairs
|
||||
if (c == '}') {
|
||||
mode = modeStack.takeLast();
|
||||
} else if (c == ',') {
|
||||
modeStack.push_back(22);
|
||||
mode = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 22: { // colon bit of object pairs
|
||||
if (c == ':') {
|
||||
modeStack.push_back(21);
|
||||
mode = 4;
|
||||
} else {
|
||||
mode = -2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: { // string mode
|
||||
if (c == '\\')
|
||||
mode = 31;
|
||||
else if (c == '"')
|
||||
mode = modeStack.takeLast();
|
||||
break;
|
||||
}
|
||||
case 31: { // escaped in string mode
|
||||
mode = 3;
|
||||
break;
|
||||
}
|
||||
case 4: { // value mode
|
||||
if (c == '[') {
|
||||
modeStack.push_back(mode);
|
||||
modeStack.push_back(1);
|
||||
trivial = false;
|
||||
mode = 4;
|
||||
} else if (c == '{') {
|
||||
modeStack.push_back(mode);
|
||||
modeStack.push_back(20);
|
||||
trivial = false;
|
||||
mode = 4;
|
||||
} else if (c == '"') {
|
||||
modeStack.push_back(mode);
|
||||
mode = 3;
|
||||
} else {
|
||||
mode = 5;
|
||||
goto revisit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: { // literal mode
|
||||
if ((c == ' ') || (c == '\t') || (c == '\n') || (c == ',') || (c == ']') || (c == '}') || (c == ':')) {
|
||||
mode = modeStack.takeLast();
|
||||
goto revisit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (c == '\n') {
|
||||
mode = -1;
|
||||
} else {
|
||||
mode = -2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasCommand = false;
|
||||
VariantList request;
|
||||
|
||||
if (mode == -2) {
|
||||
while (true) {
|
||||
char c;
|
||||
if (m_socket->read(&c, 1) != 1)
|
||||
throw StarException("Unexpected result from socket read\n");
|
||||
if (c != '\n')
|
||||
continue;
|
||||
}
|
||||
|
||||
VariantList failure;
|
||||
failure.append("syntaxError");
|
||||
m_responses.push_back(failure);
|
||||
} else {
|
||||
String requestStr = String(buffer.ptr(), buffer.dataSize());
|
||||
if (requestStr.size() == 0) {
|
||||
VariantList failure;
|
||||
failure.append("typeHelpForMoreInfo");
|
||||
m_responses.push_back(failure);
|
||||
} else if (trivial) {
|
||||
VariantList parts;
|
||||
for (auto part : requestStr.splitWhitespace()) {
|
||||
if (part.size() > 0)
|
||||
parts.append(part);
|
||||
}
|
||||
if (parts.size() == 0) {
|
||||
VariantList failure;
|
||||
failure.append("typeHelpForMoreInfo");
|
||||
m_responses.push_back(failure);
|
||||
} else {
|
||||
request = parts;
|
||||
hasCommand = true;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Variant parseResult = Variant::parse(requestStr);
|
||||
bool valid = true;
|
||||
if (valid && (parseResult.type() != Variant::Type::LIST))
|
||||
valid = false;
|
||||
if (valid)
|
||||
request = parseResult.toList();
|
||||
if (valid && (request.size() < 1))
|
||||
valid = false;
|
||||
if (valid && (request[0].type() != Variant::Type::STRING))
|
||||
valid = false;
|
||||
|
||||
if (!valid) {
|
||||
VariantList failure;
|
||||
failure.append("parseError");
|
||||
failure.append("Must be a list with the first element of string type");
|
||||
m_responses.push_back(failure);
|
||||
} else {
|
||||
hasCommand = true;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
VariantList failure;
|
||||
failure.append("parseError");
|
||||
failure.append(e.what());
|
||||
m_responses.push_back(failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCommand) {
|
||||
m_parent->commandReceived(make_shared<ControllerCommand>(shared_from_this(), request));
|
||||
}
|
||||
if (hasCommand)
|
||||
while ((m_responses.size() == 0) && (!m_stop))
|
||||
m_condition.wait(m_lock);
|
||||
for (auto response : m_responses) {
|
||||
buffer.clear();
|
||||
outputUtf8Json(response, IODeviceOutputIterator(&buffer), 0, false);
|
||||
char c = '\n';
|
||||
buffer.write(&c, 1);
|
||||
if (m_socket->write(buffer.ptr(), buffer.dataSize()) != buffer.dataSize())
|
||||
throw StarException("Unexpected result from socket write\n");
|
||||
m_socket->flush();
|
||||
}
|
||||
m_responses.clear();
|
||||
}
|
||||
} catch (const NetworkException& e) {
|
||||
goto finally;
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("UniverseController: Exception caught: %s", e.what());
|
||||
goto finally;
|
||||
} catch (...) {
|
||||
goto finally;
|
||||
}
|
||||
finally: {
|
||||
m_closed = true;
|
||||
}
|
||||
m_parent->notify();
|
||||
}
|
||||
|
||||
UniverseControllerSet::UniverseControllerSet(UniverseServer* universeServer): Thread("UniverseControllerSet") {
|
||||
m_universeServer = universeServer;
|
||||
m_stop = false;
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void UniverseControllerSet::addNewConnection(UniverseControllerPtr controller) {
|
||||
MutexLocker lock(m_lock);
|
||||
if (m_stop)
|
||||
throw StarException("Shutting down");
|
||||
controller->bind(this);
|
||||
m_controllers.add(controller);
|
||||
m_active = true;
|
||||
m_condition.broadcast();
|
||||
controller->start();
|
||||
}
|
||||
|
||||
void UniverseControllerSet::stop() {
|
||||
MutexLocker lock(m_lock);
|
||||
m_stop = true;
|
||||
m_active = true;
|
||||
m_condition.broadcast();
|
||||
}
|
||||
|
||||
void UniverseControllerSet::notify() {
|
||||
MutexLocker lock(m_lock);
|
||||
m_condition.broadcast();
|
||||
}
|
||||
|
||||
void UniverseControllerSet::commandTypeHelpForMoreInfo(ControllerCommandPtr command) {
|
||||
VariantList result;
|
||||
result.append("todo");
|
||||
command->respond(result);
|
||||
}
|
||||
|
||||
void UniverseControllerSet::commandCounters(ControllerCommandPtr command) {
|
||||
VariantList result;
|
||||
result.append("counters");
|
||||
|
||||
VariantMap config;
|
||||
if (command->request().size() >=2)
|
||||
config = command->request()[1].toMap();
|
||||
|
||||
bool reset = config.value("reset", true).toBool();
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
for (auto records : assets->variant("/perf.config:parenting").list())
|
||||
Performance::setCounterHierarchy(records.getString(1).utf8Ptr(), records.getString(0).utf8Ptr());
|
||||
|
||||
result.append(Star::Performance::dumpToVariant(reset));
|
||||
|
||||
command->respond(result);
|
||||
}
|
||||
|
||||
void UniverseControllerSet::commandUnknown(ControllerCommandPtr command) {
|
||||
VariantList result;
|
||||
result.append("unkownCommand");
|
||||
result.append(command->command());
|
||||
command->respond(result);
|
||||
}
|
||||
|
||||
void UniverseControllerSet::run() {
|
||||
while (!m_stop) {
|
||||
{
|
||||
MutexLocker lock(m_lock);
|
||||
m_active = false;
|
||||
}
|
||||
Set<UniverseControllerPtr> closed;
|
||||
{
|
||||
MutexLocker lock(m_lock);
|
||||
for (auto iter = m_controllers.begin(); iter != m_controllers.end(); iter++) {
|
||||
auto controller = *iter;
|
||||
if (controller->isClosed())
|
||||
closed.add(controller);
|
||||
}
|
||||
}
|
||||
for (auto controller : closed) {
|
||||
m_controllers.remove(controller);
|
||||
controller->stop();
|
||||
controller->join();
|
||||
}
|
||||
List<ControllerCommandPtr> commands;
|
||||
{
|
||||
MutexLocker lock(m_lock);
|
||||
for (auto command : m_commandQueue)
|
||||
commands.push_back(command);
|
||||
m_commandQueue.clear();
|
||||
}
|
||||
for (auto command : commands) {
|
||||
if (command->command() == "typeHelpForMoreInfo")
|
||||
commandTypeHelpForMoreInfo(command);
|
||||
else if (command->command() == "counters")
|
||||
commandCounters(command);
|
||||
else if (command->command() == "worlds")
|
||||
m_universeServer->commandReceived(command);
|
||||
else if (command->command() == "world")
|
||||
m_universeServer->commandReceived(command);
|
||||
else
|
||||
commandUnknown(command);
|
||||
}
|
||||
{
|
||||
MutexLocker lock(m_lock);
|
||||
if (!m_active)
|
||||
m_condition.wait(m_lock);
|
||||
}
|
||||
}
|
||||
for (auto iter = m_controllers.begin(); iter != m_controllers.end(); iter++) {
|
||||
auto controller = *iter;
|
||||
controller->stop();
|
||||
controller->join();
|
||||
}
|
||||
m_controllers.clear();
|
||||
}
|
||||
|
||||
void UniverseControllerSet::commandReceived(ControllerCommandPtr command) {
|
||||
MutexLocker lock(m_lock);
|
||||
m_commandQueue.push_back(command);
|
||||
m_active = true;
|
||||
m_condition.broadcast();
|
||||
}
|
||||
|
||||
ControllerCommand::ControllerCommand(weak_ptr<UniverseController> controller, VariantList request) {
|
||||
m_controller = controller;
|
||||
m_request = request;
|
||||
m_command = request[0].toString();
|
||||
}
|
||||
|
||||
void ControllerCommand::popOuterCommand(int innerIndex)
|
||||
{
|
||||
m_request = m_request[innerIndex].toList();
|
||||
m_command = m_request[0].toString();
|
||||
}
|
||||
|
||||
String const& ControllerCommand::command() {
|
||||
return m_command;
|
||||
}
|
||||
|
||||
VariantList& ControllerCommand::request() {
|
||||
return m_request;
|
||||
}
|
||||
|
||||
void ControllerCommand::respond(VariantList const& response) {
|
||||
if (auto controller = m_controller.lock())
|
||||
controller->receiveResponse(response);
|
||||
}
|
||||
|
||||
}
|
88
attic/old_source/StarUniverseController.hpp
Normal file
88
attic/old_source/StarUniverseController.hpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#ifndef STAR_UNIVERSE_CONTROLLER_HPP
|
||||
#define STAR_UNIVERSE_CONTROLLER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "StarConfig.hpp"
|
||||
#include "StarUniverseServer.hpp"
|
||||
#include "StarTcp.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
using namespace std;
|
||||
|
||||
class UniverseControllerSet;
|
||||
|
||||
class UniverseController : public Thread, public enable_shared_from_this<UniverseController> {
|
||||
public:
|
||||
UniverseController(TcpSocketPtr socket);
|
||||
void stop();
|
||||
bool isClosed();
|
||||
|
||||
void bind(UniverseControllerSet* parent);
|
||||
|
||||
void receiveResponse(Variant const&response);
|
||||
|
||||
virtual void run();
|
||||
private:
|
||||
TcpSocketPtr m_socket;
|
||||
UniverseControllerSet* m_parent;
|
||||
bool m_stop;
|
||||
bool m_closed;
|
||||
|
||||
Mutex m_lock;
|
||||
ConditionVariable m_condition;
|
||||
|
||||
List<Variant> m_responses;
|
||||
|
||||
};
|
||||
typedef shared_ptr<UniverseController> UniverseControllerPtr;
|
||||
|
||||
class ControllerCommand;
|
||||
typedef shared_ptr<ControllerCommand> ControllerCommandPtr;
|
||||
|
||||
class UniverseControllerSet : public Thread {
|
||||
public:
|
||||
UniverseControllerSet(UniverseServer* universeServer);
|
||||
|
||||
void addNewConnection(UniverseControllerPtr controller);
|
||||
|
||||
void commandReceived(ControllerCommandPtr command);
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
void notify();
|
||||
|
||||
private:
|
||||
UniverseServer* m_universeServer;
|
||||
|
||||
bool m_stop;
|
||||
Mutex m_lock;
|
||||
bool m_active;
|
||||
ConditionVariable m_condition;
|
||||
Set<UniverseControllerPtr> m_controllers;
|
||||
List<ControllerCommandPtr> m_commandQueue;
|
||||
|
||||
void commandTypeHelpForMoreInfo(ControllerCommandPtr command);
|
||||
void commandCounters(ControllerCommandPtr command);
|
||||
void commandUnknown(ControllerCommandPtr command);
|
||||
|
||||
};
|
||||
typedef shared_ptr<UniverseControllerSet> UniverseControllerSetPtr;
|
||||
|
||||
class ControllerCommand {
|
||||
public:
|
||||
ControllerCommand(weak_ptr<UniverseController> controller, VariantList request);
|
||||
String const& command();
|
||||
VariantList& request();
|
||||
void respond(VariantList const& response);
|
||||
void popOuterCommand(int innerIndex);
|
||||
private:
|
||||
weak_ptr<UniverseController> m_controller;
|
||||
String m_command;
|
||||
VariantList m_request;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
816
attic/old_source/net_states_test.cpp
Normal file
816
attic/old_source/net_states_test.cpp
Normal file
|
@ -0,0 +1,816 @@
|
|||
#include "StarNetStates.hpp"
|
||||
#include "StarNetStepElements.hpp"
|
||||
#include "StarDataStreamExtra.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(NetSyncStates, Values) {
|
||||
enum class Test {
|
||||
Value1,
|
||||
Value2,
|
||||
Value3
|
||||
};
|
||||
|
||||
NetStatesEnum<Test> masterField1(Test::Value2);
|
||||
NetSyncStates master(masterField1);
|
||||
|
||||
EXPECT_EQ(masterField1.get(), Test::Value2);
|
||||
masterField1.set(Test::Value3);
|
||||
EXPECT_EQ(masterField1.get(), Test::Value3);
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, DataRounding) {
|
||||
auto masterField1 = NetStatesUInt::makeUInt8();
|
||||
auto masterField2 = NetStatesUInt::makeUInt16();
|
||||
auto masterField3 = NetStatesFloat::makeNormalizedFloat8();
|
||||
auto masterField4 = NetStatesFloat::makeFixedPoint16(0.1);
|
||||
auto masterField5 = NetStatesFloat::makeFixedPoint16(0.5);
|
||||
auto masterField6 = NetStatesFloat::makeNormalizedFloat16();
|
||||
auto masterField7 = NetStatesFloat::makeFixedPoint8(0.1);
|
||||
NetSyncStates master(masterField1, masterField2, masterField3, masterField4, masterField5, masterField6, masterField7);
|
||||
|
||||
masterField1.set(300);
|
||||
masterField2.set(100000);
|
||||
masterField3.set(1.5);
|
||||
masterField4.set(2.1999);
|
||||
masterField5.set(100000);
|
||||
masterField6.set(0.777777);
|
||||
masterField7.set(0.999);
|
||||
|
||||
// Check the master side value bounds limits
|
||||
|
||||
EXPECT_EQ(masterField1.get(), 255u);
|
||||
EXPECT_EQ(masterField2.get(), 65535u);
|
||||
EXPECT_LT(fabs(masterField3.get() - 1.0), 0.00001);
|
||||
EXPECT_LT(fabs(masterField5.get() - 16383.5), 0.00001);
|
||||
|
||||
// Check to make sure encoded data is actually sent with the expected
|
||||
// limitations to the client side.
|
||||
|
||||
auto slaveField1 = NetStatesUInt::makeUInt8();
|
||||
auto slaveField2 = NetStatesUInt::makeUInt16();
|
||||
auto slaveField3 = NetStatesFloat::makeNormalizedFloat8();
|
||||
auto slaveField4 = NetStatesFloat::makeFixedPoint16(0.1);
|
||||
auto slaveField5 = NetStatesFloat::makeFixedPoint16(0.5);
|
||||
auto slaveField6 = NetStatesFloat::makeNormalizedFloat16();
|
||||
auto slaveField7 = NetStatesFloat::makeFixedPoint8(0.1);
|
||||
NetSyncStates slave(slaveField1, slaveField2, slaveField3, slaveField4, slaveField5, slaveField6, slaveField7);
|
||||
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 255u);
|
||||
EXPECT_EQ(slaveField2.get(), 65535u);
|
||||
EXPECT_LT(fabs(slaveField3.get() - 1.0), 0.00001);
|
||||
EXPECT_LT(fabs(slaveField4.get() - 2.2), 0.00001);
|
||||
EXPECT_LT(fabs(slaveField5.get() - 16383.5), 0.00001);
|
||||
EXPECT_LT(fabs(slaveField6.get() - 0.777782), 0.000001);
|
||||
EXPECT_LT(fabs(slaveField7.get() - 1.0), 0.000001);
|
||||
|
||||
// Make sure that jittering a fixed point or limited value doesn't cause
|
||||
// extra deltas
|
||||
|
||||
masterField1.set(256);
|
||||
masterField2.set(65536);
|
||||
masterField4.set(2.155);
|
||||
masterField4.set(2.24);
|
||||
|
||||
EXPECT_FALSE(master.hasDelta());
|
||||
}
|
||||
|
||||
TEST(NetStepStates, DirectWriteRead) {
|
||||
auto masterField1 = NetStatesUInt::makeVarUInt(1);
|
||||
auto masterField2 = NetStatesUInt::makeVarUInt(2);
|
||||
auto masterField3 = NetStatesUInt::makeVarUInt(3);
|
||||
auto masterField4 = NetStatesUInt::makeVarUInt(4);
|
||||
NetStepStates master(masterField1, masterField2, masterField3, masterField4);
|
||||
|
||||
auto slaveField1 = NetStatesUInt::makeVarUInt();
|
||||
auto slaveField2 = NetStatesUInt::makeVarUInt();
|
||||
auto slaveField3 = NetStatesUInt::makeVarUInt();
|
||||
auto slaveField4 = NetStatesUInt::makeVarUInt();
|
||||
NetStepStates slave(slaveField1, slaveField2, slaveField3, slaveField4);
|
||||
|
||||
DataStreamBuffer ds;
|
||||
master.writeFull(ds);
|
||||
ds.seek(0);
|
||||
slave.readFull(ds);
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 1u);
|
||||
EXPECT_EQ(slaveField2.get(), 2u);
|
||||
EXPECT_EQ(slaveField3.get(), 3u);
|
||||
EXPECT_EQ(slaveField4.get(), 4u);
|
||||
|
||||
master.updateStep(1);
|
||||
masterField1.set(10);
|
||||
masterField3.set(30);
|
||||
|
||||
ds.clear();
|
||||
master.writeDelta(ds, 1);
|
||||
ds.seek(0);
|
||||
slave.readDelta(ds, 1);
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 10u);
|
||||
EXPECT_EQ(slaveField2.get(), 2u);
|
||||
EXPECT_EQ(slaveField3.get(), 30u);
|
||||
EXPECT_EQ(slaveField4.get(), 4u);
|
||||
|
||||
master.updateStep(2);
|
||||
masterField2.set(20);
|
||||
masterField4.set(40);
|
||||
|
||||
ds.clear();
|
||||
master.writeDelta(ds, 2);
|
||||
ds.seek(0);
|
||||
slave.readDelta(ds, 2);
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 10u);
|
||||
EXPECT_EQ(slaveField2.get(), 20u);
|
||||
EXPECT_EQ(slaveField3.get(), 30u);
|
||||
EXPECT_EQ(slaveField4.get(), 40u);
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, MasterSlave) {
|
||||
auto masterField1 = NetStatesInt::makeInt8();
|
||||
auto masterField2 = NetStatesUInt::makeUInt16();
|
||||
auto masterField3 = NetStatesUInt::makeVarUInt();
|
||||
auto masterField4 = NetStatesSize();
|
||||
NetSyncStates master(masterField1, masterField2, masterField3, masterField4);
|
||||
|
||||
auto slaveField1 = NetStatesInt::makeInt8();
|
||||
auto slaveField2 = NetStatesUInt::makeUInt16();
|
||||
auto slaveField3 = NetStatesUInt::makeVarUInt();
|
||||
auto slaveField4 = NetStatesSize();
|
||||
NetSyncStates slave(slaveField1, slaveField2, slaveField3, slaveField4);
|
||||
|
||||
masterField1.set(10);
|
||||
masterField2.set(20);
|
||||
masterField3.set(30);
|
||||
masterField4.set(40);
|
||||
|
||||
EXPECT_EQ(masterField1.get(), 10);
|
||||
EXPECT_EQ(masterField2.get(), 20u);
|
||||
EXPECT_EQ(masterField3.get(), 30u);
|
||||
EXPECT_EQ(masterField4.get(), 40u);
|
||||
|
||||
auto delta = master.writeDeltaPacket();
|
||||
|
||||
// Initial delta should be at least 9 bytes, from the 1 2-byte value, 3 >= 1
|
||||
// byte value and indexes.
|
||||
EXPECT_GE(delta.size(), 9u);
|
||||
|
||||
slave.readDeltaPacket(delta);
|
||||
EXPECT_EQ(slaveField1.get(), 10);
|
||||
EXPECT_EQ(slaveField2.get(), 20u);
|
||||
EXPECT_EQ(slaveField3.get(), 30u);
|
||||
EXPECT_EQ(slaveField4.get(), 40u);
|
||||
|
||||
masterField1.set(50);
|
||||
delta = master.writeDeltaPacket();
|
||||
|
||||
// Second delta should be not include any other data than the single 1 byte
|
||||
// changed state, so make sure that it is <= 2 bytes.
|
||||
EXPECT_LE(delta.size(), 2u);
|
||||
|
||||
slave.readDeltaPacket(delta);
|
||||
EXPECT_EQ(slaveField1.get(), 50);
|
||||
|
||||
master.reset();
|
||||
delta = master.writeDeltaPacket();
|
||||
|
||||
// Delta after calling resetStep should contain all values.
|
||||
EXPECT_GE(delta.size(), 9u);
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, CustomData) {
|
||||
NetStatesData<Vec2F> masterField1;
|
||||
NetSyncStates master(masterField1);
|
||||
|
||||
NetStatesData<Vec2F> slaveField1;
|
||||
NetSyncStates slave(slaveField1);
|
||||
|
||||
masterField1.set(Vec2F(1, 2));
|
||||
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
EXPECT_TRUE(slaveField1.get() == Vec2F(1, 2));
|
||||
|
||||
masterField1.update([](Vec2F& v) {
|
||||
v = {3.0f, 4.0f};
|
||||
return true;
|
||||
});
|
||||
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
EXPECT_TRUE(slaveField1.get() == Vec2F(3, 4));
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, Forwarding) {
|
||||
auto masterField = NetStatesInt::makeInt16();
|
||||
NetSyncStates master(masterField);
|
||||
|
||||
auto forwarderField = NetStatesInt::makeInt16();
|
||||
NetSyncStates forwarder(forwarderField);
|
||||
|
||||
auto slaveField = NetStatesInt::makeInt16();
|
||||
NetSyncStates slave(slaveField);
|
||||
|
||||
masterField.set(413);
|
||||
forwarder.readDeltaPacket(master.writeDeltaPacket());
|
||||
slave.readDeltaPacket(forwarder.writeDeltaPacket());
|
||||
|
||||
EXPECT_EQ(forwarderField.get(), 413);
|
||||
EXPECT_EQ(slaveField.get(), 413);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, Forwarding) {
|
||||
auto masterField1 = NetStatesInt::makeInt16();
|
||||
auto masterField2 = NetStatesInt::makeInt16();
|
||||
auto masterField3 = NetStatesInt::makeInt16();
|
||||
auto masterField4 = NetStatesInt::makeInt16();
|
||||
NetStepStates master(masterField1, masterField2, masterField3, masterField4);
|
||||
|
||||
auto forwarderField1 = NetStatesInt::makeInt16();
|
||||
auto forwarderField2 = NetStatesInt::makeInt16();
|
||||
auto forwarderField3 = NetStatesInt::makeInt16();
|
||||
auto forwarderField4 = NetStatesInt::makeInt16();
|
||||
NetStepStates forwarder(forwarderField1, forwarderField2, forwarderField3, forwarderField4);
|
||||
|
||||
auto slaveField1 = NetStatesInt::makeInt16();
|
||||
auto slaveField2 = NetStatesInt::makeInt16();
|
||||
auto slaveField3 = NetStatesInt::makeInt16();
|
||||
auto slaveField4 = NetStatesInt::makeInt16();
|
||||
NetStepStates slave(slaveField1, slaveField2, slaveField3, slaveField4);
|
||||
|
||||
// First emulate store / load that would happen in entity initialization.
|
||||
|
||||
masterField1.set(10);
|
||||
masterField2.set(20);
|
||||
masterField3.set(30);
|
||||
masterField4.set(40);
|
||||
|
||||
forwarder.readFullPacket(master.writeFullPacket());
|
||||
slave.readFullPacket(forwarder.writeFullPacket());
|
||||
|
||||
EXPECT_EQ(forwarderField1.get(), 10);
|
||||
EXPECT_EQ(forwarderField2.get(), 20);
|
||||
EXPECT_EQ(forwarderField3.get(), 30);
|
||||
EXPECT_EQ(forwarderField4.get(), 40);
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 10);
|
||||
EXPECT_EQ(slaveField2.get(), 20);
|
||||
EXPECT_EQ(slaveField3.get(), 30);
|
||||
EXPECT_EQ(slaveField4.get(), 40);
|
||||
|
||||
// Then, update one step and transmit that step to the forwarder.
|
||||
|
||||
master.updateStep(1);
|
||||
masterField1.set(413);
|
||||
|
||||
// Write delta since and including step 1 (the update to "test1")
|
||||
ByteArray delta = master.writeDeltaPacket(1);
|
||||
// Make sure that the delta includes only one changed state.
|
||||
EXPECT_LT(delta.size(), 5u);
|
||||
|
||||
forwarder.updateStep(1);
|
||||
EXPECT_TRUE(forwarder.readDeltaPacket(delta));
|
||||
|
||||
EXPECT_EQ(forwarderField1.get(), 413);
|
||||
|
||||
// Write forwarded delta since and including step 1.
|
||||
delta = forwarder.writeDeltaPacket(1);
|
||||
// Make sure that the delta includes only one changed state.
|
||||
EXPECT_LT(delta.size(), 5u);
|
||||
|
||||
// Make sure that forwarded state makes it all the way to the slave.
|
||||
slave.updateStep(1);
|
||||
EXPECT_TRUE(slave.readDeltaPacket(delta));
|
||||
EXPECT_EQ(slaveField1.get(), 413);
|
||||
|
||||
EXPECT_EQ(forwarderField1.get(), 413);
|
||||
EXPECT_EQ(forwarderField2.get(), 20);
|
||||
EXPECT_EQ(forwarderField3.get(), 30);
|
||||
EXPECT_EQ(forwarderField4.get(), 40);
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 413);
|
||||
EXPECT_EQ(slaveField2.get(), 20);
|
||||
EXPECT_EQ(slaveField3.get(), 30);
|
||||
EXPECT_EQ(slaveField4.get(), 40);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, MasterSetGet) {
|
||||
// Make sure that Master mode sets, gets, pullStateUpdated, and pullEventOccurred
|
||||
// work properly.
|
||||
|
||||
auto masterField1 = NetStatesInt::makeInt16();
|
||||
auto masterField2 = NetStatesString();
|
||||
auto masterField3 = NetStatesEvent();
|
||||
NetStepStates master(masterField1, masterField2, masterField3);
|
||||
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
|
||||
masterField1.set(10);
|
||||
masterField2.set("foo");
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), true);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), true);
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), false);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), false);
|
||||
|
||||
masterField3.trigger();
|
||||
masterField3.trigger();
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 2u);
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, IsolatedSetGet) {
|
||||
auto masterField1 = NetStatesInt::makeInt16();
|
||||
auto masterField2 = NetStatesString();
|
||||
auto masterField3 = NetStatesEvent();
|
||||
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
|
||||
masterField1.set(10);
|
||||
masterField2.set("foo");
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), true);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), true);
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), false);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), false);
|
||||
|
||||
masterField3.trigger();
|
||||
masterField3.trigger();
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 2u);
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
|
||||
masterField1.set(20);
|
||||
masterField2.set("bar");
|
||||
|
||||
masterField3.trigger();
|
||||
masterField3.trigger();
|
||||
|
||||
NetStepStates master(masterField1, masterField2, masterField3);
|
||||
|
||||
EXPECT_EQ(masterField1.get(), 20);
|
||||
EXPECT_EQ(masterField2.get(), "bar");
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), true);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), true);
|
||||
|
||||
EXPECT_EQ(masterField1.pullUpdated(), false);
|
||||
EXPECT_EQ(masterField2.pullUpdated(), false);
|
||||
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 2u);
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, EventTest) {
|
||||
auto masterField1 = NetStatesInt::makeVarInt();
|
||||
auto masterField2 = NetStatesEvent();
|
||||
NetStepStates master(masterField1, masterField2);
|
||||
|
||||
auto slaveField1 = NetStatesInt::makeVarInt();
|
||||
auto slaveField2 = NetStatesEvent();
|
||||
NetStepStates slave(slaveField1, slaveField2);
|
||||
|
||||
// Fields should always start with pullUpdatd returning true, events should
|
||||
// always start with occurred false.
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 0u);
|
||||
|
||||
masterField2.trigger();
|
||||
EXPECT_TRUE(slave.readDeltaPacket(master.writeDeltaPacket(0)));
|
||||
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 1u);
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 0u);
|
||||
|
||||
EXPECT_FALSE(master.updateStep(1));
|
||||
// Delta should be empty, nothing happened on step 1.
|
||||
EXPECT_EQ(master.hasDelta(1), false);
|
||||
auto delta = master.writeDeltaPacket(1);
|
||||
EXPECT_TRUE(delta.empty());
|
||||
|
||||
EXPECT_FALSE(slave.updateStep(1));
|
||||
EXPECT_FALSE(slave.readDeltaPacket(delta));
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 0u);
|
||||
|
||||
EXPECT_FALSE(master.updateStep(2));
|
||||
masterField2.trigger();
|
||||
EXPECT_EQ(master.hasDelta(2), true);
|
||||
delta = master.writeDeltaPacket(2);
|
||||
|
||||
EXPECT_FALSE(slave.updateStep(2));
|
||||
EXPECT_TRUE(slave.readDeltaPacket(delta));
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 1u);
|
||||
EXPECT_EQ(slaveField2.pullOccurrences(), 0u);
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, Resetting) {
|
||||
auto masterField1 = NetStatesString("foo");
|
||||
auto masterField2 = NetStatesInt::makeVarInt();
|
||||
auto masterField3 = NetStatesEvent();
|
||||
NetSyncStates master(masterField1, masterField2, masterField3);
|
||||
|
||||
auto slaveField1 = NetStatesString();
|
||||
auto slaveField2 = NetStatesInt::makeVarInt();
|
||||
auto slaveField3 = NetStatesEvent();
|
||||
NetSyncStates slave(slaveField1, slaveField2, slaveField3);
|
||||
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), true);
|
||||
|
||||
masterField3.trigger();
|
||||
|
||||
master.reset();
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField1.get(), "foo");
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), false);
|
||||
|
||||
// We have triggered an event and called stepReset, the event should still
|
||||
// come through
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 1u);
|
||||
EXPECT_EQ(slaveField3.pullOccurrences(), 1u);
|
||||
|
||||
masterField1.set("foo");
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), false);
|
||||
|
||||
masterField1.set("bar");
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField1.get(), "bar");
|
||||
|
||||
masterField1.push("bar");
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
EXPECT_EQ(slaveField1.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField1.get(), "bar");
|
||||
|
||||
masterField3.trigger();
|
||||
masterField3.trigger();
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
slaveField3.ignoreOccurrences();
|
||||
// occurrence should not come through after "ignoreOccurrences"
|
||||
EXPECT_EQ(slaveField3.pullOccurrences(), 0u);
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 2u);
|
||||
|
||||
masterField3.trigger();
|
||||
masterField3.trigger();
|
||||
masterField3.ignoreOccurrences();
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
|
||||
// ignoreOccurrences is LOCAL only, so events should still go through to the
|
||||
// slave
|
||||
EXPECT_EQ(masterField3.pullOccurrences(), 0u);
|
||||
|
||||
EXPECT_EQ(slaveField3.pullOccurrences(), 2u);
|
||||
EXPECT_EQ(slaveField3.pullOccurrences(), 0u);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, Interpolation) {
|
||||
auto masterField1 = NetStatesFloat::makeFloat(1.0f);
|
||||
auto masterField2 = NetStatesBool(true);
|
||||
NetStepStates master(masterField1, masterField2);
|
||||
|
||||
auto slaveField1 = NetStatesFloat::makeFloat();
|
||||
slaveField1.setInterpolator(NetStatesLerpInterpolator);
|
||||
auto slaveField2 = NetStatesBool();
|
||||
NetStepStates slave(slaveField1, slaveField2);
|
||||
|
||||
slave.enableInterpolation(0);
|
||||
EXPECT_TRUE(slave.readDeltaPacket(master.writeDeltaPacket(0), 0.0));
|
||||
|
||||
EXPECT_FALSE(master.updateStep(2));
|
||||
masterField1.set(2.0f);
|
||||
masterField2.set(false);
|
||||
EXPECT_FALSE(slave.readDeltaPacket(master.writeDeltaPacket(1), 2.0));
|
||||
|
||||
EXPECT_FALSE(master.updateStep(4));
|
||||
masterField1.set(3.0f);
|
||||
masterField2.set(true);
|
||||
EXPECT_FALSE(slave.readDeltaPacket(master.writeDeltaPacket(3), 4.0));
|
||||
|
||||
EXPECT_FALSE(master.updateStep(6));
|
||||
masterField1.set(4.0f);
|
||||
masterField2.set(false);
|
||||
EXPECT_FALSE(slave.readDeltaPacket(master.writeDeltaPacket(5), 6.0));
|
||||
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 1.0) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(1));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 1.5) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), false);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(2));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 2.0) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), false);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), true);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(3));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 2.5) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), false);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), false);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(4));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 3.0) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(5));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 3.5) < 0.001);
|
||||
EXPECT_EQ(slaveField2.get(), true);
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), false);
|
||||
|
||||
EXPECT_TRUE(slave.updateStep(6));
|
||||
EXPECT_TRUE(fabs(slaveField1.get() - 4.0) < 0.001);
|
||||
EXPECT_FALSE(slaveField2.get());
|
||||
EXPECT_EQ(slaveField2.pullUpdated(), true);
|
||||
EXPECT_EQ(slaveField2.get(), false);
|
||||
|
||||
EXPECT_FALSE(slave.updateStep(7));
|
||||
EXPECT_FALSE(slave.updateStep(8));
|
||||
}
|
||||
|
||||
TEST(NetStepStates, NetStepElements) {
|
||||
class TestElement : public NetStepElement {
|
||||
public:
|
||||
TestElement(int value = 0)
|
||||
: dataState(NetStatesInt::makeVarInt(value)), netStates(dataState) {}
|
||||
|
||||
void setData(int value) {
|
||||
dataState.set(value);
|
||||
}
|
||||
|
||||
int getData() const {
|
||||
return dataState.get();
|
||||
}
|
||||
|
||||
ByteArray netStore() override {
|
||||
return netStates.writeFullPacket();
|
||||
}
|
||||
|
||||
void netLoad(ByteArray const& store) override {
|
||||
netStates.readFullPacket(store);
|
||||
}
|
||||
|
||||
void updateNetStep(uint64_t step) override {
|
||||
netStates.updateStep(step);
|
||||
}
|
||||
|
||||
void resetNetStep() override {
|
||||
netStates.resetStep();
|
||||
}
|
||||
|
||||
ByteArray writeNetDelta(uint64_t fromStep) override {
|
||||
return netStates.writeDeltaPacket(fromStep);
|
||||
}
|
||||
|
||||
void readNetDelta(ByteArray const& data, double interpolationStep = 0.0) override {
|
||||
netStates.readDeltaPacket(data, interpolationStep);
|
||||
}
|
||||
|
||||
void enableNetInterpolation(unsigned extrapolationHint) override {
|
||||
netStates.enableInterpolation(extrapolationHint);
|
||||
}
|
||||
|
||||
void disableNetInterpolation() override {
|
||||
netStates.disableInterpolation();
|
||||
}
|
||||
|
||||
void netInterpolationHeartbeat(double step) override {
|
||||
netStates.interpolationHeartbeat(step);
|
||||
}
|
||||
|
||||
private:
|
||||
NetStatesInt dataState;
|
||||
NetStepStates netStates;
|
||||
};
|
||||
|
||||
typedef NetStepDynamicElementGroup<TestElement> TestElementGroup;
|
||||
|
||||
TestElementGroup masterGroup;
|
||||
TestElementGroup slaveGroup;
|
||||
|
||||
auto objId1 = masterGroup.addElement(make_shared<TestElement>(1000));
|
||||
auto objId2 = masterGroup.addElement(make_shared<TestElement>(2000));
|
||||
|
||||
EXPECT_EQ(masterGroup.elementIds().size(), 2u);
|
||||
EXPECT_EQ(masterGroup.getElement(objId1)->getData(), 1000);
|
||||
EXPECT_EQ(masterGroup.getElement(objId2)->getData(), 2000);
|
||||
|
||||
slaveGroup.netLoad(masterGroup.netStore());
|
||||
|
||||
EXPECT_EQ(slaveGroup.elementIds().size(), 2u);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId1)->getData(), 1000);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId2)->getData(), 2000);
|
||||
|
||||
masterGroup.updateNetStep(2);
|
||||
masterGroup.getElement(objId1)->setData(1001);
|
||||
masterGroup.getElement(objId2)->setData(2001);
|
||||
|
||||
slaveGroup.updateNetStep(2);
|
||||
slaveGroup.readNetDelta(masterGroup.writeNetDelta(1), 2);
|
||||
|
||||
EXPECT_EQ(slaveGroup.getElement(objId1)->getData(), 1001);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId2)->getData(), 2001);
|
||||
|
||||
masterGroup.updateNetStep(4);
|
||||
masterGroup.getElement(objId1)->setData(1002);
|
||||
masterGroup.getElement(objId2)->setData(2002);
|
||||
|
||||
slaveGroup.updateNetStep(4);
|
||||
slaveGroup.readNetDelta(masterGroup.writeNetDelta(3), 4);
|
||||
|
||||
EXPECT_EQ(masterGroup.getElement(objId1)->getData(), 1002);
|
||||
EXPECT_EQ(masterGroup.getElement(objId2)->getData(), 2002);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId1)->getData(), 1002);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId2)->getData(), 2002);
|
||||
|
||||
masterGroup.updateNetStep(6);
|
||||
auto objId3 = masterGroup.addElement(make_shared<TestElement>(3001));
|
||||
|
||||
slaveGroup.updateNetStep(6);
|
||||
slaveGroup.readNetDelta(masterGroup.writeNetDelta(5));
|
||||
|
||||
EXPECT_EQ(slaveGroup.elementIds().size(), 3u);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId3)->getData(), 3001);
|
||||
|
||||
// Delta must be greater than MaxChangeDataSteps
|
||||
masterGroup.updateNetStep(2006);
|
||||
auto objId4 = masterGroup.addElement(make_shared<TestElement>(4001));
|
||||
masterGroup.getElement(objId3)->setData(3002);
|
||||
masterGroup.getElement(objId4)->setData(4002);
|
||||
|
||||
slaveGroup.updateNetStep(2006);
|
||||
slaveGroup.readNetDelta(masterGroup.writeNetDelta(7));
|
||||
|
||||
EXPECT_EQ(slaveGroup.elementIds().size(), 4u);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId1)->getData(), 1002);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId2)->getData(), 2002);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId3)->getData(), 3002);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId4)->getData(), 4002);
|
||||
|
||||
TestElementGroup forwardedSlaveGroup;
|
||||
forwardedSlaveGroup.readNetDelta(slaveGroup.writeNetDelta(0));
|
||||
forwardedSlaveGroup.updateNetStep(2006);
|
||||
|
||||
EXPECT_EQ(forwardedSlaveGroup.elementIds().size(), 4u);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId1)->getData(), 1002);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId2)->getData(), 2002);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId3)->getData(), 3002);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId4)->getData(), 4002);
|
||||
|
||||
masterGroup.updateNetStep(2008);
|
||||
masterGroup.removeElement(objId1);
|
||||
masterGroup.removeElement(objId3);
|
||||
masterGroup.getElement(objId2)->setData(2003);
|
||||
masterGroup.getElement(objId4)->setData(4003);
|
||||
|
||||
slaveGroup.updateNetStep(2008);
|
||||
slaveGroup.readNetDelta(masterGroup.writeNetDelta(2007));
|
||||
|
||||
forwardedSlaveGroup.updateNetStep(2008);
|
||||
forwardedSlaveGroup.readNetDelta(slaveGroup.writeNetDelta(2007));
|
||||
|
||||
EXPECT_EQ(slaveGroup.elementIds().size(), 2u);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId2)->getData(), 2003);
|
||||
EXPECT_EQ(slaveGroup.getElement(objId4)->getData(), 4003);
|
||||
|
||||
EXPECT_EQ(forwardedSlaveGroup.elementIds().size(), 2u);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId2)->getData(), 2003);
|
||||
EXPECT_EQ(forwardedSlaveGroup.getElement(objId4)->getData(), 4003);
|
||||
}
|
||||
|
||||
TEST(NetSyncStates, AllTypes) {
|
||||
auto masterField1 = NetStatesInt::makeInt8(9);
|
||||
auto masterField2 = NetStatesUInt::makeUInt8(7);
|
||||
auto masterField3 = NetStatesInt::makeInt16(333);
|
||||
auto masterField4 = NetStatesUInt::makeUInt16(999);
|
||||
auto masterField5 = NetStatesInt::makeVarInt(567);
|
||||
auto masterField6 = NetStatesUInt::makeVarUInt(17000);
|
||||
auto masterField7 = NetStatesSize(22222);
|
||||
auto masterField8 = NetStatesFloat::makeFloat(1.55f);
|
||||
auto masterField9 = NetStatesFloat::makeDouble(1.12345678910111213);
|
||||
auto masterField10 = NetStatesFloat::makeNormalizedFloat8(0.33333333f);
|
||||
auto masterField11 = NetStatesFloat::makeNormalizedFloat16(0.66666666f);
|
||||
auto masterField12 = NetStatesFloat::makeFixedPoint8(0.1, 1.6);
|
||||
auto masterField13 = NetStatesFloat::makeFixedPoint16(0.01, 1.62);
|
||||
auto masterField14 = NetStatesFloat::makeFixedPoint(0.01, 2000.62);
|
||||
auto masterField15 = NetStatesBool(true);
|
||||
NetSyncStates master(
|
||||
masterField1, masterField2, masterField3, masterField4, masterField5, masterField6, masterField7,
|
||||
masterField8, masterField9, masterField10, masterField11, masterField12, masterField13, masterField14,
|
||||
masterField15);
|
||||
// Events and generics are tested elsewhere
|
||||
|
||||
EXPECT_EQ(masterField1.get(), 9);
|
||||
EXPECT_EQ(masterField2.get(), 7u);
|
||||
EXPECT_EQ(masterField3.get(), 333);
|
||||
EXPECT_EQ(masterField4.get(), 999u);
|
||||
EXPECT_EQ(masterField5.get(), 567);
|
||||
EXPECT_EQ(masterField6.get(), 17000u);
|
||||
EXPECT_EQ(masterField7.get(), 22222u);
|
||||
EXPECT_FLOAT_EQ(masterField8.get(), 1.55f);
|
||||
EXPECT_FLOAT_EQ(masterField9.get(), 1.12345678910111213);
|
||||
EXPECT_FLOAT_EQ(masterField10.get(), 0.33333333f);
|
||||
EXPECT_FLOAT_EQ(masterField11.get(), 0.66666666f);
|
||||
EXPECT_FLOAT_EQ(masterField12.get(), 1.6);
|
||||
EXPECT_FLOAT_EQ(masterField13.get(), 1.62);
|
||||
EXPECT_FLOAT_EQ(masterField14.get(), 2000.62);
|
||||
EXPECT_EQ(masterField15.get(), true);
|
||||
|
||||
auto slaveField1 = NetStatesInt::makeInt8(9);
|
||||
auto slaveField2 = NetStatesUInt::makeUInt8(7);
|
||||
auto slaveField3 = NetStatesInt::makeInt16(333);
|
||||
auto slaveField4 = NetStatesUInt::makeUInt16(999);
|
||||
auto slaveField5 = NetStatesInt::makeVarInt(567);
|
||||
auto slaveField6 = NetStatesUInt::makeVarUInt(17000);
|
||||
auto slaveField7 = NetStatesSize(22222);
|
||||
auto slaveField8 = NetStatesFloat::makeFloat(1.55f);
|
||||
auto slaveField9 = NetStatesFloat::makeDouble(1.12345678910111213);
|
||||
auto slaveField10 = NetStatesFloat::makeNormalizedFloat8(0.33333333f);
|
||||
auto slaveField11 = NetStatesFloat::makeNormalizedFloat16(0.66666666f);
|
||||
auto slaveField12 = NetStatesFloat::makeFixedPoint8(0.1, 1.6);
|
||||
auto slaveField13 = NetStatesFloat::makeFixedPoint16(0.01, 1.62);
|
||||
auto slaveField14 = NetStatesFloat::makeFixedPoint(0.01, 2000.62);
|
||||
auto slaveField15 = NetStatesBool(true);
|
||||
NetSyncStates slave(
|
||||
slaveField1, slaveField2, slaveField3, slaveField4, slaveField5, slaveField6, slaveField7,
|
||||
slaveField8, slaveField9, slaveField10, slaveField11, slaveField12, slaveField13, slaveField14,
|
||||
slaveField15);
|
||||
|
||||
slave.readDeltaPacket(master.writeDeltaPacket());
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 9);
|
||||
EXPECT_EQ(slaveField2.get(), 7u);
|
||||
EXPECT_EQ(slaveField3.get(), 333);
|
||||
EXPECT_EQ(slaveField4.get(), 999u);
|
||||
EXPECT_EQ(slaveField5.get(), 567);
|
||||
EXPECT_EQ(slaveField6.get(), 17000u);
|
||||
EXPECT_EQ(slaveField7.get(), 22222u);
|
||||
EXPECT_FLOAT_EQ(slaveField8.get(), 1.55f);
|
||||
EXPECT_FLOAT_EQ(slaveField9.get(), 1.12345678910111213);
|
||||
EXPECT_NEAR(slaveField10.get(), 0.33333333f, 0.00001f);
|
||||
EXPECT_NEAR(slaveField11.get(), 0.66666666f, 0.00001f);
|
||||
EXPECT_FLOAT_EQ(slaveField12.get(), 1.6);
|
||||
EXPECT_FLOAT_EQ(slaveField13.get(), 1.62);
|
||||
EXPECT_FLOAT_EQ(slaveField14.get(), 2000.62);
|
||||
EXPECT_EQ(slaveField15.get(), true);
|
||||
}
|
||||
|
||||
TEST(NetStepStates, FieldConnectedSemantics) {
|
||||
NetStatesString field("foo");
|
||||
EXPECT_FALSE(field.connected());
|
||||
EXPECT_EQ(field.get(), "foo");
|
||||
NetStepStates netStates;
|
||||
netStates.addField(field);
|
||||
EXPECT_TRUE(field.connected());
|
||||
EXPECT_EQ(field.get(), "foo");
|
||||
netStates.clearFields();
|
||||
EXPECT_FALSE(field.connected());
|
||||
EXPECT_EQ(field.get(), "foo");
|
||||
}
|
||||
|
||||
TEST(NetStepStates, HashMatching) {
|
||||
auto masterField1 = NetStatesUInt::makeUInt8(5);
|
||||
auto masterField2 = NetStatesUInt::makeUInt16(10);
|
||||
auto masterField3 = NetStatesFloat::makeNormalizedFloat8();
|
||||
auto masterField4 = NetStatesFloat::makeFixedPoint16(0.1);
|
||||
auto masterField5 = NetStatesFloat::makeFixedPoint16(0.5);
|
||||
auto masterField6 = NetStatesFloat::makeNormalizedFloat16();
|
||||
NetStepStates master(masterField1, masterField2, masterField3, masterField4, masterField5, masterField6);
|
||||
|
||||
NetStepStates slave;
|
||||
EXPECT_NE(master.fieldDigest(), slave.fieldDigest());
|
||||
|
||||
auto slaveField1 = NetStatesUInt::makeUInt8(5);
|
||||
auto slaveField2 = NetStatesUInt::makeUInt16(10);
|
||||
auto slaveField3 = NetStatesFloat::makeNormalizedFloat8();
|
||||
auto slaveField4 = NetStatesFloat::makeFixedPoint16(0.1);
|
||||
auto slaveField5 = NetStatesFloat::makeFixedPoint16(0.5);
|
||||
auto slaveField6 = NetStatesFloat::makeNormalizedFloat16();
|
||||
slave.addFields(slaveField1, slaveField2, slaveField3, slaveField4, slaveField5, slaveField6);
|
||||
EXPECT_EQ(master.fieldDigest(), slave.fieldDigest());
|
||||
|
||||
auto masterField7 = NetStatesFloat::makeFixedPoint8(0.1);
|
||||
master.addField(masterField7);
|
||||
EXPECT_NE(master.fieldDigest(), slave.fieldDigest());
|
||||
auto slaveField7 = NetStatesFloat::makeFixedPoint8(0.1);
|
||||
slave.addField(slaveField7);
|
||||
EXPECT_EQ(master.fieldDigest(), slave.fieldDigest());
|
||||
|
||||
slave.readFullPacket(master.writeFullPacket());
|
||||
|
||||
EXPECT_EQ(slaveField1.get(), 5u);
|
||||
EXPECT_EQ(slaveField2.get(), 10u);
|
||||
|
||||
slave.addField(NetStatesInt::makeInt8());
|
||||
EXPECT_THROW(slave.readFullPacket(master.writeFullPacket()), NetStatesException);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue