mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-23 18:45:57 +02:00
- With events.tricks a plugin hears the local skater's tricks: trick_started (where), trick_landed (points, multiplier, where it started and landed, how long it took) and line_broken (a slam or a wipeout); reskate.score.current() is the score HUD's line, multiplier and whether a trick is going on. Without the permission the events cannot be registered. - The score HUD is read into the trick watch (Extension/Skater/score_hud.h, trick_watch.h) on the client tick only while a running plugin has events.tricks; a new level starts a new line. plugins::start takes the image base for it. - reskate-test has a tricks module (skipped until a trick lands) and ptest trick, which waits 30 s for one and prints what its events said; reskate-hostile has trick-loop. Not yet checked against real tricks in game.
110 lines
4.8 KiB
C++
110 lines
4.8 KiB
C++
#pragma once
|
|
#include "plugin_vm.h"
|
|
#include <filesystem>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
// Every plugin the game runs: which are loaded, reloaded when their files change, switched off
|
|
// with their mod, and stopped when they go over a limit. The host is what plugins share: the
|
|
// level being played and the console's command names. One thread drives it (the client tick).
|
|
namespace dingosdk::plugins {
|
|
// A mod folder that is a plugin, as discovery found it.
|
|
struct Candidate {
|
|
std::string id; // the mod's folder name
|
|
std::filesystem::path directory; // the mod folder
|
|
std::string fingerprint; // changes when any of its files does
|
|
std::string refusal; // why it may not load, from its files alone, or empty
|
|
bool operator==(const Candidate&) const = default;
|
|
};
|
|
|
|
// What the host needs from where it runs: the game, or the test runner.
|
|
class Environment {
|
|
public:
|
|
virtual ~Environment() = default;
|
|
virtual void log(LogLevel level, std::string_view text) = 0;
|
|
virtual void console(std::string_view text) = 0;
|
|
virtual double now_ms() = 0;
|
|
// <root>/<plugin id>/storage.json holds each plugin's storage.
|
|
virtual std::filesystem::path storage_root() = 0;
|
|
// Whether ReSkate has a console command starting with this word.
|
|
virtual bool builtin_command(std::string_view word) = 0;
|
|
// The game (Services, plugin_vm.h). Where there is none, there is no skater and nothing to hit.
|
|
virtual std::optional<SkaterState> skater() { return std::nullopt; }
|
|
virtual Teleport teleport(const Vec3&, float) { return Teleport::unavailable; }
|
|
virtual std::optional<Vec3> raycast(const Vec3&, const Vec3&) { return std::nullopt; }
|
|
virtual std::optional<ScoreState> score() { return std::nullopt; }
|
|
};
|
|
|
|
struct Status {
|
|
std::string id;
|
|
std::string state; // "running", "stopped", "switched off", "not loaded"
|
|
std::string reason; // why it is not running
|
|
std::size_t memory{};
|
|
double tick_ms{};
|
|
std::vector<std::pair<std::string, std::string>> commands;
|
|
};
|
|
|
|
class Host final : private Services {
|
|
public:
|
|
// A client host runs client plugins and those for both sides; a server host, server ones and both.
|
|
Host(Environment& environment, Side side, Limits limits = {});
|
|
~Host() override;
|
|
Host(const Host&) = delete;
|
|
Host& operator=(const Host&) = delete;
|
|
|
|
// The plugins discovery found now: new ones load, changed ones reload, missing ones unload.
|
|
void apply(const std::vector<Candidate>& found);
|
|
void tick(double dt_ms);
|
|
void set_level(std::string level);
|
|
// A trick of the local skater, to every plugin with events.tricks.
|
|
void trick(const TrickNews& news);
|
|
// Whether a running plugin has events.tricks: the score HUD is read only then.
|
|
bool wants_tricks() const;
|
|
// Runs a plugin's console command when the text starts with one; false when none matched.
|
|
bool run_console(std::string_view text);
|
|
std::vector<Status> status() const;
|
|
// The `plugin` console commands. Each returns what to print.
|
|
std::string reload(std::string_view id);
|
|
std::string switch_off(std::string_view id);
|
|
std::string switch_on(std::string_view id);
|
|
// Unloads every plugin (their unload handlers run).
|
|
void shutdown();
|
|
|
|
private:
|
|
struct Entry {
|
|
Candidate candidate;
|
|
std::unique_ptr<Plugin> plugin;
|
|
std::string failure; // why it is not running
|
|
bool held{}; // switched off with `plugin off` until `plugin on` or the game restarts
|
|
};
|
|
void load(Entry& entry);
|
|
void unload(Entry& entry);
|
|
// A plugin that stopped itself (a limit, its errors) is closed and its commands released.
|
|
void collect(Entry& entry);
|
|
void release_commands(std::string_view id);
|
|
Entry* find(std::string_view id);
|
|
|
|
void log(std::string_view plugin, LogLevel level, std::string_view text) override;
|
|
void console(std::string_view text) override;
|
|
double now_ms() override;
|
|
std::string level() override;
|
|
std::filesystem::path storage_file(std::string_view plugin) override;
|
|
std::string claim_command(std::string_view plugin, std::string_view name) override;
|
|
std::optional<SkaterState> skater() override { return environment_.skater(); }
|
|
Teleport teleport(const Vec3& position, float heading) override { return environment_.teleport(position, heading); }
|
|
std::optional<Vec3> raycast(const Vec3& from, const Vec3& to) override { return environment_.raycast(from, to); }
|
|
std::optional<ScoreState> score() override { return environment_.score(); }
|
|
|
|
Environment& environment_;
|
|
Side side_;
|
|
Limits limits_;
|
|
std::map<std::string, Entry, std::less<>> entries_;
|
|
std::map<std::string, std::string, std::less<>> commands_; // command name: plugin id
|
|
std::string level_;
|
|
double next_save_{};
|
|
};
|
|
}
|