Redesign of animation queue for unreal, add get_tangibles_near to drivenengine
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -5,40 +5,26 @@
|
||||
// An animation queue is a fifo queue of animation steps. New animations are
|
||||
// pushed on the back, and old ones are popped from the front.
|
||||
//
|
||||
// An animation step has an "action" which is usually the name of an animation,
|
||||
// or it's a special token like "walk" or "warp." Each animation step shows the
|
||||
// resulting AnimState that the player finds himself in after executing the
|
||||
// action.
|
||||
// An animation step is a set of key-value pairs, where each key is an
|
||||
// identifier, and each value is either a number, a boolean, an XYZ coordinate,
|
||||
// or a string. A key-value pair can be either persistent or nonpersistent.
|
||||
// So a typical animation step might be:
|
||||
//
|
||||
// The first step in an animation queue always has id=0 and action="". This step
|
||||
// represents the initial state of the sprite before any animations or
|
||||
// movements.
|
||||
// action=walk [nonpersistent]
|
||||
// xyz=3,4,5 [persistent]
|
||||
// facing=320 [persistent]
|
||||
// plane=earth [persistent]
|
||||
//
|
||||
// To add new items to the AnimQueue, use this process: first, call add(id,
|
||||
// action). This adds a new step to the queue. Then, call set_xyz, set_facing,
|
||||
// set_plane, or an other setter. These setters are meant to only be used
|
||||
// immediately after calling 'add' to populate the new step.
|
||||
// Persistent values are retained from one animation step to the next,
|
||||
// nonpersistent values exist for one animation step only.
|
||||
//
|
||||
// VERSION NUMBERS
|
||||
// Animation steps are stored encoded as strings, which is convenient for
|
||||
// passing the data to unreal, but it means that the animation step has to be
|
||||
// decoded whenever you want access to the key-value pairs.
|
||||
//
|
||||
// The version number field: if the version number in the synchronous model is
|
||||
// equal to the version number in the master model, then the two animqueues are
|
||||
// guaranteed to be equal. Here's how we achieve that invariant:
|
||||
//
|
||||
// * In the master model, the version number starts at 1 and is auto-incremented
|
||||
// every time the animation queue is mutated.
|
||||
//
|
||||
// * In the synchronous model, the version number is set to zero every time the
|
||||
// animation queue is mutated. Note that master version numbers are never
|
||||
// zero. Applying a patch also sets the version number to zero.
|
||||
//
|
||||
// * Serializing and deserializing causes the version number to be saved and
|
||||
// restored in both master and synchronous models.
|
||||
//
|
||||
// * The routine 'update_version' should be called after difference
|
||||
// transmission. This copies the version number from the master to the
|
||||
// synchronous model. This is guaranteed to be safe because we just finished
|
||||
// difference transmission, therefore, the queues should match.
|
||||
// Each animation step has a hash value. The hash value is generated
|
||||
// by mixing the hash value of the previous step with the hash value
|
||||
// of the encoded string of key-value pairs.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -50,6 +36,7 @@
|
||||
#include "wrap-deque.hpp"
|
||||
#include "wrap-unordered-map.hpp"
|
||||
|
||||
#include "luastack.hpp"
|
||||
#include "streambuffer.hpp"
|
||||
#include "debugcollector.hpp"
|
||||
#include "util.hpp"
|
||||
@@ -57,184 +44,226 @@
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
|
||||
class AnimStep : public eng::nevernew {
|
||||
friend class AnimQueue;
|
||||
public:
|
||||
enum {
|
||||
HAS_FACING = 1,
|
||||
HAS_X = 2,
|
||||
HAS_Y = 4,
|
||||
HAS_Z = 8,
|
||||
HAS_XYZ = 14,
|
||||
HAS_GRAPHIC = 16,
|
||||
HAS_PLANE = 32,
|
||||
HAS_EVERYTHING = 63,
|
||||
};
|
||||
enum AnimValueType {
|
||||
T_STRING,
|
||||
T_NUMBER,
|
||||
T_BOOLEAN,
|
||||
T_XYZ,
|
||||
T_UNINITIALIZED
|
||||
};
|
||||
|
||||
AnimStep();
|
||||
~AnimStep();
|
||||
struct AnimValue {
|
||||
AnimValue() : persistent(false), type(T_UNINITIALIZED) {}
|
||||
|
||||
int64_t id() const { return id_; }
|
||||
int bits() const { return bits_; }
|
||||
bool persistent;
|
||||
AnimValueType type;
|
||||
eng::string str;
|
||||
util::DXYZ xyz;
|
||||
|
||||
const eng::string &action() const { return action_; }
|
||||
double facing() const { return facing_; }
|
||||
float x() const { return xyz_.x; }
|
||||
float y() const { return xyz_.y; }
|
||||
float z() const { return xyz_.z; }
|
||||
const util::XYZ &xyz() const { return xyz_; }
|
||||
const eng::string &graphic() const { return graphic_; }
|
||||
const eng::string &plane() const { return plane_; }
|
||||
// These set the type, str, and xyz fields in a consistent way.
|
||||
void set_boolean(bool b);
|
||||
void set_number(double n);
|
||||
void set_xyz(const util::DXYZ &xyz);
|
||||
void set_string(std::string_view s);
|
||||
|
||||
bool has_facing() const { return bits_ & HAS_FACING; }
|
||||
bool has_x() const { return bits_ & HAS_X; }
|
||||
bool has_y() const { return bits_ & HAS_Y; }
|
||||
bool has_z() const { return bits_ & HAS_Z; }
|
||||
bool has_xyz() const { return (bits_ & HAS_XYZ) == HAS_XYZ; }
|
||||
bool has_graphic() const { return bits_ & AnimStep::HAS_GRAPHIC; }
|
||||
bool has_plane() const { return bits_ & AnimStep::HAS_PLANE; }
|
||||
// The get the type, str, and xyz fields in a consistent way.
|
||||
bool get_boolean() const;
|
||||
double get_number() const;
|
||||
const util::DXYZ &get_xyz() const;
|
||||
std::string_view get_string() const;
|
||||
|
||||
void set_action(const eng::string &action);
|
||||
void set_facing(float f);
|
||||
void set_x(float f);
|
||||
void set_y(float f);
|
||||
void set_z(float z);
|
||||
void set_xyz(const util::XYZ &xyz);
|
||||
void set_graphic(const eng::string &g);
|
||||
void set_plane(const eng::string &p);
|
||||
|
||||
void clear();
|
||||
|
||||
// ExactlyEqual compares all fields.
|
||||
bool exactly_equal(const AnimStep &other) const;
|
||||
|
||||
// LogicallyEqual only compares fields whose HAS_XXX bits are set.
|
||||
bool logically_equal(const AnimStep &other) const;
|
||||
|
||||
// StateEqual is true if the plane, graphic, facing, and xyz match.
|
||||
bool state_equal(const AnimStep &other) const;
|
||||
|
||||
// read/write the step using a stream buffer.
|
||||
//
|
||||
void write_into(StreamBuffer *sb) const;
|
||||
void read_from(StreamBuffer *sb);
|
||||
|
||||
// Create an AnimStep from a lua table.
|
||||
//
|
||||
// Lua stack must contain a table, which may contain:
|
||||
// action: "action"
|
||||
// facing: 0.0 - 360.0
|
||||
// x: x-coordinate
|
||||
// y: y-coordinate
|
||||
// z: z-coordinate
|
||||
// graphic: "graphic"
|
||||
// plane: "plane"
|
||||
//
|
||||
// aqback: the animation queue back, from which relative
|
||||
// moves are computed.
|
||||
//
|
||||
void configure(LuaKeywordParser &kp, const AnimStep &aqback);
|
||||
|
||||
// Make this step into a first-step of an anim queue.
|
||||
void keep_state_only();
|
||||
|
||||
// For any values that are unchanged in this step,
|
||||
// echo the values of the previous step.
|
||||
void echo(const AnimStep &prev);
|
||||
|
||||
// Verify that this step echoes the previous step.
|
||||
bool echoes(const AnimStep &prev) const;
|
||||
|
||||
// Convert to a string for debugging purposes.
|
||||
eng::string debug_string() const;
|
||||
|
||||
// Convert a string to an animstep (for testing only).
|
||||
bool from_string(const eng::string &s);
|
||||
|
||||
private:
|
||||
int64_t id_;
|
||||
int16_t bits_;
|
||||
eng::string action_;
|
||||
|
||||
float facing_;
|
||||
util::XYZ xyz_;
|
||||
eng::string graphic_;
|
||||
eng::string plane_;
|
||||
|
||||
void config_store_string(lua_State *L, int idx, eng::string *target, int16_t bits, const char *name);
|
||||
void config_store_number(lua_State *L, int idx, float *target, float offset, int16_t bits, const char *name);
|
||||
// Copy the value from another animvalue. Don't copy the persistent flag.
|
||||
void copy_value(const AnimValue &other);
|
||||
};
|
||||
|
||||
|
||||
class AnimQueue : public eng::nevernew {
|
||||
class AnimState
|
||||
{
|
||||
private:
|
||||
using Map = eng::map<eng::string, AnimValue>;
|
||||
Map map_;
|
||||
|
||||
// Set the default value, internal
|
||||
//
|
||||
eng::string add_default(const eng::string &name, const AnimValue &v, const AnimState *other);
|
||||
|
||||
public:
|
||||
// World type determines whether versions increment or autozero
|
||||
AnimQueue(WorldType wt);
|
||||
// Clear everything
|
||||
//
|
||||
void clear() { map_.clear(); }
|
||||
|
||||
// Simple getters.
|
||||
const AnimStep &nth(int n) const { return steps_[n]; }
|
||||
size_t size() const { return steps_.size(); }
|
||||
// Set the persistent flag on a single variable.
|
||||
// If the variable isn't present, add it.
|
||||
//
|
||||
void set_persistent(const eng::string &name);
|
||||
|
||||
// Return if it contains a value for the specified name.
|
||||
//
|
||||
bool contains(const eng::string &name) { return map_.find(name) != map_.end(); }
|
||||
|
||||
// Get the value of a specific variable.
|
||||
// If the variable isn't present, return a default value.
|
||||
//
|
||||
bool get_boolean(const eng::string &name);
|
||||
double get_number(const eng::string &name);
|
||||
util::DXYZ get_xyz(const eng::string &name);
|
||||
std::string_view get_string(const eng::string &name);
|
||||
|
||||
// Set a single variable to a value of a specified type.
|
||||
// If the variable isn't present, add it.
|
||||
//
|
||||
void set_boolean(const eng::string &name, bool v);
|
||||
void set_number(const eng::string &name, double v);
|
||||
void set_xyz(const eng::string &name, const util::DXYZ &value);
|
||||
void set_string(const eng::string &name, std::string_view value);
|
||||
|
||||
// Print a debug string into the stringstream.
|
||||
//
|
||||
void print_debug_string(eng::ostringstream &oss);
|
||||
|
||||
// Return the debug string.
|
||||
eng::string debug_string();
|
||||
|
||||
// Constructs an empty state.
|
||||
//
|
||||
AnimState() {}
|
||||
|
||||
// Convert to an encoded string.
|
||||
//
|
||||
eng::string encode() const;
|
||||
|
||||
// Decode an encoded string.
|
||||
//
|
||||
void decode(std::string_view enc);
|
||||
|
||||
// Decode an encoded string, discarding non-persistent data.
|
||||
//
|
||||
void decode_persistent(std::string_view enc);
|
||||
|
||||
// Add default values for all builtin persistent variables.
|
||||
//
|
||||
// For each builtin default (plane, xyz, facing, bp, model)
|
||||
//
|
||||
// - Will generate an error if a value is already present,
|
||||
// but the present value is of the wrong type.
|
||||
//
|
||||
// - If 'other' is not nullptr, then we look in 'other' for a default
|
||||
// value. If a valid value of the correct type is present, it is
|
||||
// used as the default value.
|
||||
//
|
||||
// - If no default value can be found in 'other', then a hardwired
|
||||
// default value is provided.
|
||||
//
|
||||
eng::string add_defaults(const AnimState *other);
|
||||
|
||||
// Apply a configuration from a lua table.
|
||||
//
|
||||
// If a key already exists in the state, then type type from the table
|
||||
// must match the type from the existing state.
|
||||
//
|
||||
eng::string apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist);
|
||||
|
||||
// Convert an animstate to a lua table.
|
||||
//
|
||||
// You can either convert the persistent key-value pairs, or the
|
||||
// nonpersistent. So you'll need two lua tables to store both.
|
||||
//
|
||||
void to_lua(LuaCoreStack &LS, LuaSlot tab, bool persistent);
|
||||
|
||||
// Parse a string, for unit testing.
|
||||
//
|
||||
void parse(std::string_view s);
|
||||
void clear_and_parse(std::string_view s);
|
||||
|
||||
// Constructor from an encoded string.
|
||||
//
|
||||
AnimState(std::string_view s) { decode(s); }
|
||||
};
|
||||
|
||||
struct AnimCoreState
|
||||
{
|
||||
util::DXYZ xyz;
|
||||
eng::string plane;
|
||||
|
||||
void decode(std::string_view enc);
|
||||
};
|
||||
|
||||
class AnimQueue : public eng::nevernew {
|
||||
private:
|
||||
struct Step {
|
||||
Step() { hash=0; }
|
||||
Step(const eng::string &e, uint64_t h) : encoding(e), hash(h) {}
|
||||
eng::string encoding;
|
||||
uint64_t hash;
|
||||
};
|
||||
|
||||
public:
|
||||
// Construct an empty animation queue.
|
||||
// clears the state to a valid state.
|
||||
//
|
||||
AnimQueue();
|
||||
|
||||
// Size limit.
|
||||
//
|
||||
int32_t size_limit() const { return size_limit_; }
|
||||
int64_t version_number() const { return version_number_; }
|
||||
bool version_identical(const AnimQueue &aq) const;
|
||||
|
||||
// Return true if the size limit and steps are identical.
|
||||
bool size_and_steps_equal(const AnimQueue &aq) const;
|
||||
// Clear and set the initial state.
|
||||
//
|
||||
void clear();
|
||||
void clear(const AnimState &initial);
|
||||
|
||||
// Mutator to create a new step.
|
||||
void add(int64_t id, const AnimStep &step);
|
||||
// Set the size limit. Must be 2-250
|
||||
//
|
||||
void set_limit(int n);
|
||||
|
||||
// Clear and set the plane.
|
||||
void clear(const eng::string &plane);
|
||||
// Add an animation step.
|
||||
//
|
||||
// Note: add does not automatically compose the step with the previous
|
||||
// step, you have to do that yourself.
|
||||
//
|
||||
void add(const AnimState &state);
|
||||
|
||||
// Serialize or deserialize to a StreamBuffer
|
||||
//
|
||||
// Caution: version numbers are not stored. On deserialize,
|
||||
// version number is set to zero.
|
||||
//
|
||||
void serialize(StreamBuffer *sb) const;
|
||||
void deserialize(StreamBuffer *sb);
|
||||
|
||||
// Difference transmission
|
||||
//
|
||||
bool diff(const AnimQueue &auth, StreamBuffer *sb) const;
|
||||
void patch(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void update_version(const AnimQueue &auth);
|
||||
|
||||
// Get the final resting place after all animations are complete.
|
||||
const AnimStep &back() const;
|
||||
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Check for exactly equal. This does the full check of all
|
||||
// fields, it is used exclusively for debugging.
|
||||
//
|
||||
// TESTING SUPPORT
|
||||
bool exactly_equal(const AnimQueue &aq) const;
|
||||
|
||||
// Check for exactly equal (fast). Compares the size, limit,
|
||||
// and hash of the last entry. If these are equal, then the whole
|
||||
// thing should be equal.
|
||||
//
|
||||
// The following functions are not designed to be useful for production
|
||||
// code, they're designed to be helpful for unit testing.
|
||||
bool exactly_equal_fast(const AnimQueue &aq) const;
|
||||
|
||||
// Debug strings.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Change the size limit.
|
||||
void full_clear_and_set_limit(int szl);
|
||||
void set_limit(int szl);
|
||||
|
||||
// Make sure the invariants are preserved.
|
||||
bool valid() const;
|
||||
|
||||
// Convert to a string for debugging purposes.
|
||||
eng::string steps_debug_string() const;
|
||||
eng::string full_debug_string() const;
|
||||
|
||||
// Convert to a
|
||||
// Get the final entry, xyz and plane only.
|
||||
//
|
||||
AnimCoreState get_final_core_state() const;
|
||||
|
||||
// Get the final entry, all persistent variables.
|
||||
//
|
||||
AnimState get_final_persistent() const;
|
||||
|
||||
// Get the final entry, everything persistent and non-persistent.
|
||||
//
|
||||
AnimState get_final_everything() const;
|
||||
|
||||
private:
|
||||
bool version_autoinc_;
|
||||
int32_t size_limit_;
|
||||
eng::deque<AnimStep> steps_;
|
||||
int64_t version_number_;
|
||||
|
||||
// Called whenever the animation queue is mutated.
|
||||
// serialization and deserialization
|
||||
void mutated();
|
||||
int size_limit_;
|
||||
eng::deque<Step> steps_;
|
||||
};
|
||||
|
||||
#endif // ANIMQUEUE_HPP
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "wrap-vector.hpp"
|
||||
#include "util.hpp"
|
||||
#include "drivenengine.hpp"
|
||||
#include "world.hpp"
|
||||
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -138,6 +139,11 @@ void DrivenEngine::rescan_lua_source() {
|
||||
rescan_lua_source_ = true;
|
||||
}
|
||||
|
||||
void DrivenEngine::set_visible_world_and_actor(World *w, int64_t id) {
|
||||
visible_world_ = w;
|
||||
visible_actor_id_ = id;
|
||||
}
|
||||
|
||||
void DrivenEngine::stop_driver() {
|
||||
stop_driver_ = true;
|
||||
for (int i = 0; i < DRV_MAX_CHAN; i++) {
|
||||
@@ -190,8 +196,8 @@ inline static const char *action_string(DrvAction act) {
|
||||
case PLAY_RECV_INCOMING: return "PLAY_RECV_INCOMING";
|
||||
case PLAY_NOTIFY_CLOSE: return "PLAY_NOTIFY_CLOSE";
|
||||
case PLAY_NOTIFY_ACCEPT: return "PLAY_NOTIFY_ACCEPT";
|
||||
case PLAY_SET_LUA_SOURCE: return "PLAY_SET_LUA_SOURCE";
|
||||
case PLAY_INVOKE_EVENT_UPDATE: return "PLAY_INVOKE_EVENT_UPDATE";
|
||||
case PLAY_SET_LUA_SOURCE: return "PLAY_SET_LUA_SOURCE";
|
||||
case PLAY_RELEASE: return "PLAY_RELEASE";
|
||||
default: return "unknown";
|
||||
}
|
||||
@@ -403,6 +409,30 @@ bool DrivenEngine::drv_get_stop_driver() const {
|
||||
return stop_driver_;
|
||||
}
|
||||
|
||||
uint64_t DrivenEngine::drv_get_actor_id() const {
|
||||
return visible_actor_id_;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_get_tangibles_near(uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids) {
|
||||
scan_result_.clear();
|
||||
if ((visible_world_ != 0) && (tanid != 0)) {
|
||||
PlaneScan scan;
|
||||
scan.set_near(tanid, true);
|
||||
scan.set_omit_nowhere(true);
|
||||
scan.set_sorted(false);
|
||||
scan.set_radius(util::XYZ(rx, ry, rz));
|
||||
scan.set_shape(PlaneScan::CYLINDER);
|
||||
visible_world_->get_near(scan, &scan_result_);
|
||||
}
|
||||
*count = scan_result_.size();
|
||||
if (*count > 0) {
|
||||
*ids = &scan_result_[0];
|
||||
} else {
|
||||
*ids = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -465,6 +495,8 @@ void DrivenEngine::drv_set_lua_source(uint32_t srcpklen, const char *srcpk) {
|
||||
rescan_lua_source_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -522,6 +554,15 @@ static bool drv_get_stop_driver(EngineWrapper *w) {
|
||||
return w->engine->drv_get_stop_driver();
|
||||
}
|
||||
|
||||
static uint64_t drv_get_actor_id(EngineWrapper *w) {
|
||||
return w->engine->drv_get_actor_id();
|
||||
}
|
||||
|
||||
static void drv_get_tangibles_near(EngineWrapper *w, uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids) {
|
||||
return w->engine->drv_get_tangibles_near(tanid, rx, ry, rz, count, ids);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -798,6 +839,7 @@ void replay_set_lua_source(EngineWrapper *w) {
|
||||
w->engine->drv_set_lua_source(srcpack.size(), srcpack.c_str());
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -864,21 +906,13 @@ static void replaycore_step(EngineWrapper *w) {
|
||||
case PLAY_RECV_INCOMING: replay_recv_incoming(w); return;
|
||||
case PLAY_NOTIFY_CLOSE: replay_notify_close(w); return;
|
||||
case PLAY_NOTIFY_ACCEPT: replay_notify_accept(w); return;
|
||||
case PLAY_SET_LUA_SOURCE: replay_set_lua_source(w); return;
|
||||
case PLAY_INVOKE_EVENT_UPDATE: replay_invoke_event_update(w); return;
|
||||
case PLAY_SET_LUA_SOURCE: replay_set_lua_source(w); return;
|
||||
case PLAY_RELEASE: release(w); return;
|
||||
default: return reset_wrapper(w, "Replay log corrupt in command dispatcher");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// General Mutators
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -923,6 +957,8 @@ static void init_engine_wrapper_helper(EngineWrapper *w) {
|
||||
w->get_clock = drv_get_clock;
|
||||
w->get_rescan_lua_source = drv_get_rescan_lua_source;
|
||||
w->get_stop_driver = drv_get_stop_driver;
|
||||
w->get_actor_id = drv_get_actor_id;
|
||||
w->get_tangibles_near = drv_get_tangibles_near;
|
||||
|
||||
w->play_initialize = play_initialize;
|
||||
w->play_clear_new_outgoing = play_clear_new_outgoing;
|
||||
|
||||
@@ -52,8 +52,10 @@
|
||||
#include "util.hpp"
|
||||
#include "streambuffer.hpp"
|
||||
#include "enginewrapper.hpp"
|
||||
#include "planemap.hpp"
|
||||
|
||||
class DrivenEngine;
|
||||
class World;
|
||||
using UniqueDrivenEngine = std::unique_ptr<DrivenEngine>;
|
||||
using DrivenEngineMaker = UniqueDrivenEngine (*)();
|
||||
using DrivenEngineInitializer = void (*)();
|
||||
@@ -222,6 +224,14 @@ public:
|
||||
//
|
||||
void rescan_lua_source();
|
||||
|
||||
// Set the world pointer and the actor ID.
|
||||
//
|
||||
// This allows the graphics engine to query the DrivenEngine
|
||||
// about the state of the world and the player. It is legal to set these
|
||||
// to zero, in which case queries will return null results.
|
||||
//
|
||||
void set_visible_world_and_actor(World *w, int64_t actor);
|
||||
|
||||
// Stop the driver. The engine should call this when it's done
|
||||
// and there's nothing left to do.
|
||||
//
|
||||
@@ -270,6 +280,8 @@ public:
|
||||
double drv_get_clock() const;
|
||||
bool drv_get_rescan_lua_source() const;
|
||||
bool drv_get_stop_driver() const;
|
||||
uint64_t drv_get_actor_id() const;
|
||||
void drv_get_tangibles_near(uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids);
|
||||
|
||||
void drv_initialize(uint32_t srcpklen, const char *srcpk, int argc, char **argv);
|
||||
void drv_clear_new_outgoing();
|
||||
@@ -296,6 +308,9 @@ private:
|
||||
eng::vector<uint32_t> new_outgoing_;
|
||||
util::LuaSourcePtr lua_source_;
|
||||
eng::vector<uint32_t> listen_ports_;
|
||||
World *visible_world_;
|
||||
int64_t visible_actor_id_;
|
||||
util::IdVector scan_result_;
|
||||
bool rescan_lua_source_;
|
||||
double clock_;
|
||||
bool stop_driver_;
|
||||
|
||||
@@ -110,6 +110,14 @@ struct EngineWrapper {
|
||||
//
|
||||
bool (*get_stop_driver)(EngineWrapper *w);
|
||||
|
||||
// Get the actor ID. May return zero if the server is down.
|
||||
//
|
||||
uint64_t (*get_actor_id)(EngineWrapper *w);
|
||||
|
||||
// Get the results of the last scan radius.
|
||||
//
|
||||
void (*get_tangibles_near)(EngineWrapper *w, uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
|
||||
// Set the console prompt.
|
||||
set_console_prompt(console_.get_prompt());
|
||||
|
||||
// Export stuff to the graphics engine.
|
||||
set_visible_world_and_actor(master_.get(), admin_id_);
|
||||
}
|
||||
|
||||
void do_luainvoke_command(const util::StringVec &words) {
|
||||
|
||||
@@ -94,6 +94,10 @@ void LuaConsole::simplify(const StringVec &words) {
|
||||
if (words.size() != 1) {
|
||||
synerr("/work takes no arguments");
|
||||
}
|
||||
} else if (words[0] == "display") {
|
||||
if (words.size() != 1) {
|
||||
synerr("/display takes no arguments");
|
||||
}
|
||||
} else if (words[0] == "aborthttp") {
|
||||
if (words.size() != 1) {
|
||||
synerr("/aborthttp takes no arguments");
|
||||
|
||||
@@ -224,13 +224,12 @@ public:
|
||||
|
||||
using NodeID = uint64_t;
|
||||
using ChildBits = uint64_t;
|
||||
using IdVector = util::IdVector;
|
||||
|
||||
// The PlaneMap that this tree is a part of.
|
||||
PlaneMap *planemap_;
|
||||
|
||||
// The name of this plane.
|
||||
eng::string plane_;
|
||||
std::string plane_;
|
||||
|
||||
// Internal nodes in the tree just have bits indicating
|
||||
// which children exist.
|
||||
@@ -440,7 +439,7 @@ public:
|
||||
(*os) << "| ";
|
||||
print_node_id(node, os);
|
||||
(*os) << " ";
|
||||
util::IdVector ids;
|
||||
IdVector ids;
|
||||
collect_planeitem_ids(first, &ids);
|
||||
std::sort(ids.begin(), ids.end());
|
||||
util::print_id_vector(ids, os);
|
||||
@@ -667,7 +666,7 @@ public:
|
||||
}
|
||||
|
||||
// Construct a PlaneTree.
|
||||
PlaneTree(PlaneMap *pmap, const eng::string &plane) {
|
||||
PlaneTree(PlaneMap *pmap, std::string_view plane) {
|
||||
planemap_ = pmap;
|
||||
plane_ = plane;
|
||||
total_count_ = 0;
|
||||
@@ -779,30 +778,29 @@ PlaneMap::PlaneMap() : default_radius_(32768.0) {}
|
||||
PlaneMap::~PlaneMap() {}
|
||||
|
||||
|
||||
IdVector PlaneMap::scan(const PlaneScan &sc) const {
|
||||
IdVector result;
|
||||
void PlaneMap::scan(const PlaneScan &sc, util::IdVector *into) const {
|
||||
into->clear();
|
||||
int startpos = 0;
|
||||
if (sc.near_ != 0) {
|
||||
if (sc.include_near_) {
|
||||
result.push_back(sc.near_);
|
||||
into->push_back(sc.near_);
|
||||
startpos = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc.omit_nowhere_ && (sc.plane_ == "nowhere")) {
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
|
||||
auto piter = planes_.find(sc.plane_);
|
||||
auto piter = planes_.find(std::string_view(sc.plane_));
|
||||
if (piter != planes_.end()) {
|
||||
const std::unique_ptr<PlaneTree> &tree = piter->second;
|
||||
tree->scan(sc, &result, nullptr);
|
||||
tree->scan(sc, into, nullptr);
|
||||
}
|
||||
|
||||
if (sc.sorted_) {
|
||||
std::sort(result.begin() + startpos, result.end());
|
||||
std::sort(into->begin() + startpos, into->end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
eng::string PlaneMap::tree_debug_string(const eng::string &plane) {
|
||||
@@ -814,11 +812,11 @@ eng::string PlaneMap::outliers_debug_string(const eng::string &plane) {
|
||||
}
|
||||
|
||||
eng::string PlaneMap::search_bboxes_debug_string(const PlaneScan &scan) {
|
||||
return PlaneTree::get(this, scan.plane_)->search_bboxes_debug_string(scan);
|
||||
return PlaneTree::get(this, eng::string(scan.plane_))->search_bboxes_debug_string(scan);
|
||||
}
|
||||
|
||||
eng::string PlaneMap::scan_steps_debug_string(const PlaneScan &scan) {
|
||||
return PlaneTree::get(this, scan.plane_)->scan_steps_debug_string(scan);
|
||||
return PlaneTree::get(this, eng::string(scan.plane_))->scan_steps_debug_string(scan);
|
||||
}
|
||||
|
||||
void PlaneMap::untrack_all() {
|
||||
@@ -1161,7 +1159,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
// The two corners are deliberately not in low-high order.
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x23, 0x97, 0x103), 2.0f);
|
||||
scan.set_center_and_radius(util::XYZ(0x23, 0x97, 0x103), 2.0f);
|
||||
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
|
||||
"|Level 8 0,0,0 - 0,0,0"
|
||||
"|Level 6 8,8,8 - 8,8,8"
|
||||
@@ -1181,7 +1179,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
// sure they only include the one cell.
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x12, 0x34, 0x45), 0.0);
|
||||
scan.set_center_and_radius(util::XYZ(0x12, 0x34, 0x45), 0.0);
|
||||
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
|
||||
"|Level 8 0,0,0 - 0,0,0"
|
||||
"|Level 6 8,8,8 - 8,8,8"
|
||||
@@ -1207,7 +1205,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
// from one cell to the next.
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x12 + 0.5, 0x34, 0x45), 0.0);
|
||||
scan.set_center_and_radius(util::XYZ(0x12 + 0.5, 0x34, 0x45), 0.0);
|
||||
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
|
||||
"|Level 8 0,0,0 - 0,0,0"
|
||||
"|Level 6 8,8,8 - 8,8,8"
|
||||
@@ -1235,7 +1233,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
// to make sure they cover the entire PlaneTree.
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x12, 0x34, 0x45), 100000.0);
|
||||
scan.set_center_and_radius(util::XYZ(0x12, 0x34, 0x45), 100000.0);
|
||||
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
|
||||
"|Level 8 0,0,0 - 0,0,0"
|
||||
"|Level 6 0,0,0 - f,f,f"
|
||||
@@ -1292,7 +1290,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_shape(PlaneScan::SPHERE);
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x12 + 0.1, 0x34, 0x45), 0.0);
|
||||
scan.set_center_and_radius(util::XYZ(0x12 + 0.1, 0x34, 0x45), 0.0);
|
||||
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
|
||||
"|L8:root"
|
||||
"| L7:2,2,2"
|
||||
@@ -1350,7 +1348,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
|
||||
// because the radius is nonzero.
|
||||
scan.clear();
|
||||
scan.set_plane("p");
|
||||
scan.set_bbox_given_center_radius(util::XYZ(0x100000, 0x16, 0x23), 0.2);
|
||||
scan.set_center_and_radius(util::XYZ(0x100000, 0x16, 0x23), 0.2);
|
||||
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
|
||||
"|Level 8 0,0,0 - 0,0,0"
|
||||
"|Level 6 f,8,8 - f,8,8"
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
class PlaneMap;
|
||||
class PlaneTree;
|
||||
|
||||
|
||||
class PlaneScan : public eng::nevernew {
|
||||
public:
|
||||
friend class PlaneMap;
|
||||
@@ -94,7 +95,11 @@ public:
|
||||
enum Shape { BOX, CYLINDER, SPHERE };
|
||||
private:
|
||||
// The plane to scan.
|
||||
eng::string plane_;
|
||||
//
|
||||
// Note: the reason this uses std::string instead of eng::string
|
||||
// is that we want plane scans to not touch the engine heap.
|
||||
//
|
||||
std::string plane_;
|
||||
|
||||
// The bounding box of the scan.
|
||||
util::XYZ center_, radius_;
|
||||
@@ -119,13 +124,14 @@ private:
|
||||
|
||||
public:
|
||||
void clear() {
|
||||
plane_ = "";
|
||||
plane_.clear();
|
||||
shape_ = BOX;
|
||||
sorted_ = true;
|
||||
near_ = 0;
|
||||
include_near_ = false;
|
||||
omit_nowhere_ = false;
|
||||
radius_ = center_ = util::XYZ();
|
||||
radius_ = util::XYZ();
|
||||
center_ = util::XYZ();
|
||||
}
|
||||
PlaneScan() { clear(); }
|
||||
|
||||
@@ -143,7 +149,7 @@ public:
|
||||
//
|
||||
void configure(LuaKeywordParser &kw);
|
||||
|
||||
void set_bbox_given_center_radius(const util::XYZ ¢er, float r) {
|
||||
void set_center_and_radius(const util::XYZ ¢er, float r) {
|
||||
set_center(center);
|
||||
set_radius(r);
|
||||
}
|
||||
@@ -204,12 +210,12 @@ public:
|
||||
class PlaneMap : public eng::nevernew {
|
||||
friend class PlaneItem;
|
||||
friend class PlaneTree;
|
||||
public:
|
||||
using IdVector = util::IdVector;
|
||||
|
||||
private:
|
||||
float default_radius_;
|
||||
eng::map<eng::string, std::unique_ptr<PlaneTree>> planes_;
|
||||
|
||||
// Plane Trees table.
|
||||
//
|
||||
eng::map<eng::string, std::unique_ptr<PlaneTree>, std::less<>> planes_;
|
||||
|
||||
public:
|
||||
// No special code is needed for construction or destruction.
|
||||
@@ -220,7 +226,11 @@ public:
|
||||
// PlaneItem. See PlaneItem::track and PlaneItem::untrack.
|
||||
|
||||
// Scan the PlaneMap for items, return their IDs.
|
||||
IdVector scan(const PlaneScan &s) const;
|
||||
//
|
||||
// Note: the reason this uses IdVector instead of IdVector
|
||||
// is that we want scans to not touch the engine heap.
|
||||
//
|
||||
void scan(const PlaneScan &sc, util::IdVector *into) const;
|
||||
|
||||
// Set the default radius for all planes.
|
||||
// Maybe we'll make it adaptive some day.
|
||||
|
||||
@@ -281,6 +281,26 @@ void StreamBuffer::write_double(double d) {
|
||||
write_cursor_ += 8;
|
||||
}
|
||||
|
||||
void StreamBuffer::write_xyz(const util::XYZ &xyz) {
|
||||
make_space(12);
|
||||
memcpy(write_cursor_, &xyz.x, 4);
|
||||
write_cursor_ += 4;
|
||||
memcpy(write_cursor_, &xyz.y, 4);
|
||||
write_cursor_ += 4;
|
||||
memcpy(write_cursor_, &xyz.z, 4);
|
||||
write_cursor_ += 4;
|
||||
}
|
||||
|
||||
void StreamBuffer::write_dxyz(const util::DXYZ &xyz) {
|
||||
make_space(24);
|
||||
memcpy(write_cursor_, &xyz.x, 8);
|
||||
write_cursor_ += 8;
|
||||
memcpy(write_cursor_, &xyz.y, 8);
|
||||
write_cursor_ += 8;
|
||||
memcpy(write_cursor_, &xyz.z, 8);
|
||||
write_cursor_ += 8;
|
||||
}
|
||||
|
||||
int8_t StreamBuffer::read_int8() {
|
||||
check_available(1);
|
||||
int8_t v;
|
||||
@@ -336,6 +356,32 @@ double StreamBuffer::read_double() {
|
||||
return d;
|
||||
}
|
||||
|
||||
util::XYZ StreamBuffer::read_xyz() {
|
||||
check_available(12);
|
||||
util::XYZ result;
|
||||
memcpy(&result.x, read_cursor_, 4);
|
||||
read_cursor_ += 4;
|
||||
memcpy(&result.y, read_cursor_, 4);
|
||||
read_cursor_ += 4;
|
||||
memcpy(&result.z, read_cursor_, 4);
|
||||
read_cursor_ += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
util::DXYZ StreamBuffer::read_dxyz() {
|
||||
check_available(24);
|
||||
util::DXYZ result;
|
||||
memcpy(&result.x, read_cursor_, 8);
|
||||
read_cursor_ += 8;
|
||||
memcpy(&result.y, read_cursor_, 8);
|
||||
read_cursor_ += 8;
|
||||
memcpy(&result.z, read_cursor_, 8);
|
||||
read_cursor_ += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StreamBuffer::write_hashvalue(const util::HashValue &hv) {
|
||||
write_uint64(hv.first);
|
||||
write_uint64(hv.second);
|
||||
|
||||
@@ -313,6 +313,8 @@ public:
|
||||
void write_char(char c);
|
||||
void write_float(float f);
|
||||
void write_double(double d);
|
||||
void write_xyz(const util::XYZ &xyz);
|
||||
void write_dxyz(const util::DXYZ &xyz);
|
||||
|
||||
// Read fixed-size integers from the buffer.
|
||||
//
|
||||
@@ -329,6 +331,8 @@ public:
|
||||
char read_char();
|
||||
float read_float();
|
||||
double read_double();
|
||||
util::XYZ read_xyz();
|
||||
util::DXYZ read_dxyz();
|
||||
|
||||
// Write other types into the buffer.
|
||||
//
|
||||
|
||||
@@ -494,6 +494,15 @@ void print_id_vector(const IdVector &idv, std::ostream *os) {
|
||||
}
|
||||
}
|
||||
|
||||
void print_id_vector(const std::vector<uint64_t> &idv, std::ostream *os) {
|
||||
bool first = true;
|
||||
for (int64_t id : idv) {
|
||||
if (!first) (*os) << ",";
|
||||
(*os) << id;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
eng::string id_vector_debug_string(const IdVector &idv) {
|
||||
eng::ostringstream oss;
|
||||
print_id_vector(idv, &oss);
|
||||
@@ -518,10 +527,17 @@ IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2) {
|
||||
return result;
|
||||
}
|
||||
|
||||
HashValue hash_string(const eng::string &s) {
|
||||
HashValue hash_string(std::string_view s) {
|
||||
uint64_t hash1 = 0;
|
||||
uint64_t hash2 = 0;
|
||||
SpookyHash::ChainHash128(s.c_str(), s.size(), &hash1, &hash2);
|
||||
SpookyHash::ChainHash128(s.data(), s.size(), &hash1, &hash2);
|
||||
return util::HashValue(hash1, hash2);
|
||||
}
|
||||
|
||||
HashValue hash_string(HashValue prev, std::string_view s) {
|
||||
uint64_t hash1 = prev.first;
|
||||
uint64_t hash2 = prev.second;
|
||||
SpookyHash::ChainHash128(s.data(), s.size(), &hash1, &hash2);
|
||||
return util::HashValue(hash1, hash2);
|
||||
}
|
||||
|
||||
@@ -718,11 +734,6 @@ LuaSourcePtr make_lua_source(const eng::string &code) {
|
||||
return result;
|
||||
}
|
||||
|
||||
eng::string XYZ::debug_string() const {
|
||||
eng::ostringstream oss;
|
||||
oss << "(" << x << "," << y << "," << z << ")";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
void (*dprint_hook)(const char *oneline, size_t size);
|
||||
|
||||
@@ -211,13 +211,18 @@ enum MessageType {
|
||||
MSG_INVOKE,
|
||||
};
|
||||
|
||||
// Note: IdVector is weird in that it deliberately uses std::vector
|
||||
// instead of eng::vector. This is because we want plane scans
|
||||
// to not touch the engine heap.
|
||||
//
|
||||
using IdVector = std::vector<int64_t>;
|
||||
|
||||
using StringVec = eng::vector<eng::string>;
|
||||
using StringPair = std::pair<eng::string, eng::string>;
|
||||
using StringSet = eng::set<eng::string>;
|
||||
using LuaSourceVec = eng::vector<StringPair>;
|
||||
using LuaSourcePtr = std::unique_ptr<LuaSourceVec>;
|
||||
using HashValue = std::pair<uint64_t, uint64_t>;
|
||||
using IdVector = eng::vector<int64_t>;
|
||||
|
||||
// Ascii uppercase and lowercase.
|
||||
eng::string ascii_tolower(std::string_view c);
|
||||
@@ -241,6 +246,7 @@ IdVector id_vector_create(int64_t id1=-1, int64_t id2=-1, int64_t id3=-1, int64_
|
||||
|
||||
// Print an ID vector to a stream.
|
||||
void print_id_vector(const IdVector &idv, std::ostream *os);
|
||||
void print_id_vector(const std::vector<uint64_t> &idv, std::ostream *os);
|
||||
|
||||
// ID vector debug string.
|
||||
eng::string id_vector_debug_string(const IdVector &idv);
|
||||
@@ -249,7 +255,10 @@ eng::string id_vector_debug_string(const IdVector &idv);
|
||||
IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2);
|
||||
|
||||
// Get a 128-bit hashvalue for a string.
|
||||
HashValue hash_string(const eng::string &str);
|
||||
HashValue hash_string(std::string_view str);
|
||||
|
||||
// Get a 128-bit hashvalue for a string, with a previous value.
|
||||
HashValue hash_string(HashValue prev, std::string_view str);
|
||||
|
||||
// Get a 128-bit hashvalue for an ID vector.
|
||||
HashValue hash_id_vector(const IdVector &idv);
|
||||
@@ -322,18 +331,32 @@ void remove_marked_items(T &vec) {
|
||||
}
|
||||
|
||||
// An XYZ coordinate, general purpose.
|
||||
struct XYZ {
|
||||
float x, y, z;
|
||||
XYZ() { x=0; y=0; z=0; }
|
||||
XYZ(float ix, float iy, float iz) { x=ix; y=iy; z=iz; }
|
||||
bool operator ==(const XYZ &o) const { return x==o.x && y == o.y && z==o.z; }
|
||||
bool operator !=(const XYZ &o) const { return x!=o.x || y != o.y || z!=o.z; }
|
||||
XYZ operator -(const XYZ &o) const { return XYZ(x-o.x, y-o.y, z-o.z); }
|
||||
XYZ operator +(const XYZ &o) const { return XYZ(x+o.x, y+o.y, z+o.z); }
|
||||
XYZ operator *(float scale) const { return XYZ(x*scale, y*scale, z*scale); }
|
||||
eng::string debug_string() const;
|
||||
template <typename NUMBER>
|
||||
struct NumXYZ {
|
||||
using Number = NUMBER;
|
||||
Number x, y, z;
|
||||
NumXYZ() { x=0; y=0; z=0; }
|
||||
NumXYZ(Number ix, Number iy, Number iz) { x=ix; y=iy; z=iz; }
|
||||
void operator =(const NumXYZ &other) { x = other.x; y = other.y; z = other.z; }
|
||||
void operator =(Number n) { x = n; y = n; z = n; }
|
||||
bool operator ==(const NumXYZ &o) const { return x==o.x && y == o.y && z==o.z; }
|
||||
bool operator !=(const NumXYZ &o) const { return x!=o.x || y != o.y || z!=o.z; }
|
||||
NumXYZ operator -(const NumXYZ &o) const { return NumXYZ(x-o.x, y-o.y, z-o.z); }
|
||||
NumXYZ operator +(const NumXYZ &o) const { return NumXYZ(x+o.x, y+o.y, z+o.z); }
|
||||
NumXYZ operator *(float scale) const { return NumXYZ(x*scale, y*scale, z*scale); }
|
||||
template<typename ONUMBER>
|
||||
const NumXYZ<ONUMBER> convert() const { NumXYZ<ONUMBER> r; r.x=ONUMBER(x); r.y=ONUMBER(y); r.z=ONUMBER(z); return r; }
|
||||
|
||||
eng::string debug_string() const {
|
||||
eng::ostringstream oss;
|
||||
oss << "(" << x << "," << y << "," << z << ")";
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
using XYZ=NumXYZ<float>;
|
||||
using DXYZ=NumXYZ<double>;
|
||||
|
||||
// util::ostringstream
|
||||
//
|
||||
// This is a variant of ostringstream in which it is possible
|
||||
@@ -455,4 +478,9 @@ inline std::ostream &operator<<(std::ostream &oss, const util::XYZ &xyz) {
|
||||
return oss;
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &oss, const util::DXYZ &xyz) {
|
||||
oss << xyz.x << "," << xyz.y << "," << xyz.z;
|
||||
return oss;
|
||||
}
|
||||
|
||||
#endif // UTIL_HPP
|
||||
|
||||
@@ -17,56 +17,169 @@ static void tangible_getall(LuaCoreStack &LS0, LuaSlot list, const util::IdVecto
|
||||
}
|
||||
}
|
||||
|
||||
LuaDefine(tangible_animstate, "tan",
|
||||
"|Get the entire animation state of the tangible."
|
||||
"|Returns six values: graphic,plane,x,y,z,facing.") {
|
||||
LuaArg tanobj;
|
||||
LuaRet graphic, plane, x, y, z, facing;
|
||||
LuaDefStack LS(L, tanobj, graphic, plane, x, y, z, facing);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
LS.set(graphic, aqback.graphic());
|
||||
LS.set(plane, aqback.plane());
|
||||
LS.set(x, aqback.xyz().x);
|
||||
LS.set(y, aqback.xyz().y);
|
||||
LS.set(z, aqback.xyz().z);
|
||||
LS.set(facing, aqback.facing());
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_xyz, "tan",
|
||||
"|Get the current coordinates of the tangible."
|
||||
"|Returns three values: x, y, z") {
|
||||
"|Get the current coordinates of the tangible and the plane."
|
||||
"|Returns four values: x, y, z, plane") {
|
||||
LuaArg tanobj;
|
||||
LuaRet x, y, z;
|
||||
LuaRet x, y, z, plane;
|
||||
LuaDefStack LS(L, tanobj, x, y, z);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
LS.set(x, aqback.xyz().x);
|
||||
LS.set(y, aqback.xyz().y);
|
||||
LS.set(z, aqback.xyz().z);
|
||||
AnimCoreState pos = tan->anim_queue_.get_final_core_state();
|
||||
LS.set(x, pos.xyz.x);
|
||||
LS.set(y, pos.xyz.y);
|
||||
LS.set(z, pos.xyz.z);
|
||||
LS.set(plane, pos.plane);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_animate, "tan,configtable",
|
||||
"|Add an animation step to the tangible."
|
||||
"|The configtable is a table containing any of the following:"
|
||||
"|action,graphic,plane,x,y,z,facing") {
|
||||
LuaArg tanobj, config;
|
||||
LuaDefStack LS(L, tanobj, config);
|
||||
LuaKeywordParser kp(LS, config);
|
||||
LuaDefine(tangible_animdebug, "tan",
|
||||
"|Return a debug string showing the entire animation queue"
|
||||
"|") {
|
||||
LuaArg tanobj;
|
||||
LuaRet result;
|
||||
LuaDefStack LS(L, tanobj, result);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
int64_t id = w->alloc_id_predictable();
|
||||
AnimStep step;
|
||||
step.configure(kp, tan->anim_queue_.back());
|
||||
kp.final_check_throw();
|
||||
if (step.action() == "") {
|
||||
luaL_error(L, "animation action must be specified");
|
||||
LS.set(result, tan->anim_queue_.steps_debug_string());
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_animstate, "tan",
|
||||
"|Return the animation state variables of the tangible as a table."
|
||||
"|"
|
||||
"|The animation system stores 'animation state variables. "
|
||||
"|There are several builtin animation state variables. The"
|
||||
"|following is a list, along with some example values that"
|
||||
"|might be used if the object were a pirate treasure chest."
|
||||
"|"
|
||||
"| xyz={1,2,3} # xyz coordinate"
|
||||
"| plane='earth' # plane where the chest is located"
|
||||
"| facing=0 # rotation of the chest"
|
||||
"| bp='BP_piratechest' # name of an unreal blueprint"
|
||||
"| model='SM_piratechest' # name of an unreal mesh"
|
||||
"|"
|
||||
"|There can also be user-defined animation state variables."
|
||||
"|For example, for a pirate chest, you might want to add two"
|
||||
"|more state variables:"
|
||||
"|"
|
||||
"| open=true # chest can be open or closed"
|
||||
"| fullness=0.8 # how big the heap of coins is"
|
||||
"|"
|
||||
"|All state variables must be one of four types: number, string,"
|
||||
"|boolean, or coordinate. All state variables must have simple"
|
||||
"|identifiers for names."
|
||||
"|"
|
||||
"|Animation state variables are updated when you use"
|
||||
"|tangible.animate to create an animation record. For example,"
|
||||
"|suppose your character is initialized at xyz={1,1,1}. Then"
|
||||
"|he walks to xyz={2,2,2}, then he walks to xyz={3,3,3}."
|
||||
"|The animation queue now contains three animation steps:"
|
||||
"|"
|
||||
"|Animation Queue:"
|
||||
"| Step 1: action:initialize xyz={1,1,1} ..."
|
||||
"| Step 2: action:walkto xyz={2,2,2} ..."
|
||||
"| Step 3: action:walkto xyz={3,3,3} ..."
|
||||
"|"
|
||||
"|Notice that the state variable xyz is stored three times, once"
|
||||
"|in each animation step. All animation state variables are stored"
|
||||
"|in every animation step. When you use tangible.animstate to fetch"
|
||||
"|the current value of the animation state variables, you're"
|
||||
"|actually fetching the state variables from the last step in"
|
||||
"|the animation queue."
|
||||
"|"
|
||||
"|You can use this function, tangible.animstate, to view the"
|
||||
"|current set of state variables. You can use tangible.animinit"
|
||||
"|to reconfigure the set of user-defined state variables. You"
|
||||
"|can use tangible.animate to create animation steps, which can"
|
||||
"|alter any of the state variables."
|
||||
"|") {
|
||||
LuaArg tanobj;
|
||||
LuaRet result;
|
||||
LuaDefStack LS(L, tanobj, result);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
AnimState state = tan->anim_queue_.get_final_persistent();
|
||||
state.to_lua(LS, result, true);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_animinit, "tan,config",
|
||||
"|Reinitialize the animation queue."
|
||||
"|"
|
||||
"|The animation queue stores certain animation state variables."
|
||||
"|See doc(tangible.animstate) for more information about animation"
|
||||
"|state variables."
|
||||
"|"
|
||||
"|This function, tangible.animinit, is used to reconfigure the set of"
|
||||
"|user-defined state variables. The config table that you pass in must"
|
||||
"|be key-value pairs. Keys must be simple identifiers. Values can be"
|
||||
"|numbers, strings, booleans, or coordinates."
|
||||
"|"
|
||||
"|After tangible.animinit, the tangible's animation state variables will"
|
||||
"|consist of the user-defined variables listed in the config table,"
|
||||
"|plus all the builtin animation state variables."
|
||||
"|"
|
||||
"|Optionally, the config table can also supply values for some or all"
|
||||
"|of the builtin state variables. For example, you could supply xyz"
|
||||
"|in the config table. If you do, the tangible will move to a new xyz"
|
||||
"|coordinate. If not, the tangible will retain its old xyz coordinate."
|
||||
"|") {
|
||||
LuaArg tanobj, config;
|
||||
LuaDefStack LS(L, tanobj, config);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
AnimState state;
|
||||
eng::string error = state.apply_lua(LS, config, true);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
}
|
||||
tan->anim_queue_.add(id, step);
|
||||
AnimState defsource = tan->anim_queue_.get_final_persistent();
|
||||
error = state.add_defaults(&defsource);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
}
|
||||
tan->anim_queue_.clear(state);
|
||||
tan->update_plane_item();
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
|
||||
LuaDefine(tangible_animate, "tan,config",
|
||||
"|Add an animation step to the tangible."
|
||||
"|"
|
||||
"|The animation queue stores animation steps. This function, "
|
||||
"|tangible.animate, adds one new animation step to the queue."
|
||||
"|"
|
||||
"|It might be useful to read about 'animation state variables' before"
|
||||
"|reading this explanation. See doc(tangible.animstate)."
|
||||
"|"
|
||||
"|An animation step is just a list of key-value pairs. Therefore,"
|
||||
"|the config table must be key-value pairs. Keys must be simple"
|
||||
"|identifiers. Values can be numbers, strings, booleans, or"
|
||||
"|coordinates."
|
||||
"|"
|
||||
"|Some of the key-value pairs may match the name of an animation state"
|
||||
"|variable. If so, that key-value pair permanently changes the"
|
||||
"|value of that animation state variable. The new value will"
|
||||
"|be retained for all future animation steps."
|
||||
"|"
|
||||
"|Some of the key-value pairs may NOT match the name of any animation"
|
||||
"|state variable. If so, that key-value pair is part of the"
|
||||
"|animation step, but nothing is propagated forward to future animation"
|
||||
"|steps."
|
||||
"|"
|
||||
"|") {
|
||||
LuaArg tanobj, config;
|
||||
LuaDefStack LS(L, tanobj, config);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, tanobj, false);
|
||||
AnimState state = tan->anim_queue_.get_final_persistent();
|
||||
eng::string error = state.apply_lua(LS, config, false);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
}
|
||||
tan->anim_queue_.add(state);
|
||||
tan->update_plane_item();
|
||||
return LS.result();
|
||||
}
|
||||
@@ -132,51 +245,53 @@ LuaDefine(tangible_delete, "tan",
|
||||
|
||||
LuaDefine(tangible_build, "config",
|
||||
"|Build a new tangible object."
|
||||
"|The config table must contain: class,x,y,z,plane,graphic."
|
||||
"|The config table can optionally contain: facing.") {
|
||||
"|"
|
||||
"|The config table must contain: class,animstate."
|
||||
"|" ){
|
||||
LuaArg config;
|
||||
LuaVar classname, classtab, mt;
|
||||
LuaVar classname, classtab, mt, animstate;
|
||||
LuaRet database;
|
||||
LuaDefStack LS(L, config, classname, classtab, database, mt);
|
||||
LuaDefStack LS(L, config, classname, classtab, database, mt, animstate);
|
||||
LuaKeywordParser kp(LS, config);
|
||||
|
||||
// Get the class of the new tangible.
|
||||
// Get the keyword arguments.
|
||||
if (!kp.parse(classname, "class")) {
|
||||
luaL_error(L, "You must specify a class for the tangible");
|
||||
}
|
||||
if (!kp.parse(animstate, "animstate")) {
|
||||
luaL_error(L, "You must specify an animstate table");
|
||||
}
|
||||
kp.final_check_throw();
|
||||
|
||||
// Find the class.
|
||||
eng::string err = LS.getclass(classtab, classname);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
}
|
||||
|
||||
// Parse the initial animation step.
|
||||
AnimStep initstep, blank;
|
||||
initstep.configure(kp, blank);
|
||||
kp.final_check_throw();
|
||||
if (!initstep.has_xyz()) {
|
||||
luaL_error(L, "You must specify (X,Y,Z) for new tangible");
|
||||
// Calculate the initial animation state.
|
||||
AnimState state;
|
||||
err = state.apply_lua(LS, animstate, true);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
}
|
||||
if (!initstep.has_plane()) {
|
||||
luaL_error(L, "You must specify plane for new tangible");
|
||||
if (!state.contains("xyz") || !state.contains("plane")) {
|
||||
luaL_error(L, "You must specify both xyz and plane in animstate");
|
||||
}
|
||||
if (!initstep.has_graphic()) {
|
||||
luaL_error(L, "You must specify graphic for new tangible");
|
||||
err = state.add_defaults(nullptr);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
}
|
||||
|
||||
// TODO: generate error if there's extra crap in the config table.
|
||||
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
int64_t new_id = w->alloc_id_predictable();
|
||||
Tangible *tan = w->tangible_make(LS, database, new_id, "nowhere");
|
||||
Tangible *tan = w->tangible_make(LS, database, new_id);
|
||||
|
||||
// Update the class of the new tangible.
|
||||
LS.getmetatable(mt, database);
|
||||
LS.rawset(mt, "__index", classtab);
|
||||
|
||||
// Update the animation queue and planemap of the new tangible.
|
||||
int64_t stepid = w->alloc_id_predictable();
|
||||
tan->anim_queue_.add(stepid, initstep);
|
||||
tan->update_plane_item();
|
||||
tan->anim_queue_.clear(state);
|
||||
|
||||
return LS.result();
|
||||
}
|
||||
@@ -259,34 +374,27 @@ LuaDefine(tangible_place, "",
|
||||
}
|
||||
|
||||
LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self",
|
||||
"|Scan near the specified tangible."
|
||||
"|If omit_nowhere is true, and the tangible is on the nowhere plane,"
|
||||
"|then the scan returns empty. If omit_self is true, then the "
|
||||
"|tangible passed in is omitted from the results.") {
|
||||
"|Deprecated. Use tangible.find instead.") {
|
||||
LuaArg ltan, lradius, lomit_nowhere, lomit_self;
|
||||
LuaRet list;
|
||||
LuaDefStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, ltan, false);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
|
||||
PlaneScan scan;
|
||||
scan.set_plane(aqback.plane());
|
||||
scan.set_bbox_given_center_radius(aqback.xyz(), LS.cknumber(lradius));
|
||||
scan.set_radius(LS.cknumber(lradius));
|
||||
scan.set_shape(PlaneScan::SPHERE);
|
||||
scan.set_sorted(true);
|
||||
scan.set_near(tan->id(), !LS.ckboolean(lomit_self));
|
||||
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
||||
|
||||
util::IdVector idv = w->plane_map_.scan(scan);
|
||||
util::IdVector idv;
|
||||
w->get_near(scan, &idv);
|
||||
tangible_getall(LS, list, idv);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
||||
"|Scan the specified plane."
|
||||
"|If omit_nowhere is true, and the plane is 'nowhere', then"
|
||||
"|the scan returns empty.") {
|
||||
"|Deprecated. Use tangible.find instead.") {
|
||||
LuaArg lplane, lx, ly, lradius, lomit_nowhere;
|
||||
LuaRet list;
|
||||
LuaDefStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list);
|
||||
@@ -294,12 +402,13 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
||||
|
||||
PlaneScan scan;
|
||||
scan.set_plane(LS.ckstring(lplane));
|
||||
scan.set_bbox_given_center_radius(util::XYZ(LS.cknumber(lx), LS.cknumber(ly), 0), LS.cknumber(lradius));
|
||||
scan.set_center_and_radius(util::XYZ(LS.cknumber(lx), LS.cknumber(ly), 0), LS.cknumber(lradius));
|
||||
scan.set_shape(PlaneScan::SPHERE);
|
||||
scan.set_sorted(true);
|
||||
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
||||
|
||||
util::IdVector idv = w->plane_map_.scan(scan);
|
||||
util::IdVector idv;
|
||||
w->get_near(scan, &idv);
|
||||
tangible_getall(LS, list, idv);
|
||||
return LS.result();
|
||||
}
|
||||
@@ -369,21 +478,9 @@ LuaDefine(tangible_find, "config",
|
||||
scan.configure(kw);
|
||||
kw.final_check_throw();
|
||||
|
||||
// When the configure routine sees the 'near' flag, it stores the tangible
|
||||
// ID, but not the center and plane, because doing so would require it to
|
||||
// know about world models. We have to handle center and plane for 'near'
|
||||
// separately.
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
int64_t near = scan.near();
|
||||
if (near != 0) {
|
||||
Tangible *t = w->tangible_get(near);
|
||||
assert(t != nullptr); // Should never happen.
|
||||
const AnimStep &aqback = t->anim_queue_.back();
|
||||
scan.set_plane(aqback.plane());
|
||||
scan.set_center(aqback.xyz());
|
||||
}
|
||||
// Do the scan.
|
||||
util::IdVector idv = w->plane_map_.scan(scan);
|
||||
util::IdVector idv;
|
||||
w->get_near(scan, &idv);
|
||||
tangible_getall(LS, result, idv);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -77,14 +77,14 @@ World::World(WorldType wt) {
|
||||
assign_seqno_ = 1;
|
||||
}
|
||||
|
||||
Tangible::Tangible(World *w, int64_t id) : world_(w), anim_queue_(w->world_type_), id_player_pool_(&w->id_global_pool_) {
|
||||
Tangible::Tangible(World *w, int64_t id) : world_(w), anim_queue_(), id_player_pool_(&w->id_global_pool_) {
|
||||
plane_item_.set_id(id);
|
||||
plane_item_.track(&w->plane_map_);
|
||||
}
|
||||
|
||||
void Tangible::update_plane_item() {
|
||||
const AnimStep &aqback = anim_queue_.back();
|
||||
plane_item_.set_pos(aqback.plane(), aqback.xyz().x, aqback.xyz().y, aqback.xyz().z);
|
||||
AnimCoreState pos = anim_queue_.get_final_core_state();
|
||||
plane_item_.set_pos(pos.plane, pos.xyz.x, pos.xyz.y, pos.xyz.z);
|
||||
}
|
||||
|
||||
void Tangible::serialize(StreamBuffer *sb) {
|
||||
@@ -140,7 +140,7 @@ Tangible *World::tangible_get(const LuaCoreStack &LS, LuaSlot tab, bool allowdel
|
||||
return result;
|
||||
}
|
||||
|
||||
Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_t id, const eng::string &plane) {
|
||||
Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_t id) {
|
||||
assert(id != 0);
|
||||
LuaVar metatab;
|
||||
LuaExtStack LS(LS0.state(), metatab);
|
||||
@@ -150,8 +150,10 @@ Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_
|
||||
assert (t == nullptr);
|
||||
t.reset(new Tangible(this, id));
|
||||
|
||||
// Set up initial animation state.
|
||||
t->anim_queue_.clear(plane);
|
||||
// AnimQueue initializes itself to a valid default state.
|
||||
AnimState state;
|
||||
state.add_defaults(nullptr);
|
||||
t->anim_queue_.clear(state);
|
||||
t->update_plane_item();
|
||||
|
||||
// Fetch the tangible's Lua database and metatable.
|
||||
@@ -165,10 +167,10 @@ Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_
|
||||
return t.get();
|
||||
}
|
||||
|
||||
Tangible *World::tangible_make(int64_t id, const eng::string &plane) {
|
||||
Tangible *World::tangible_make(int64_t id) {
|
||||
LuaVar database;
|
||||
LuaExtStack LS(state(), database);
|
||||
return tangible_make(LS, database, id, plane);
|
||||
return tangible_make(LS, database, id);
|
||||
}
|
||||
|
||||
void World::tangible_delete(int64_t id) {
|
||||
@@ -199,23 +201,33 @@ void World::tangible_delete(int64_t id) {
|
||||
tangibles_.erase(iter);
|
||||
}
|
||||
|
||||
util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted) const {
|
||||
const Tangible *player = tangible_get(player_id);
|
||||
void World::get_near(PlaneScan &scan, util::IdVector *into) const {
|
||||
uint32_t hash1 = eng::memhash();
|
||||
into->clear();
|
||||
// If 'near' is set, update the plane and center.
|
||||
int64_t actor_id = scan.near();
|
||||
if (actor_id != 0) {
|
||||
const Tangible *player = tangible_get(actor_id);
|
||||
if (player == nullptr) {
|
||||
return IdVector();
|
||||
return;
|
||||
}
|
||||
const PlaneItem &pi = player->plane_item_;
|
||||
scan.set_plane(pi.plane());
|
||||
scan.set_center(util::XYZ(pi.x(), pi.y(), pi.z()));
|
||||
}
|
||||
plane_map_.scan(scan, into);
|
||||
uint32_t hash2 = eng::memhash();
|
||||
assert(hash1 == hash2);
|
||||
}
|
||||
|
||||
// Find out where the player is.
|
||||
const AnimStep &aqback = player->anim_queue_.back();
|
||||
|
||||
void World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted, util::IdVector *into) const {
|
||||
PlaneScan scan;
|
||||
scan.set_plane(aqback.plane());
|
||||
scan.set_bbox_given_center_radius(aqback.xyz(), radius);
|
||||
scan.set_radius(radius);
|
||||
scan.set_shape(PlaneScan::SPHERE);
|
||||
scan.set_sorted(sorted);
|
||||
scan.set_omit_nowhere(exclude_nowhere);
|
||||
scan.set_near(player_id, !omit_player);
|
||||
return plane_map_.scan(scan);
|
||||
get_near(scan, into);
|
||||
}
|
||||
|
||||
World::Redirects World::fetch_redirects() {
|
||||
@@ -229,7 +241,7 @@ int64_t World::create_login_actor() {
|
||||
int64_t id = id_global_pool_.get_one();
|
||||
LuaVar database, classtab, mt;
|
||||
LuaExtStack LS(state(), database, classtab, mt);
|
||||
Tangible *tan = tangible_make(LS, database, id, "nowhere");
|
||||
Tangible *tan = tangible_make(LS, database, id);
|
||||
LS.makeclass(classtab, "login");
|
||||
LS.getmetatable(mt, database);
|
||||
LS.rawset(mt, "__index", classtab);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
#include "serializelua.hpp"
|
||||
|
||||
util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
|
||||
return util::sort_union_id_vectors(
|
||||
master->get_near(actor_id, RadiusVisibility, true, false, false),
|
||||
get_near(actor_id, RadiusVisibility, true, false, false));
|
||||
util::IdVector v1, v2;
|
||||
master->get_near(actor_id, RadiusVisibility, true, false, false, &v1);
|
||||
get_near(actor_id, RadiusVisibility, true, false, false, &v2);
|
||||
return util::sort_union_id_vectors(v1, v2);
|
||||
}
|
||||
|
||||
int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
@@ -14,7 +15,7 @@ int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
Tangible *s_actor = tangible_get(actor_id);
|
||||
if (s_actor == nullptr) {
|
||||
DebugLine(dbc) << "create new actor " << actor_id;
|
||||
s_actor = tangible_make(actor_id, "");
|
||||
s_actor = tangible_make(actor_id);
|
||||
s_actor->id_player_pool_.deserialize(sb);
|
||||
s_actor->anim_queue_.deserialize(sb);
|
||||
s_actor->print_buffer_.deserialize(sb);
|
||||
@@ -61,7 +62,7 @@ void World::patch_visible(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int64_t id = sb->read_int64();
|
||||
DebugLine(dbc) << "patch_visible create tan " << id;
|
||||
Tangible *t = tangible_make(id, "");
|
||||
Tangible *t = tangible_make(id);
|
||||
t->anim_queue_.deserialize(sb);
|
||||
t->update_plane_item();
|
||||
}
|
||||
@@ -162,7 +163,6 @@ void World::diff_visible(const util::IdVector &visible, World *master,
|
||||
s_tan = tangible_get(m_tan->id());
|
||||
assert(s_tan != nullptr);
|
||||
}
|
||||
s_tan->anim_queue_.update_version(m_tan->anim_queue_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,8 +172,8 @@ void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
int64_t actor_id = sb->read_int64();
|
||||
util::HashValue closehash = sb->read_hashvalue();
|
||||
int ncreate = sb->read_int32();
|
||||
util::IdVector closetans =
|
||||
get_near(actor_id, RadiusClose, true, false, true);
|
||||
util::IdVector closetans;
|
||||
get_near(actor_id, RadiusClose, true, false, true, &closetans);
|
||||
assert(closehash == util::hash_id_vector(closetans));
|
||||
number_lua_tables(closetans);
|
||||
create_new_tables(ncreate);
|
||||
@@ -188,22 +188,23 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
|
||||
StreamBuffer tsb;
|
||||
|
||||
// Calculate the set of close tangibles.
|
||||
util::IdVector closetans =
|
||||
master->get_near(actor_id, RadiusClose, true, false, true);
|
||||
assert(get_near(actor_id, RadiusClose, true, false, true) == closetans);
|
||||
util::HashValue closehash = util::hash_id_vector(closetans);
|
||||
util::IdVector mclosetans, sclosetans;
|
||||
master->get_near(actor_id, RadiusClose, true, false, true, &mclosetans);
|
||||
get_near(actor_id, RadiusClose, true, false, true, &sclosetans);
|
||||
assert(mclosetans == sclosetans);
|
||||
util::HashValue closehash = util::hash_id_vector(mclosetans);
|
||||
|
||||
// Number and pair tables in the synchronous and master model.
|
||||
number_lua_tables(closetans);
|
||||
pair_lua_tables(closetans, master->state());
|
||||
int ncreate = number_remaining_tables(closetans, master->state());
|
||||
number_lua_tables(mclosetans);
|
||||
pair_lua_tables(mclosetans, master->state());
|
||||
int ncreate = number_remaining_tables(mclosetans, master->state());
|
||||
create_new_tables(ncreate);
|
||||
|
||||
// Difference transmit.
|
||||
tsb.write_int64(actor_id);
|
||||
tsb.write_hashvalue(closehash);
|
||||
tsb.write_int32(ncreate);
|
||||
diff_tangible_databases(closetans, master->state(), &tsb);
|
||||
diff_tangible_databases(mclosetans, master->state(), &tsb);
|
||||
diff_numbered_tables(master->state(), &tsb);
|
||||
|
||||
// Forward to client, and apply to server-synchronous.
|
||||
@@ -257,8 +258,8 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
|
||||
// Calculate the set of close tangibles.
|
||||
// TODO: we've already calculated this in an earlier function. This is
|
||||
// wasteful.
|
||||
util::IdVector closetans =
|
||||
master->get_near(actor_id, RadiusClose, true, false, true);
|
||||
util::IdVector closetans;
|
||||
master->get_near(actor_id, RadiusClose, true, false, true, &closetans);
|
||||
|
||||
tsb.write_int32(0);
|
||||
int write_count_after = tsb.total_writes();
|
||||
|
||||
@@ -3,18 +3,37 @@
|
||||
#include "json.hpp"
|
||||
#include <cassert>
|
||||
|
||||
void World::tangible_walkto(int64_t id, int64_t animid, float x, float y) {
|
||||
|
||||
void World::tangible_clear_anim_queue_to_empty(int64_t id) {
|
||||
Tangible *t = tangible_get(id);
|
||||
assert(animid != 0);
|
||||
assert(t != nullptr);
|
||||
AnimStep step;
|
||||
step.set_action("walkto");
|
||||
step.set_x(x);
|
||||
step.set_y(y);
|
||||
t->anim_queue_.add(animid, step);
|
||||
AnimState state;
|
||||
t->anim_queue_.clear(state);
|
||||
t->update_plane_item();
|
||||
}
|
||||
|
||||
|
||||
void World::tangible_clear_plane_and_xyz(int64_t id, const eng::string &plane, const util::DXYZ &xyz) {
|
||||
Tangible *t = tangible_get(id);
|
||||
assert(t != nullptr);
|
||||
AnimState state;
|
||||
state.set_string("plane", plane);
|
||||
state.set_xyz("xyz", xyz);
|
||||
state.set_persistent("plane");
|
||||
state.set_persistent("xyz");
|
||||
t->anim_queue_.clear(state);
|
||||
t->update_plane_item();
|
||||
}
|
||||
|
||||
void World::tangible_walkto(int64_t id, float x, float y) {
|
||||
Tangible *t = tangible_get(id);
|
||||
assert(t != nullptr);
|
||||
AnimState state = t->anim_queue_.get_final_persistent();
|
||||
state.set_string("action", "walkto");
|
||||
state.set_xyz("xyz", util::DXYZ(x,y,0.0));
|
||||
t->anim_queue_.add(state);
|
||||
}
|
||||
|
||||
eng::string World::tangible_anim_debug_string(int64_t id) const {
|
||||
const Tangible *t = tangible_get(id);
|
||||
if (t == 0) return "no such tangible";
|
||||
@@ -40,10 +59,12 @@ eng::string World::tangible_ids_debug_string() const {
|
||||
|
||||
eng::string World::tangibles_near_debug_string(int64_t actor, int64_t distance) {
|
||||
eng::ostringstream result;
|
||||
for (int64_t id : get_near(actor, distance, true, false, true)) {
|
||||
util::IdVector tans;
|
||||
get_near(actor, distance, true, false, true, &tans);
|
||||
for (int64_t id : tans) {
|
||||
const Tangible *tan = tangible_get(id);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
result << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl;
|
||||
AnimState state = tan->anim_queue_.get_final_everything();
|
||||
result << id << ": " << state.debug_string() << std::endl;
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
@@ -267,53 +288,35 @@ LuaDefine(unittests_world1animdiff, "", "some unit tests") {
|
||||
util::IdVector ids = util::id_vector_create(123, 345);
|
||||
|
||||
// Create some tangibles, and add some animations.
|
||||
m->tangible_make(123, "somewhere");
|
||||
m->tangible_make(345, "somewhere");
|
||||
m->tangible_walkto(123, 770, 3, 4);
|
||||
m->tangible_walkto(345, 771, 6, 2);
|
||||
m->tangible_make(123);
|
||||
m->tangible_make(345);
|
||||
m->tangible_clear_anim_queue_to_empty(123);
|
||||
m->tangible_clear_anim_queue_to_empty(345);
|
||||
m->tangible_walkto(123, 3, 4);
|
||||
m->tangible_walkto(345, 6, 2);
|
||||
LuaAssertStrEq(L, m->tangible_ids_debug_string(), "123,345");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(123),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=770 action=walkto x=3 y=4; ");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(345),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=771 action=walkto x=6 y=2; ");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(123), "[empty]; action:walkto xyz:3,4,0");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(345), "[empty]; action:walkto xyz:6,2,0");
|
||||
|
||||
// Now difference transmit all that to the client.
|
||||
ss->diff_visible(ids, m.get(), &sb);
|
||||
cs->patch_visible(&sb, nullptr);
|
||||
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123,345");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=770 action=walkto x=3 y=4; ");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=771 action=walkto x=6 y=2; ");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123), "[empty]; action:walkto xyz:3,4,0");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345), "[empty]; action:walkto xyz:6,2,0");
|
||||
LuaAssert(L, worlds_identical(ss, cs));
|
||||
|
||||
// Now add some more animation records to the master.
|
||||
m->tangible_walkto(123, 772, 7, 3);
|
||||
m->tangible_walkto(345, 773, 2, 5);
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(123),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=770 action=walkto x=3 y=4; "
|
||||
"id=772 action=walkto x=7 y=3; ");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(345),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=771 action=walkto x=6 y=2; "
|
||||
"id=773 action=walkto x=2 y=5; ");
|
||||
m->tangible_walkto(123, 7, 3);
|
||||
m->tangible_walkto(345, 2, 5);
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(123), "[empty]; action:walkto xyz:3,4,0; action:walkto xyz:7,3,0");
|
||||
LuaAssertStrEq(L, m->tangible_anim_debug_string(345), "[empty]; action:walkto xyz:6,2,0; action:walkto xyz:2,5,0");
|
||||
|
||||
// Now difference transmit all that to the client again.
|
||||
ss->diff_visible(ids, m.get(), &sb);
|
||||
cs->patch_visible(&sb, nullptr);
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=770 action=walkto x=3 y=4; "
|
||||
"id=772 action=walkto x=7 y=3; ");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345),
|
||||
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
|
||||
"id=771 action=walkto x=6 y=2; "
|
||||
"id=773 action=walkto x=2 y=5; ");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123), "[empty]; action:walkto xyz:3,4,0; action:walkto xyz:7,3,0");
|
||||
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345), "[empty]; action:walkto xyz:6,2,0; action:walkto xyz:2,5,0");
|
||||
LuaAssert(L, worlds_identical(ss, cs));
|
||||
|
||||
// Delete tangible 345.
|
||||
@@ -337,7 +340,7 @@ LuaDefine(unittests_world2pairtab, "", "some unit tests") {
|
||||
|
||||
// Create a master model containing some general tables, and
|
||||
// some specialty tables (not numberable).
|
||||
m->tangible_make(123, "somewhere");
|
||||
m->tangible_make(123);
|
||||
m->tangible_set_string(123, "inventory.TID", "inventory");
|
||||
m->tangible_set_string(123, "transactions.TID", "transactions");
|
||||
m->tangible_set_string(123, "skills.TID", "skills");
|
||||
@@ -348,7 +351,7 @@ LuaDefine(unittests_world2pairtab, "", "some unit tests") {
|
||||
|
||||
// Now we're going to create a synchronous model that's similar to, but not
|
||||
// exactly the same as that master model.
|
||||
ss->tangible_make(123, "somewhere");
|
||||
ss->tangible_make(123);
|
||||
ss->tangible_set_string(123, "inventory.TID", "inventory");
|
||||
ss->tangible_set_string(123, "skills.TID", "skills");
|
||||
ss->tangible_set_string(123, "skills.crap.TID", "skills.crap");
|
||||
@@ -384,12 +387,19 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
|
||||
StreamBuffer sb;
|
||||
|
||||
// Initialize all three models so that a tangible exists.
|
||||
m->tangible_make(123, "somewhere");
|
||||
ss->tangible_make(123, "somewhere");
|
||||
cs->tangible_make(123, "somewhere");
|
||||
m->tangible_make(345, "somewhere");
|
||||
ss->tangible_make(345, "somewhere");
|
||||
cs->tangible_make(345, "somewhere");
|
||||
m->tangible_make(123);
|
||||
ss->tangible_make(123);
|
||||
cs->tangible_make(123);
|
||||
m->tangible_make(345);
|
||||
ss->tangible_make(345);
|
||||
cs->tangible_make(345);
|
||||
|
||||
m->tangible_clear_plane_and_xyz(123, "earth", util::DXYZ(0,0,0));
|
||||
ss->tangible_clear_plane_and_xyz(123, "earth", util::DXYZ(0,0,0));
|
||||
cs->tangible_clear_plane_and_xyz(123, "earth", util::DXYZ(0,0,0));
|
||||
m->tangible_clear_plane_and_xyz(345, "earth", util::DXYZ(0,0,0));
|
||||
ss->tangible_clear_plane_and_xyz(345, "earth", util::DXYZ(0,0,0));
|
||||
cs->tangible_clear_plane_and_xyz(345, "earth", util::DXYZ(0,0,0));
|
||||
|
||||
// Put some data into the master model.
|
||||
m->tangible_set_string(123, "bacon", "crispy");
|
||||
@@ -438,9 +448,9 @@ LuaDefine(unittests_world4difftanclass, "", "some unit tests") {
|
||||
StreamBuffer sb;
|
||||
|
||||
// Initialize all three models so that a tangible exists.
|
||||
m->tangible_make(123, "somewhere");
|
||||
ss->tangible_make(123, "somewhere");
|
||||
cs->tangible_make(123, "somewhere");
|
||||
m->tangible_make(123);
|
||||
ss->tangible_make(123);
|
||||
cs->tangible_make(123);
|
||||
|
||||
// Change the lua class of the tangible.
|
||||
m->tangible_set_class(123, "chicken");
|
||||
|
||||
@@ -125,7 +125,15 @@ public:
|
||||
// The unsorted version returns the tangibles in an unpredictable order. If sorted
|
||||
// is false, return them in an unpredictable order.
|
||||
//
|
||||
IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted) const;
|
||||
void get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted, IdVector *into) const;
|
||||
|
||||
// get_near
|
||||
//
|
||||
// Get a list of tangibles in any arbitrary region. This differs from
|
||||
// PlaneMap::scan in that if the scan specifies a 'near', then the plane
|
||||
// and center of the scan are updated from the near tangible.
|
||||
//
|
||||
void get_near(PlaneScan &sc, IdVector *into) const;
|
||||
|
||||
// Make a tangible.
|
||||
//
|
||||
@@ -133,8 +141,8 @@ public:
|
||||
// stack untouched. Returns a pointer to the C++ part of the tangible, and
|
||||
// optionally stores the Lua part in a stack slot.
|
||||
//
|
||||
Tangible *tangible_make(const LuaCoreStack &LS0, LuaSlot tan, int64_t id, const eng::string &plane);
|
||||
Tangible *tangible_make(int64_t id, const eng::string &plane);
|
||||
Tangible *tangible_make(const LuaCoreStack &LS0, LuaSlot tan, int64_t id);
|
||||
Tangible *tangible_make(int64_t id);
|
||||
|
||||
// Get a pointer to the specified tangible.
|
||||
//
|
||||
@@ -362,9 +370,17 @@ public:
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Clear the animation queue and remove all persistent state.
|
||||
//
|
||||
void tangible_clear_anim_queue_to_empty(int64_t id);
|
||||
|
||||
// Clear the animation queue to a reasonable starting position.
|
||||
//
|
||||
void tangible_clear_plane_and_xyz(int64_t id, const eng::string &plane, const util::DXYZ &xyz);
|
||||
|
||||
// Add a 'walkto' animation to the specified tangible.
|
||||
//
|
||||
void tangible_walkto(int64_t id, int64_t animid, float x, float y);
|
||||
void tangible_walkto(int64_t id, float x, float y);
|
||||
|
||||
// Get the tangible's animation queue as a debug string.
|
||||
//
|
||||
|
||||
@@ -6,7 +6,7 @@ function ug.the()
|
||||
|
||||
lis=tangible.scan('globals',0,0,0,true)
|
||||
if #lis==0 then
|
||||
local ugid=tangible.build{class='ug',x=0,y=0,z=0,plane='globals',graphic='box'}
|
||||
local ugid=tangible.build{class='ug', animstate={xyz={0,0,0}, plane='globals'}}
|
||||
print("The global table is "..tangible.id(ugid))
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user