commit
2007fb7edc
200 files changed
+27710
No files matched your search
+224
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include "bot.h"
|
||||
#include "input.h"
|
||||
#include "match.h"
|
||||
#include "rollback.h"
|
||||
#include "trinket.h"
|
||||
#include "weapon.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Game;
|
||||
|
||||
constexpr int INPUT_RING = 1024;
|
||||
constexpr int SNAPSHOT_RING = 64;
|
||||
// A member's connection has no ping to show (it's this screen's own, hosting).
|
||||
constexpr float PING_NONE = -2;
|
||||
|
||||
// A player's setup as they join online: their name, the colour they'd like (-1 for any), and their sandbox slots,
|
||||
// loadouts, levels and stacking, which their fighter starts with. Plain data, sent as it sits in memory.
|
||||
struct Profile
|
||||
{
|
||||
char name[PLAYER_NAME_MAX + 1] = {};
|
||||
int color = -1;
|
||||
int weaponSlots = SLOTS_DEFAULT;
|
||||
int trinketSlots = SLOTS_DEFAULT;
|
||||
int weaponLoadout[SLOT_MAX] = {};
|
||||
int trinketLoadout[SLOT_MAX] = {};
|
||||
int weaponSlotLevels[SLOT_MAX] = {};
|
||||
int trinketSlotLevels[SLOT_MAX] = {};
|
||||
int weaponItemLevels[WEAPON_COUNT] = {};
|
||||
int trinketItemLevels[TRINKET_COUNT] = {};
|
||||
};
|
||||
|
||||
enum SessionEventType
|
||||
{
|
||||
EVENT_JOIN,
|
||||
EVENT_LEAVE,
|
||||
// A member queues a game in the lobby's sandbox, with its rules, or stops queueing one.
|
||||
EVENT_QUEUE,
|
||||
EVENT_UNQUEUE
|
||||
};
|
||||
|
||||
// A change to who's playing, at a set tick, so every machine makes it at the same point in the game (and again when
|
||||
// rolling back over it). From is, for a join, the tick the newcomer's copy of the game starts at.
|
||||
struct SessionEvent
|
||||
{
|
||||
int tick = 0;
|
||||
int type = EVENT_JOIN;
|
||||
int member = -1;
|
||||
int from = 0;
|
||||
Profile profile;
|
||||
Rules rules;
|
||||
};
|
||||
|
||||
// The queued game being played, a second game beside the lobby's sandbox (see Session): its state between ticks
|
||||
// (while it isn't swapped into the game), its bots' map of it, which fighter is this screen's in it and the devices
|
||||
// that go with that, and the tick it started (each play of it, a fresh one).
|
||||
struct Arena
|
||||
{
|
||||
Snapshot state;
|
||||
Bot bot;
|
||||
int devices[MAX_FIGHTERS] = {};
|
||||
int local = 0;
|
||||
int serial = -1;
|
||||
int graphSerial = -1;
|
||||
bool isActive = false;
|
||||
};
|
||||
|
||||
// One member's controls by tick, in a ring that keeps the last INPUT_RING ticks. They're needed from start (when the
|
||||
// member's fighter appears) until until (when it goes). Contiguous is the last tick up to which every tick from start
|
||||
// on is known, so up to which the member's play is certain. Used is what a tick was
|
||||
// simulated with when the member's controls had to be guessed, to spot a wrong guess when the real ones come.
|
||||
struct InputLog
|
||||
{
|
||||
Controls controls[INPUT_RING];
|
||||
int ticks[INPUT_RING];
|
||||
Controls used[INPUT_RING];
|
||||
int usedTicks[INPUT_RING];
|
||||
int start = 0;
|
||||
int until = INT32_MAX;
|
||||
int contiguous = -1;
|
||||
int latest = -1;
|
||||
|
||||
InputLog();
|
||||
void restart(int from);
|
||||
bool has(int tick) const;
|
||||
bool put(int tick, const Controls& controls);
|
||||
Controls guess(int tick) const;
|
||||
};
|
||||
|
||||
// What this screen knows of another member: the last tick of this screen's inputs they've confirmed having, the first
|
||||
// one they need, whether to send them inputs at all yet, and the tick they last said they were on.
|
||||
struct SessionPeer
|
||||
{
|
||||
int ack = -1;
|
||||
int sendFrom = 0;
|
||||
bool isSending = false;
|
||||
int remoteTick = 0;
|
||||
};
|
||||
|
||||
// Online play as one game on every machine: rollback netcode. Each member's controls go to everyone, a few ticks
|
||||
// ahead of when they're used (the input delay); a member whose controls for a tick haven't arrived yet is guessed to
|
||||
// keep doing what they last did, and when the real ones turn out different, the game rewinds to that tick and plays
|
||||
// forward again (silently). A machine that gets too far ahead of what it knows, or of the others, waits.
|
||||
//
|
||||
// The lobby's host decides who comes and goes, and when: a newcomer gets the game's state as of a tick everyone's
|
||||
// controls are certain for, fast-forwards from there, and appears at a tick a moment ahead, on every machine at once.
|
||||
// Someone leaving is taken out at the tick after their last known controls. Checksums of certain ticks are compared,
|
||||
// to catch machines drifting apart.
|
||||
//
|
||||
// Lobby members can queue a game in the sandbox (see Match::queue_open). Once it's ready, it starts as a second game,
|
||||
// the arena, beside the sandbox: both step every tick on every machine, each with their own fighters, the players
|
||||
// in the arena standing faded in the sandbox meanwhile. This screen shows one of the two (the arena while it plays in
|
||||
// it, or watches it): that one is swapped into the game around this screen's own work, which otherwise always sees
|
||||
// the sandbox.
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
bool isActive = false;
|
||||
bool isJoined = false;
|
||||
bool isDesynced = false;
|
||||
// Set when what this screen shows changes between the sandbox and the arena, for the camera to jump.
|
||||
bool isViewChanged = false;
|
||||
int tick = 0;
|
||||
int delay = 2;
|
||||
int rollbacks = 0;
|
||||
int stalls = 0;
|
||||
|
||||
void host_start(Game& game);
|
||||
void join_start(Game& game);
|
||||
void stop(Game& game);
|
||||
void update(Game& game, Controls local);
|
||||
bool is_wanted(Game& game);
|
||||
Profile profile_get(Game& game);
|
||||
int fighter_get(Game& game, int member);
|
||||
std::string base_name_get(Game& game, int member);
|
||||
std::string name_get(Game& game, int member);
|
||||
void chat_send(Game& game, const std::string& text);
|
||||
void typing_send(Game& game, bool isTyping);
|
||||
void kick(Game& game, const std::string& name, const std::string& reason);
|
||||
int rename_wait_get();
|
||||
void queue_send(Game& game, const Rules& rules);
|
||||
void unqueue_send(Game& game);
|
||||
bool is_queued(Game& game);
|
||||
bool view_enter(Game& game);
|
||||
void view_leave(Game& game);
|
||||
bool is_arena_on();
|
||||
bool is_playing_arena(Game& game);
|
||||
bool is_watching();
|
||||
void watch_start(Game& game);
|
||||
void watch_stop(Game& game);
|
||||
void arena_leave(Game& game);
|
||||
const Arena& arena_get();
|
||||
std::string arena_names_get(Game& game);
|
||||
float ping_get(Game& game, int member);
|
||||
|
||||
private:
|
||||
std::map<int, InputLog> logs;
|
||||
std::map<int, SessionPeer> peers;
|
||||
std::vector<SessionEvent> events;
|
||||
std::vector<int> departed;
|
||||
Snapshot snapshots[SNAPSHOT_RING];
|
||||
Snapshot arenaSnapshots[SNAPSHOT_RING];
|
||||
int arenaSerials[SNAPSHOT_RING];
|
||||
Arena arena;
|
||||
// Whether this screen watches the arena (not being in it), whether the arena's swapped into the game now, which
|
||||
// one it showed last (to start afresh on a change), and the chat lines already said for things in the game (which
|
||||
// replays would otherwise say again).
|
||||
bool isWatching = false;
|
||||
bool isSwapped = false;
|
||||
int shownSerial = -1;
|
||||
std::set<std::string> announced;
|
||||
int snapshotTicks[SNAPSHOT_RING];
|
||||
Uint32 checksums[SNAPSHOT_RING];
|
||||
std::map<int, Uint32> remoteChecksums;
|
||||
int checksumSent = -1;
|
||||
int ownNext = 0;
|
||||
int ownStart = 0;
|
||||
int rollbackTo = -1;
|
||||
bool isRequested = false;
|
||||
// Every member's chosen name (cleaned), kept current by their renames, and the colour this screen last asked for.
|
||||
std::map<int, std::string> names;
|
||||
int sentColor = -1;
|
||||
// Names can change only every RENAME_WAIT, so a player can't dodge being kicked by renaming: when this screen's can
|
||||
// next, whether it's told its pending one is waiting, and when each other member's last rename came.
|
||||
Uint64 nextRename = 0;
|
||||
bool isRenameWaiting = false;
|
||||
std::map<int, Uint64> renamedAt;
|
||||
|
||||
void packets_take(Game& game);
|
||||
void packet_handle(Game& game, int from, const std::string& bytes, bool isReliable);
|
||||
void members_watch(Game& game);
|
||||
void join_grant(Game& game, int member, const Profile& profile);
|
||||
void leave_decide(Game& game, int member);
|
||||
void event_add(Game& game, const SessionEvent& event);
|
||||
void event_send(Game& game, int to, const SessionEvent& event, int inputsFrom, int inputsTo);
|
||||
void inputs_send(Game& game);
|
||||
void step(Game& game, bool isLive);
|
||||
void events_apply(Game& game, int at);
|
||||
bool is_needed(int member, int at);
|
||||
int confirmed_get(Game& game);
|
||||
void checksums_exchange(Game& game);
|
||||
void local_find(Game& game);
|
||||
void broadcast(Game& game, const std::string& bytes);
|
||||
void rename_send(Game& game, int to, bool isQuiet);
|
||||
void profile_watch(Game& game);
|
||||
void controls_give(Game& game, bool isArena);
|
||||
void arena_swap(Game& game);
|
||||
void arena_start(Game& game, int queuer);
|
||||
void arena_build(Game& game, const Rules& rules, const int* members, int count);
|
||||
void arena_after(Game& game);
|
||||
void arena_end(Game& game);
|
||||
void arena_locals_find(Game& game);
|
||||
void arena_save(Game& game, int slot);
|
||||
void arena_restore(Game& game, int slot);
|
||||
Uint32 checksum_full(Game& game);
|
||||
std::string state_full_save(Game& game);
|
||||
bool state_full_load(Game& game, const std::string& bytes);
|
||||
void announce(Game& game, const std::string& key, const std::string& text);
|
||||
void queue_decide(Game& game, int member, bool isOpen, const Rules& rules);
|
||||
};
|
||||
Reference in new issue
Block a user