Files
brandling-safari/shared/Protocol.cpp
Gabe 238cd9c6c3 Brandling Safari: LAN safari-zone creature catching on the HD-2D engine
- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass /
  water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon
- Client: title / join / character creator / play screens, prediction + reconciliation,
  interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection)
- Shared deterministic zone generator, movement, protocol, roster, appearance
- Paper-doll character art pipeline (PixelLab base walk + per-item animation edits)
- Brandling roster from the company list (logo colors, jev types), Blender safari props
- Headless test bot
2026-10-02 15:20:27 -05:00

144 lines
3.3 KiB
C++

#include "Protocol.h"
#include <algorithm>
namespace bs {
namespace {
const char* kWeatherNames[] = {"clear", "cloudy", "rain", "storm", "fog"};
}
const char* weatherName(Weather w) {
int i = int(w);
return i >= 0 && i < int(Weather::Count) ? kWeatherNames[i] : "clear";
}
bool parseWeather(const std::string& s, Weather& out) {
for (int i = 0; i < int(Weather::Count); ++i)
if (s == kWeatherNames[i]) {
out = Weather(i);
return true;
}
return false;
}
void writeAppearance(Writer& w, const Appearance& a) {
w.str(a.body);
w.u8(a.skin);
for (int s = 0; s < kSlotCount; ++s) {
w.str(a.items[s]);
w.u32(a.colors[s]);
}
}
Appearance readAppearance(Reader& r) {
Appearance a;
a.body = r.str();
a.skin = r.u8();
for (int s = 0; s < kSlotCount; ++s) {
a.items[s] = r.str();
a.colors[s] = r.u32();
}
return a;
}
void InputMsg::write(Writer& w) const {
size_t n = std::min<size_t>(cmds.size(), 4);
w.u8(uint8_t(n));
for (size_t i = cmds.size() - n; i < cmds.size(); ++i) {
w.u32(cmds[i].seq);
w.i8(cmds[i].moveX);
w.i8(cmds[i].moveZ);
w.u8(cmds[i].flags);
}
}
void InputMsg::read(Reader& r) {
uint8_t n = std::min<uint8_t>(r.u8(), 4);
cmds.resize(n);
for (InputCmd& c : cmds) {
c.seq = r.u32();
c.moveX = r.i8();
c.moveZ = r.i8();
c.flags = r.u8();
}
}
void ProfileMsg::write(Writer& w) const {
w.u32(entityId);
w.str(name);
writeAppearance(w, look);
w.i32(capsules);
w.u8(introDone);
w.u32(seen);
w.u32(caught);
w.u32(uint32_t(creatures.size()));
for (const OwnedCreature& c : creatures) {
w.u32(c.uid);
w.u16(c.species);
w.u8(c.inParty);
w.i64(c.caughtAt);
}
}
void ProfileMsg::read(Reader& r) {
entityId = r.u32();
name = r.str();
look = readAppearance(r);
capsules = r.i32();
introDone = r.u8();
seen = r.u32();
caught = r.u32();
uint32_t n = std::min<uint32_t>(r.u32(), 100000);
creatures.clear();
for (uint32_t i = 0; i < n && r.ok(); ++i) {
OwnedCreature c;
c.uid = r.u32();
c.species = r.u16();
c.inParty = r.u8();
c.caughtAt = r.i64();
creatures.push_back(c);
}
}
void SnapshotMsg::write(Writer& w) const {
w.u32(tick);
w.u32(ackInput);
w.f32(hour);
w.u8(uint8_t(weather));
w.f32(weatherBlend);
w.u16(uint16_t(entities.size()));
for (const EntityState& e : entities) {
w.u32(e.id);
w.u8(uint8_t(e.kind));
w.u8(uint8_t(e.anim));
w.u16(e.species);
w.u32(e.owner);
w.vec3(e.pos);
w.f32(e.yaw);
w.u8(e.order);
}
}
void SnapshotMsg::read(Reader& r) {
tick = r.u32();
ackInput = r.u32();
hour = r.f32();
weather = Weather(std::min<uint8_t>(r.u8(), uint8_t(Weather::Count) - 1));
weatherBlend = r.f32();
uint16_t n = r.u16();
entities.resize(n);
for (EntityState& e : entities) {
e.id = r.u32();
e.kind = EntityKind(r.u8());
e.anim = Anim(r.u8());
e.species = r.u16();
e.owner = r.u32();
e.pos = r.vec3();
e.yaw = r.f32();
e.order = r.u8();
}
}
} // namespace bs