Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-07-22 22:31:04 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-07-22 22:31:04 +1000
commitcb19eef701b5c9e27d0464795fffcf8a4d795a21 (patch)
tree5cfbdf49ebd7ac9539891eea850e244887d4c355
parent4fbd67daccfa69df6988bdf17c67ee3d5f3049c5 (diff)
Add character swapping (no GUI yet)
-rw-r--r--source/client/StarClientApplication.cpp8
-rw-r--r--source/core/CMakeLists.txt2
-rw-r--r--source/core/StarText.cpp89
-rw-r--r--source/core/StarText.hpp26
-rw-r--r--source/core/StarTime.cpp2
-rw-r--r--source/frontend/StarClientCommandProcessor.cpp17
-rw-r--r--source/frontend/StarClientCommandProcessor.hpp1
-rw-r--r--source/frontend/StarInventory.cpp13
-rw-r--r--source/frontend/StarInventory.hpp1
-rw-r--r--source/frontend/StarTeamBar.cpp6
-rw-r--r--source/game/StarClientContext.cpp7
-rw-r--r--source/game/StarClientContext.hpp7
-rw-r--r--source/game/StarEntityMap.cpp19
-rw-r--r--source/game/StarEntityMap.hpp4
-rw-r--r--source/game/StarPlayer.cpp84
-rw-r--r--source/game/StarPlayer.hpp11
-rw-r--r--source/game/StarPlayerInventory.cpp16
-rw-r--r--source/game/StarPlayerStorage.cpp44
-rw-r--r--source/game/StarPlayerStorage.hpp8
-rw-r--r--source/game/StarQuestManager.cpp2
-rw-r--r--source/game/StarSystemWorld.hpp1
-rw-r--r--source/game/StarSystemWorldClient.cpp4
-rw-r--r--source/game/StarSystemWorldClient.hpp1
-rw-r--r--source/game/StarSystemWorldServer.cpp4
-rw-r--r--source/game/StarSystemWorldServer.hpp1
-rw-r--r--source/game/StarUniverseClient.cpp88
-rw-r--r--source/game/StarUniverseClient.hpp13
-rw-r--r--source/game/StarWorldClient.cpp70
-rw-r--r--source/game/StarWorldClient.hpp4
-rw-r--r--source/game/items/StarUnlockItem.cpp2
-rw-r--r--source/game/scripting/StarPlayerLuaBindings.cpp7
-rw-r--r--source/rendering/StarTextPainter.cpp89
-rw-r--r--source/rendering/StarTextPainter.hpp11
-rw-r--r--source/windowing/StarItemGridWidget.cpp7
-rw-r--r--source/windowing/StarWidget.hpp1
35 files changed, 476 insertions, 194 deletions
diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp
index 460c17b..5483591 100644
--- a/source/client/StarClientApplication.cpp
+++ b/source/client/StarClientApplication.cpp
@@ -506,9 +506,17 @@ void ClientApplication::changeState(MainAppState newState) {
m_playerStorage = make_shared<PlayerStorage>(m_root->toStoragePath("player"));
m_statistics = make_shared<Statistics>(m_root->toStoragePath("player"), appController()->statisticsService());
m_universeClient = make_shared<UniverseClient>(m_playerStorage, m_statistics);
+
m_universeClient->setLuaCallbacks("input", LuaBindings::makeInputCallbacks());
m_universeClient->setLuaCallbacks("voice", LuaBindings::makeVoiceCallbacks());
+ m_universeClient->playerReloadCallback() = [&]() {
+ if (auto paneManager = m_mainInterface->paneManager()) {
+ if (auto inventory = paneManager->registeredPane<InventoryPane>(MainInterfacePanes::Inventory))
+ inventory->clearChangedSlots();
+ }
+ };
+
m_mainMixer->setUniverseClient(m_universeClient);
m_titleScreen = make_shared<TitleScreen>(m_playerStorage, m_mainMixer->mixer());
if (auto renderer = Application::renderer())
diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt
index 1fd2aac..8c2964e 100644
--- a/source/core/CMakeLists.txt
+++ b/source/core/CMakeLists.txt
@@ -112,6 +112,7 @@ SET (star_core_HEADERS
StarStringView.hpp
StarStrongTypedef.hpp
StarTcp.hpp
+ StarText.hpp
StarThread.hpp
StarTickRateMonitor.hpp
StarTime.hpp
@@ -171,6 +172,7 @@ SET (star_core_SOURCES
StarString.cpp
StarStringView.cpp
StarTcp.cpp
+ StarText.cpp
StarThread.cpp
StarTime.cpp
StarTickRateMonitor.cpp
diff --git a/source/core/StarText.cpp b/source/core/StarText.cpp
new file mode 100644
index 0000000..11fca27
--- /dev/null
+++ b/source/core/StarText.cpp
@@ -0,0 +1,89 @@
+#include "StarText.hpp"
+
+#include <regex>
+
+namespace Star {
+
+namespace Text {
+ static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc));
+ String stripEscapeCodes(String const& s) {
+ return std::regex_replace(s.utf8(), stripEscapeRegex, "");
+ }
+
+ static std::string escapeChars = strf("{:c}{:c}", CmdEsc, StartEsc);
+
+ bool processText(StringView text, TextCallback textFunc, CommandsCallback commandsFunc, bool includeCommandSides) {
+ std::string_view escChars(escapeChars);
+
+ std::string_view str = text.utf8();
+ while (true) {
+ size_t escape = str.find_first_of(escChars);
+ if (escape != NPos) {
+ escape = str.find_first_not_of(escChars, escape) - 1; // jump to the last ^
+
+ size_t end = str.find_first_of(EndEsc, escape);
+ if (end != NPos) {
+ if (escape && !textFunc(str.substr(0, escape)))
+ return false;
+ if (commandsFunc) {
+ StringView commands = includeCommandSides
+ ? str.substr(escape, end - escape + 1)
+ : str.substr(escape + 1, end - escape - 1);
+ if (!commands.empty() && !commandsFunc(commands))
+ return false;
+ }
+ str = str.substr(end + 1);
+ continue;
+ }
+ }
+
+ if (!str.empty())
+ return textFunc(str);
+
+ return true;
+ }
+ }
+
+ // The below two functions aren't used anymore, not bothering with StringView for them
+ String preprocessEscapeCodes(String const& s) {
+ bool escape = false;
+ std::string result = s.utf8();
+
+ size_t escapeStartIdx = 0;
+ for (size_t i = 0; i < result.size(); i++) {
+ auto& c = result[i];
+ if (isEscapeCode(c)) {
+ escape = true;
+ escapeStartIdx = i;
+ }
+ if ((c <= SpecialCharLimit) && !(c == StartEsc))
+ escape = false;
+ if ((c == EndEsc) && escape)
+ result[escapeStartIdx] = StartEsc;
+ }
+ return {result};
+ }
+
+ String extractCodes(String const& s) {
+ bool escape = false;
+ StringList result;
+ String escapeCode;
+ for (auto c : preprocessEscapeCodes(s)) {
+ if (c == StartEsc)
+ escape = true;
+ if (c == EndEsc) {
+ escape = false;
+ for (auto command : escapeCode.split(','))
+ result.append(command);
+ escapeCode = "";
+ }
+ if (escape && (c != StartEsc))
+ escapeCode.append(c);
+ }
+ if (!result.size())
+ return "";
+ return "^" + result.join(",") + ";";
+ }
+}
+
+} \ No newline at end of file
diff --git a/source/core/StarText.hpp b/source/core/StarText.hpp
new file mode 100644
index 0000000..e318484
--- /dev/null
+++ b/source/core/StarText.hpp
@@ -0,0 +1,26 @@
+#ifndef STAR_TEXT_HPP
+#define STAR_TEXT_HPP
+#include "StarString.hpp"
+#include "StarStringView.hpp"
+
+namespace Star {
+
+namespace Text {
+ unsigned char const StartEsc = '\x1b';
+ unsigned char const EndEsc = ';';
+ unsigned char const CmdEsc = '^';
+ unsigned char const SpecialCharLimit = ' ';
+
+ String stripEscapeCodes(String const& s);
+ inline bool isEscapeCode(char c) { return c == CmdEsc || c == StartEsc; }
+
+ typedef function<bool(StringView text)> TextCallback;
+ typedef function<bool(StringView commands)> CommandsCallback;
+ bool processText(StringView text, TextCallback textFunc, CommandsCallback commandsFunc = CommandsCallback(), bool includeCommandSides = false);
+ String preprocessEscapeCodes(String const& s);
+ String extractCodes(String const& s);
+}
+
+}
+
+#endif \ No newline at end of file
diff --git a/source/core/StarTime.cpp b/source/core/StarTime.cpp
index 622ea50..f64b850 100644
--- a/source/core/StarTime.cpp
+++ b/source/core/StarTime.cpp
@@ -43,7 +43,7 @@ String Time::printDuration(double time) {
seconds = strf("{} second{}", numSeconds, numSeconds == 1 ? "" : "s");
}
- int numMilliseconds = round(time * 1000);
+ int numMilliseconds = round(fmod(time, 1.0) * 1000);
milliseconds = strf("{} millisecond{}", numMilliseconds, numMilliseconds == 1 ? "" : "s");
return String::joinWith(", ", hours, minutes, seconds, milliseconds);
diff --git a/source/frontend/StarClientCommandProcessor.cpp b/source/frontend/StarClientCommandProcessor.cpp
index ec5b27c..c7a510f 100644
--- a/source/frontend/StarClientCommandProcessor.cpp
+++ b/source/frontend/StarClientCommandProcessor.cpp
@@ -50,7 +50,8 @@ ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient,
{"giveessentialitem", bind(&ClientCommandProcessor::giveEssentialItem, this, _1)},
{"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)},
{"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)},
- {"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)}
+ {"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)},
+ {"swap", bind(&ClientCommandProcessor::swap, this, _1)}
};
}
@@ -304,7 +305,7 @@ String ClientCommandProcessor::playTime() {
String ClientCommandProcessor::deathCount() {
auto deaths = m_universeClient->mainPlayer()->log()->deathCount();
- return strf("Total deaths: {}{}", deaths, deaths == 0 ? ". Well done!" : "");
+ return deaths ? strf("Total deaths: {}", deaths) : "Total deaths: 0. Well done!";
}
String ClientCommandProcessor::cinema(String const& argumentsString) {
@@ -408,4 +409,16 @@ String ClientCommandProcessor::upgradeShip(String const& argumentsString) {
return strf("Upgraded ship");
}
+String ClientCommandProcessor::swap(String const& argumentsString) {
+ auto arguments = m_parser.tokenizeToStringList(argumentsString);
+
+ if (arguments.size() == 0)
+ return "Not enouch arguments to /swap";
+
+ if (m_universeClient->switchPlayer(arguments[0]))
+ return "Successfully swapped player";
+ else
+ return "Failed to swap player";
+}
+
}
diff --git a/source/frontend/StarClientCommandProcessor.hpp b/source/frontend/StarClientCommandProcessor.hpp
index e9e8962..7446e70 100644
--- a/source/frontend/StarClientCommandProcessor.hpp
+++ b/source/frontend/StarClientCommandProcessor.hpp
@@ -58,6 +58,7 @@ private:
String makeTechAvailable(String const& argumentsString);
String enableTech(String const& argumentsString);
String upgradeShip(String const& argumentsString);
+ String swap(String const& argumentsString);
UniverseClientPtr m_universeClient;
CinematicPtr m_cinematicOverlay;
diff --git a/source/frontend/StarInventory.cpp b/source/frontend/StarInventory.cpp
index a7626a7..a983f3e 100644
--- a/source/frontend/StarInventory.cpp
+++ b/source/frontend/StarInventory.cpp
@@ -210,7 +210,7 @@ bool InventoryPane::giveContainerResult(ContainerResult result) {
if (!m_expectingSwap)
return false;
- for (auto item : result) {
+ for (auto& item : result) {
auto inv = m_player->inventory();
m_player->triggerPickupEvents(item);
@@ -224,18 +224,25 @@ bool InventoryPane::giveContainerResult(ContainerResult result) {
}
void InventoryPane::updateItems() {
- for (auto p : m_itemGrids)
+ for (auto& p : m_itemGrids)
p.second->updateItemState();
}
bool InventoryPane::containsNewItems() const {
- for (auto p : m_itemGrids) {
+ for (auto& p : m_itemGrids) {
if (p.second->slotsChanged())
return true;
}
return false;
}
+void InventoryPane::clearChangedSlots() {
+ for (auto& p : m_itemGrids) {
+ p.second->updateItemState();
+ p.second->clearChangedSlots();
+ }
+}
+
void InventoryPane::update(float dt) {
auto inventory = m_player->inventory();
auto context = Widget::context();
diff --git a/source/frontend/StarInventory.hpp b/source/frontend/StarInventory.hpp
index cb5deed..807e2ec 100644
--- a/source/frontend/StarInventory.hpp
+++ b/source/frontend/StarInventory.hpp
@@ -33,6 +33,7 @@ public:
// this is a little hacky and should probably be checked in the player inventory instead
void updateItems();
bool containsNewItems() const;
+ void clearChangedSlots();
protected:
virtual void update(float dt) override;
diff --git a/source/frontend/StarTeamBar.cpp b/source/frontend/StarTeamBar.cpp
index 192edfa..a378040 100644
--- a/source/frontend/StarTeamBar.cpp
+++ b/source/frontend/StarTeamBar.cpp
@@ -40,7 +40,7 @@ TeamBar::TeamBar(MainInterface* mainInterface, UniverseClientPtr client) {
return;
auto position = jsonToVec2I(Root::singleton().assets()->json("/interface/windowconfig/teambar.config:selfMenuOffset"));
position[1] += windowHeight() / m_guiContext->interfaceScale();
- showMemberMenu(m_client->mainPlayer()->clientContext()->serverUuid(), position);
+ showMemberMenu(m_client->mainPlayer()->clientContext()->playerUuid(), position);
});
reader.construct(assets->json("/interface/windowconfig/teambar.config:paneLayout"), this);
@@ -155,7 +155,7 @@ void TeamBar::buildTeamBar() {
int memberSize = assets->json("/interface/windowconfig/teambar.config:memberSize").toInt();
int memberSpacing = assets->json("/interface/windowconfig/teambar.config:memberSpacing").toInt();
- Uuid myUuid = player->clientContext()->serverUuid();
+ Uuid myUuid = player->clientContext()->playerUuid();
for (auto member : teamClient->members()) {
if (member.uuid == myUuid) {
memberIndex++;
@@ -360,7 +360,7 @@ void TeamMemberMenu::update(float dt) {
void TeamMemberMenu::updateWidgets() {
bool isLeader = m_owner->m_client->teamClient()->isTeamLeader();
- bool isSelf = m_owner->m_client->mainPlayer()->clientContext()->serverUuid() == m_memberUuid;
+ bool isSelf = m_owner->m_client->mainPlayer()->clientContext()->playerUuid() == m_memberUuid;
fetchChild<ButtonWidget>("beamToShip")->setEnabled(m_canBeam);
fetchChild<ButtonWidget>("makeLeader")->setEnabled(isLeader && !isSelf);
diff --git a/source/game/StarClientContext.cpp b/source/game/StarClientContext.cpp
index 1482168..748b864 100644
--- a/source/game/StarClientContext.cpp
+++ b/source/game/StarClientContext.cpp
@@ -24,8 +24,9 @@ DataStream& operator<<(DataStream& ds, ShipUpgrades const& upgrades) {
return ds;
}
-ClientContext::ClientContext(Uuid serverUuid) {
+ClientContext::ClientContext(Uuid serverUuid, Uuid playerUuid) {
m_serverUuid = move(serverUuid);
+ m_playerUuid = move(playerUuid);
m_rpc = make_shared<JsonRpc>();
m_netGroup.addNetElement(&m_orbitWarpActionNetState);
@@ -40,6 +41,10 @@ Uuid ClientContext::serverUuid() const {
return m_serverUuid;
}
+Uuid ClientContext::playerUuid() const {
+ return m_playerUuid;
+}
+
CelestialCoordinate ClientContext::shipCoordinate() const {
return m_shipCoordinate.get();
}
diff --git a/source/game/StarClientContext.hpp b/source/game/StarClientContext.hpp
index 22db10f..425bfd3 100644
--- a/source/game/StarClientContext.hpp
+++ b/source/game/StarClientContext.hpp
@@ -17,9 +17,12 @@ STAR_CLASS(ClientContext);
class ClientContext {
public:
- ClientContext(Uuid serverUuid);
+ ClientContext(Uuid serverUuid, Uuid playerUuid);
Uuid serverUuid() const;
+ // The player Uuid can differ from the mainPlayer's Uuid
+ // if the player has swapped character - use this for ship saving.
+ Uuid playerUuid() const;
// The coordinate for the world which the player's ship is currently
// orbiting.
@@ -43,6 +46,8 @@ public:
private:
Uuid m_serverUuid;
+ Uuid m_playerUuid;
+
JsonRpcPtr m_rpc;
NetElementTopGroup m_netGroup;
diff --git a/source/game/StarEntityMap.cpp b/source/game/StarEntityMap.cpp
index 66d09a5..2232099 100644
--- a/source/game/StarEntityMap.cpp
+++ b/source/game/StarEntityMap.cpp
@@ -27,6 +27,25 @@ EntityId EntityMap::reserveEntityId() {
return id;
}
+Maybe<EntityId> EntityMap::maybeReserveEntityId(EntityId entityId) {
+ if (m_spatialMap.size() >= (size_t)(m_endIdSpace - m_beginIdSpace))
+ throw EntityMapException("No more entity id space in EntityMap::reserveEntityId");
+
+ if (m_spatialMap.contains(entityId))
+ return {};
+ else
+ return entityId;
+}
+
+EntityId EntityMap::reserveEntityId(EntityId entityId) {
+ if (auto reserved = maybeReserveEntityId(entityId))
+ return *reserved;
+
+ m_nextId = entityId;
+ return reserveEntityId();
+}
+
+
void EntityMap::addEntity(EntityPtr entity) {
auto position = entity->position();
auto boundBox = entity->metaBoundBox();
diff --git a/source/game/StarEntityMap.hpp b/source/game/StarEntityMap.hpp
index d1adc49..bf8a12a 100644
--- a/source/game/StarEntityMap.hpp
+++ b/source/game/StarEntityMap.hpp
@@ -30,6 +30,10 @@ public:
// Get the next free id in the entity id space.
EntityId reserveEntityId();
+ // Or a specific one, can fail.
+ Maybe<EntityId> maybeReserveEntityId(EntityId entityId);
+ // If it doesn't matter that we don't get the one want
+ EntityId reserveEntityId(EntityId entityId);
// Add an entity to this EntityMap. The entity must already be initialized
// and have a unique EntityId returned by reserveEntityId.
diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp
index 54af56f..7bfa224 100644
--- a/source/game/StarPlayer.cpp
+++ b/source/game/StarPlayer.cpp
@@ -56,6 +56,7 @@ Player::Player(PlayerConfigPtr config, Uuid uuid) {
auto assets = Root::singleton().assets();
m_config = config;
+ m_client = nullptr;
m_state = State::Idle;
m_emoteState = HumanoidEmote::Idle;
@@ -186,13 +187,35 @@ Player::Player(PlayerConfigPtr config, Uuid uuid) {
m_netGroup.setNeedsStoreCallback(bind(&Player::setNetStates, this));
}
+Player::Player(PlayerConfigPtr config, ByteArray const& netStore) : Player(config) {
+ DataStreamBuffer ds(netStore);
+
+ setUniqueId(ds.read<String>());
+
+ ds.read(m_description);
+ ds.read(m_modeType);
+ ds.read(m_identity);
+
+ m_humanoid = make_shared<Humanoid>(Root::singleton().speciesDatabase()->species(m_identity.species)->humanoidConfig());
+ m_humanoid->setIdentity(m_identity);
+ m_movementController->resetBaseParameters(ActorMovementParameters(jsonMerge(m_humanoid->defaultMovementParameters(), m_config->movementParameters)));
+}
+
+
Player::Player(PlayerConfigPtr config, Json const& diskStore) : Player(config) {
+ diskLoad(diskStore);
+}
+
+void Player::diskLoad(Json const& diskStore) {
setUniqueId(diskStore.getString("uuid"));
m_description = diskStore.getString("description");
setModeType(PlayerModeNames.getLeft(diskStore.getString("modeType")));
m_shipUpgrades = ShipUpgrades(diskStore.get("shipUpgrades"));
m_blueprints = make_shared<PlayerBlueprints>(diskStore.get("blueprints"));
m_universeMap = make_shared<PlayerUniverseMap>(diskStore.get("universeMap"));
+ if (m_clientContext)
+ m_universeMap->setServerUuid(m_clientContext->serverUuid());
+
m_codexes = make_shared<PlayerCodexes>(diskStore.get("codexes"));
m_techs = make_shared<PlayerTech>(diskStore.get("techs"));
m_identity = HumanoidIdentity(diskStore.get("identity"));
@@ -208,48 +231,39 @@ Player::Player(PlayerConfigPtr config, Json const& diskStore) : Player(config) {
m_log = make_shared<PlayerLog>(diskStore.get("log"));
- m_codexes->learnInitialCodexes(species());
-
- // Make sure to merge the stored player blueprints with what a new player
- // would get as default.
- for (auto const& descriptor : m_config->defaultBlueprints)
- m_blueprints->add(descriptor);
- for (auto const& descriptor : Root::singleton().speciesDatabase()->species(m_identity.species)->defaultBlueprints())
- m_blueprints->add(descriptor);
+ auto speciesDef = Root::singleton().speciesDatabase()->species(m_identity.species);
m_questManager->diskLoad(diskStore.get("quests", JsonObject{}));
m_companions->diskLoad(diskStore.get("companions", JsonObject{}));
m_deployment->diskLoad(diskStore.get("deployment", JsonObject{}));
- m_humanoid = make_shared<Humanoid>(Root::singleton().speciesDatabase()->species(m_identity.species)->humanoidConfig());
+ m_humanoid = make_shared<Humanoid>(speciesDef->humanoidConfig());
m_humanoid->setIdentity(m_identity);
m_movementController->resetBaseParameters(ActorMovementParameters(jsonMerge(m_humanoid->defaultMovementParameters(), m_config->movementParameters)));
- m_effectsAnimator->setGlobalTag("effectDirectives", Root::singleton().speciesDatabase()->species(m_identity.species)->effectDirectives());
+ m_effectsAnimator->setGlobalTag("effectDirectives", speciesDef->effectDirectives());
m_genericProperties = diskStore.getObject("genericProperties");
refreshEquipment();
+ m_codexes->learnInitialCodexes(species());
+
m_aiState = AiState(diskStore.get("aiState", JsonObject{}));
+ for (auto& script : m_genericScriptContexts)
+ script.second->setScriptStorage({});
+
for (auto& p : diskStore.get("genericScriptStorage", JsonObject{}).toObject()) {
if (auto script = m_genericScriptContexts.maybe(p.first).value({})) {
script->setScriptStorage(p.second.toObject());
}
}
-}
-
-Player::Player(PlayerConfigPtr config, ByteArray const& netStore) : Player(config) {
- DataStreamBuffer ds(netStore);
-
- setUniqueId(ds.read<String>());
-
- ds.read(m_description);
- ds.read(m_modeType);
- ds.read(m_identity);
- m_humanoid = make_shared<Humanoid>(Root::singleton().speciesDatabase()->species(m_identity.species)->humanoidConfig());
- m_humanoid->setIdentity(m_identity);
- m_movementController->resetBaseParameters(ActorMovementParameters(jsonMerge(m_humanoid->defaultMovementParameters(), m_config->movementParameters)));
+ // Make sure to merge the stored player blueprints with what a new player
+ // would get as default.
+ for (auto const& descriptor : m_config->defaultBlueprints)
+ m_blueprints->add(descriptor);
+ for (auto const& descriptor : speciesDef->defaultBlueprints())
+ m_blueprints->add(descriptor);
}
ClientContextPtr Player::clientContext() const {
@@ -279,6 +293,10 @@ EntityType Player::entityType() const {
return EntityType::Player;
}
+ClientEntityMode Player::clientEntityMode() const {
+ return ClientEntityMode::ClientPresenceMaster;
+}
+
void Player::init(World* world, EntityId entityId, EntityMode mode) {
Entity::init(world, entityId, mode);
@@ -1961,6 +1979,13 @@ void Player::setShipUpgrades(ShipUpgrades shipUpgrades) {
m_shipUpgrades = move(shipUpgrades);
}
+void Player::applyShipUpgrades(Json const& upgrades) {
+ if (m_clientContext->playerUuid() == uuid())
+ m_clientContext->rpcInterface()->invokeRemote("ship.applyShipUpgrades", upgrades);
+ else
+ m_shipUpgrades.apply(upgrades);
+}
+
String Player::name() const {
return m_identity.name;
}
@@ -2176,6 +2201,19 @@ List<PhysicsForceRegion> Player::forceRegions() const {
return m_tools->forceRegions();
}
+
+StatusControllerPtr Player::statusControllerPtr() {
+ return m_statusController;
+}
+
+ActorMovementControllerPtr Player::movementControllerPtr() {
+ return m_movementController;
+}
+
+PlayerConfigPtr Player::config() {
+ return m_config;
+}
+
SongbookPtr Player::songbook() const {
return m_songbook;
}
diff --git a/source/game/StarPlayer.hpp b/source/game/StarPlayer.hpp
index 7bc0a73..99962cd 100644
--- a/source/game/StarPlayer.hpp
+++ b/source/game/StarPlayer.hpp
@@ -62,8 +62,10 @@ class Player :
public:
Player(PlayerConfigPtr config, Uuid uuid = Uuid());
- Player(PlayerConfigPtr config, Json const& diskStore);
Player(PlayerConfigPtr config, ByteArray const& netStore);
+ Player(PlayerConfigPtr config, Json const& diskStore);
+
+ void diskLoad(Json const& diskStore);
ClientContextPtr clientContext() const;
void setClientContext(ClientContextPtr clientContext);
@@ -79,6 +81,7 @@ public:
ByteArray netStore();
EntityType entityType() const override;
+ ClientEntityMode clientEntityMode() const override;
void init(World* world, EntityId entityId, EntityMode mode) override;
void uninit() override;
@@ -283,6 +286,7 @@ public:
ShipUpgrades shipUpgrades();
void setShipUpgrades(ShipUpgrades shipUpgrades);
+ void applyShipUpgrades(Json const& upgrades);
String name() const override;
void setName(String const& name);
@@ -395,6 +399,11 @@ public:
List<PhysicsForceRegion> forceRegions() const override;
+ StatusControllerPtr statusControllerPtr();
+ ActorMovementControllerPtr movementControllerPtr();
+
+ PlayerConfigPtr config();
+
SongbookPtr songbook() const;
void finalizeCreation();
diff --git a/source/game/StarPlayerInventory.cpp b/source/game/StarPlayerInventory.cpp
index 0ebb18c..58e2b31 100644
--- a/source/game/StarPlayerInventory.cpp
+++ b/source/game/StarPlayerInventory.cpp
@@ -755,9 +755,18 @@ void PlayerInventory::load(Json const& store) {
m_equipment[EquipmentSlot::LegsCosmetic] = itemDatabase->diskLoad(store.get("legsCosmeticSlot"));
m_equipment[EquipmentSlot::BackCosmetic] = itemDatabase->diskLoad(store.get("backCosmeticSlot"));
- auto itemBags = store.get("itemBags");
- for (String const& bagType : itemBags.toObject().keys())
- m_bags[bagType] = make_shared<ItemBag>(ItemBag::loadStore(itemBags.get(bagType)));
+ //reuse ItemBags so the Inventory pane still works after load()'ing into the same PlayerInventory again (from swap)
+ auto itemBags = store.get("itemBags").toObject();
+ eraseWhere(m_bags, [&](auto const& p) { return !itemBags.contains(p.first); });
+ for (auto const& p : itemBags) {
+ auto& bagType = p.first;
+ auto newBag = ItemBag::loadStore(p.second);
+ auto& bagPtr = m_bags[bagType];
+ if (bagPtr)
+ *bagPtr = move(newBag);
+ else
+ bagPtr = make_shared<ItemBag>(move(newBag));
+ }
m_swapSlot = itemDatabase->diskLoad(store.get("swapSlot"));
m_trashSlot = itemDatabase->diskLoad(store.get("trashSlot"));
@@ -778,6 +787,7 @@ void PlayerInventory::load(Json const& store) {
m_selectedActionBar = jsonToSelectedActionBarLocation(store.get("selectedActionBar"));
+ m_essential.clear();
m_essential[EssentialItem::BeamAxe] = itemDatabase->diskLoad(store.get("beamAxe"));
m_essential[EssentialItem::WireTool] = itemDatabase->diskLoad(store.get("wireTool"));
m_essential[EssentialItem::PaintTool] = itemDatabase->diskLoad(store.get("paintTool"));
diff --git a/source/game/StarPlayerStorage.cpp b/source/game/StarPlayerStorage.cpp
index dfccc88..c6ae2a3 100644
--- a/source/game/StarPlayerStorage.cpp
+++ b/source/game/StarPlayerStorage.cpp
@@ -8,6 +8,7 @@
#include "StarAssets.hpp"
#include "StarEntityFactory.hpp"
#include "StarRoot.hpp"
+#include "StarText.hpp"
namespace Star {
@@ -98,7 +99,28 @@ Maybe<Uuid> PlayerStorage::playerUuidAt(size_t index) {
return {};
}
-void PlayerStorage::savePlayer(PlayerPtr const& player) {
+Maybe<Uuid> PlayerStorage::playerUuidByName(String const& name) {
+ String cleanMatch = Text::stripEscapeCodes(name).toLower();
+ Maybe<Uuid> uuid;
+
+ RecursiveMutexLocker locker(m_mutex);
+
+ size_t longest = SIZE_MAX;
+ for (auto& cache : m_savedPlayersCache) {
+ if (auto name = cache.second.optQueryString("identity.name")) {
+ auto cleanName = Text::stripEscapeCodes(*name).toLower();
+ auto len = cleanName.size();
+ if (len < longest && cleanName.utf8().rfind(cleanMatch.utf8()) == 0) {
+ longest = len;
+ uuid = cache.first;
+ }
+ }
+ }
+
+ return uuid;
+}
+
+Json PlayerStorage::savePlayer(PlayerPtr const& player) {
auto entityFactory = Root::singleton().entityFactory();
auto versioningDatabase = Root::singleton().versioningDatabase();
@@ -113,15 +135,28 @@ void PlayerStorage::savePlayer(PlayerPtr const& player) {
VersionedJson versionedJson = entityFactory->storeVersionedJson(EntityType::Player, playerCacheData);
VersionedJson::writeFile(versionedJson, File::relativeTo(m_storageDirectory, strf("{}.player", uuid.hex())));
}
+ return newPlayerData;
}
-PlayerPtr PlayerStorage::loadPlayer(Uuid const& uuid) {
+Maybe<Json> PlayerStorage::maybeGetPlayerData(Uuid const& uuid) {
RecursiveMutexLocker locker(m_mutex);
- if (!m_savedPlayersCache.contains(uuid))
+ if (auto cache = m_savedPlayersCache.ptr(uuid))
+ return *cache;
+ else
+ return {};
+}
+
+Json PlayerStorage::getPlayerData(Uuid const& uuid) {
+ auto data = maybeGetPlayerData(uuid);
+ if (!data)
throw PlayerException(strf("No such stored player with uuid '{}'", uuid.hex()));
+ else
+ return *data;
+}
+PlayerPtr PlayerStorage::loadPlayer(Uuid const& uuid) {
+ auto playerCacheData = getPlayerData(uuid);
auto entityFactory = Root::singleton().entityFactory();
- auto const& playerCacheData = m_savedPlayersCache.get(uuid);
try {
auto player = convert<Player>(entityFactory->diskLoadEntity(EntityType::Player, playerCacheData));
if (player->uuid() != uuid)
@@ -129,6 +164,7 @@ PlayerPtr PlayerStorage::loadPlayer(Uuid const& uuid) {
return player;
} catch (std::exception const& e) {
Logger::error("Error loading player file, ignoring! {}", outputException(e, false));
+ RecursiveMutexLocker locker(m_mutex);
m_savedPlayersCache.remove(uuid);
return {};
}
diff --git a/source/game/StarPlayerStorage.hpp b/source/game/StarPlayerStorage.hpp
index 68dda4d..4b9edc7 100644
--- a/source/game/StarPlayerStorage.hpp
+++ b/source/game/StarPlayerStorage.hpp
@@ -18,8 +18,14 @@ public:
size_t playerCount() const;
// Returns nothing if index is out of bounds.
Maybe<Uuid> playerUuidAt(size_t index);
+ // Returns nothing if name doesn't match a player.
+ Maybe<Uuid> playerUuidByName(String const& name);
- void savePlayer(PlayerPtr const& player);
+ // Also returns the diskStore Json if needed.
+ Json savePlayer(PlayerPtr const& player);
+
+ Maybe<Json> maybeGetPlayerData(Uuid const& uuid);
+ Json getPlayerData(Uuid const& uuid);
PlayerPtr loadPlayer(Uuid const& uuid);
void deletePlayer(Uuid const& uuid);
diff --git a/source/game/StarQuestManager.cpp b/source/game/StarQuestManager.cpp
index 6ea4689..e9736c7 100644
--- a/source/game/StarQuestManager.cpp
+++ b/source/game/StarQuestManager.cpp
@@ -110,7 +110,7 @@ bool QuestManager::canStart(QuestArcDescriptor const& questArc) const {
if (!m_player->inventory()->hasItem(item))
return false;
if (questTemplate->requiredShipLevel) {
- if (m_player->clientContext()->shipUpgrades().shipLevel < *questTemplate->requiredShipLevel)
+ if (m_player->shipUpgrades().shipLevel < *questTemplate->requiredShipLevel)
return false;
}
}
diff --git a/source/game/StarSystemWorld.hpp b/source/game/StarSystemWorld.hpp
index be9a30a..c68bc59 100644
--- a/source/game/StarSystemWorld.hpp
+++ b/source/game/StarSystemWorld.hpp
@@ -94,6 +94,7 @@ public:
Maybe<WarpAction> objectWarpAction(Uuid const& uuid) const;
virtual List<SystemObjectPtr> objects() const = 0;
+ virtual List<Uuid> objectKeys() const = 0;
virtual SystemObjectPtr getObject(Uuid const& uuid) const = 0;
SystemObjectConfig systemObjectConfig(String const& name, Uuid const& uuid) const;
diff --git a/source/game/StarSystemWorldClient.cpp b/source/game/StarSystemWorldClient.cpp
index d4f8c15..da28c98 100644
--- a/source/game/StarSystemWorldClient.cpp
+++ b/source/game/StarSystemWorldClient.cpp
@@ -82,6 +82,10 @@ List<SystemObjectPtr> SystemWorldClient::objects() const {
return m_objects.values();
}
+List<Uuid> SystemWorldClient::objectKeys() const {
+ return m_objects.keys();
+}
+
SystemObjectPtr SystemWorldClient::getObject(Uuid const& uuid) const {
return m_objects.maybe(uuid).value({});
}
diff --git a/source/game/StarSystemWorldClient.hpp b/source/game/StarSystemWorldClient.hpp
index 1ec059a..a222ff5 100644
--- a/source/game/StarSystemWorldClient.hpp
+++ b/source/game/StarSystemWorldClient.hpp
@@ -28,6 +28,7 @@ public:
void update(float dt);
List<SystemObjectPtr> objects() const override;
+ List<Uuid> objectKeys() const override;
SystemObjectPtr getObject(Uuid const& uuid) const override;
List<SystemClientShipPtr> ships() const;
diff --git a/source/game/StarSystemWorldServer.cpp b/source/game/StarSystemWorldServer.cpp
index f430bcb..39a1aad 100644
--- a/source/game/StarSystemWorldServer.cpp
+++ b/source/game/StarSystemWorldServer.cpp
@@ -247,6 +247,10 @@ List<SystemObjectPtr> SystemWorldServer::objects() const {
return m_objects.values();
}
+List<Uuid> SystemWorldServer::objectKeys() const {
+ return m_objects.keys();
+}
+
SystemObjectPtr SystemWorldServer::getObject(Uuid const& uuid) const {
return m_objects.maybe(uuid).value({});
}
diff --git a/source/game/StarSystemWorldServer.hpp b/source/game/StarSystemWorldServer.hpp
index f1bc30f..26a703c 100644
--- a/source/game/StarSystemWorldServer.hpp
+++ b/source/game/StarSystemWorldServer.hpp
@@ -40,6 +40,7 @@ public:
void update(float dt);
List<SystemObjectPtr> objects() const override;
+ List<Uuid> objectKeys() const override;
SystemObjectPtr getObject(Uuid const& uuid) const override;
List<ConnectionId> pullShipFlights();
diff --git a/source/game/StarUniverseClient.cpp b/source/game/StarUniverseClient.cpp
index 3adc95d..90d5f07 100644
--- a/source/game/StarUniverseClient.cpp
+++ b/source/game/StarUniverseClient.cpp
@@ -109,7 +109,7 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
if (auto success = as<ConnectSuccessPacket>(packet)) {
m_universeClock = make_shared<Clock>();
- m_clientContext = make_shared<ClientContext>(success->serverUuid);
+ m_clientContext = make_shared<ClientContext>(success->serverUuid, m_mainPlayer->uuid());
m_teamClient = make_shared<TeamClient>(m_mainPlayer, m_clientContext);
m_mainPlayer->setClientContext(m_clientContext);
m_mainPlayer->setStatistics(m_statistics);
@@ -397,6 +397,10 @@ bool UniverseClient::playerOnOwnShip() const {
return playerWorld().is<ClientShipWorldId>() && playerWorld().get<ClientShipWorldId>() == mainPlayer()->uuid();
}
+bool UniverseClient::playerIsOriginal() const {
+ return m_clientContext->playerUuid() == mainPlayer()->uuid();
+}
+
WorldId UniverseClient::playerWorld() const {
return m_clientContext->playerWorldId();
}
@@ -473,6 +477,80 @@ void UniverseClient::stopLua() {
m_scriptContexts.clear();
}
+bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid) {
+ auto player = mainPlayer();
+ auto world = worldClient();
+ bool inWorld = player->inWorld();
+ EntityId entityId = player->entityId();
+
+ if (m_playerReloadPreCallback)
+ m_playerReloadPreCallback();
+
+ if (inWorld)
+ world->removeEntity(entityId, false);
+ else {
+ m_respawning = false;
+ m_respawnTimer.reset();
+ }
+
+ Json originalData = m_playerStorage->savePlayer(player);
+ std::exception_ptr exception;
+
+ try {
+ auto newData = data.set("movementController", originalData.get("movementController"));
+ player->diskLoad(newData);
+ }
+ catch (std::exception const& e) {
+ player->diskLoad(originalData);
+ exception = std::current_exception();
+ }
+
+ world->addEntity(player);
+
+ CelestialCoordinate coordinate = m_systemWorldClient->location();
+ player->universeMap()->addMappedCoordinate(coordinate);
+ player->universeMap()->filterMappedObjects(coordinate, m_systemWorldClient->objectKeys());
+
+ if (m_playerReloadCallback)
+ m_playerReloadCallback();
+
+ if (exception)
+ std::rethrow_exception(exception);
+
+ return true;
+}
+
+bool UniverseClient::switchPlayer(Uuid const& uuid) {
+ if (uuid == mainPlayer()->uuid())
+ return false;
+ else if (auto data = m_playerStorage->maybeGetPlayerData(uuid))
+ return reloadPlayer(*data, uuid);
+ else
+ return false;
+}
+
+bool UniverseClient::switchPlayer(size_t index) {
+ if (auto uuid = m_playerStorage->playerUuidAt(index))
+ return switchPlayer(*uuid);
+ else
+ return false;
+}
+
+bool UniverseClient::switchPlayer(String const& name) {
+ if (auto uuid = m_playerStorage->playerUuidByName(name))
+ return switchPlayer(*uuid);
+ else
+ return false;
+}
+
+UniverseClient::Callback& UniverseClient::playerReloadPreCallback() {
+ return m_playerReloadPreCallback;
+}
+
+UniverseClient::Callback& UniverseClient::playerReloadCallback() {
+ return m_playerReloadCallback;
+}
+
ClockConstPtr UniverseClient::universeClock() const {
return m_universeClock;
}
@@ -518,10 +596,14 @@ void UniverseClient::handlePackets(List<PacketPtr> const& packets) {
for (auto const& packet : packets) {
if (auto clientContextUpdate = as<ClientContextUpdatePacket>(packet)) {
m_clientContext->readUpdate(clientContextUpdate->updateData);
- m_playerStorage->applyShipUpdates(m_mainPlayer->uuid(), m_clientContext->newShipUpdates());
- m_mainPlayer->setShipUpgrades(m_clientContext->shipUpgrades());
+ m_playerStorage->applyShipUpdates(m_clientContext->playerUuid(), m_clientContext->newShipUpdates());
+
+ if (playerIsOriginal())
+ m_mainPlayer->setShipUpgrades(m_clientContext->shipUpgrades());
+
m_mainPlayer->setAdmin(m_clientContext->isAdmin());
m_mainPlayer->setTeam(m_clientContext->team());
+
} else if (auto chatReceivePacket = as<ChatReceivePacket>(packet)) {
m_pendingMessages.append(chatReceivePacket->receivedMessage);
diff --git a/source/game/StarUniverseClient.hpp b/source/game/StarUniverseClient.hpp
index 9fda492..911061c 100644
--- a/source/game/StarUniverseClient.hpp
+++ b/source/game/StarUniverseClient.hpp
@@ -69,6 +69,7 @@ public:
CelestialCoordinate shipCoordinate() const;
bool playerOnOwnShip() const;
+ bool playerIsOriginal() const;
WorldId playerWorld() const;
bool isAdmin() const;
@@ -90,6 +91,15 @@ public:
void startLua();
void stopLua();
+ bool reloadPlayer(Json const& data, Uuid const& uuid);
+ bool switchPlayer(Uuid const& uuid);
+ bool switchPlayer(size_t index);
+ bool switchPlayer(String const& name);
+
+ typedef std::function<void()> Callback;
+ Callback& playerReloadPreCallback();
+ Callback& playerReloadCallback();
+
ClockConstPtr universeClock() const;
CelestialLogConstPtr celestialLog() const;
JsonRpcInterfacePtr rpcInterface() const;
@@ -150,6 +160,9 @@ private:
typedef LuaUpdatableComponent<LuaBaseComponent> ScriptComponent;
typedef shared_ptr<ScriptComponent> ScriptComponentPtr;
StringMap<ScriptComponentPtr> m_scriptContexts;
+
+ Callback m_playerReloadPreCallback;
+ Callback m_playerReloadCallback;
};
}
diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp
index 56f28a2..3f80682 100644
--- a/source/game/StarWorldClient.cpp
+++ b/source/game/StarWorldClient.cpp
@@ -136,6 +136,40 @@ bool WorldClient::respawnInWorld() const {
return m_respawnInWorld;
}
+void WorldClient::removeEntity(EntityId entityId, bool andDie) {
+ auto entity = m_entityMap->entity(entityId);
+ if (!entity)
+ return;
+
+ if (andDie) {
+ ClientRenderCallback renderCallback;
+ entity->destroy(&renderCallback);
+
+ const List<Directives>* directives = nullptr;
+ if (auto& worldTemplate = m_worldTemplate) {
+ if (const auto& parameters = worldTemplate->worldParameters())
+ if (auto& globalDirectives = m_worldTemplate->worldParameters()->globalDirectives)
+ directives = &globalDirectives.get();
+ }
+ if (directives) {
+ int directiveIndex = unsigned(entity->entityId()) % directives->size();
+ for (auto& p : renderCallback.particles)
+ p.directives.append(directives->get(directiveIndex));
+ }
+
+ m_particles->addParticles(move(renderCallback.particles));
+ m_samples.appendAll(move(renderCallback.audios));
+ }
+
+ if (auto version = m_masterEntitiesNetVersion.maybeTake(entity->entityId())) {
+ ByteArray finalNetState = entity->writeNetState(*version).first;
+ m_outgoingPackets.append(make_shared<EntityDestroyPacket>(entity->entityId(), move(finalNetState), andDie));
+ }
+
+ m_entityMap->removeEntity(entityId);
+ entity->uninit();
+}
+
WorldTemplateConstPtr WorldClient::currentTemplate() const {
return m_worldTemplate;
}
@@ -989,7 +1023,7 @@ void WorldClient::update(float dt) {
action(this);
List<EntityId> toRemove;
- List<EntityId> clientPresenceEntities{m_mainPlayer->entityId()};
+ List<EntityId> clientPresenceEntities;
m_entityMap->updateAllEntities([&](EntityPtr const& entity) {
entity->update(dt, m_currentStep);
@@ -1415,40 +1449,6 @@ void WorldClient::sparkDamagedBlocks() {
}
}
-void WorldClient::removeEntity(EntityId entityId, bool andDie) {
- auto entity = m_entityMap->entity(entityId);
- if (!entity)
- return;
-
- if (andDie) {
- ClientRenderCallback renderCallback;
- entity->destroy(&renderCallback);
-
- const List<Directives>* directives = nullptr;
- if (auto& worldTemplate = m_worldTemplate) {
- if (const auto& parameters = worldTemplate->worldParameters())
- if (auto& globalDirectives = m_worldTemplate->worldParameters()->globalDirectives)
- directives = &globalDirectives.get();
- }
- if (directives) {
- int directiveIndex = unsigned(entity->entityId()) % directives->size();
- for (auto& p : renderCallback.particles)
- p.directives.append(directives->get(directiveIndex));
- }
-
- m_particles->addParticles(move(renderCallback.particles));
- m_samples.appendAll(move(renderCallback.audios));
- }
-
- if (auto version = m_masterEntitiesNetVersion.maybeTake(entity->entityId())) {
- ByteArray finalNetState = entity->writeNetState(*version).first;
- m_outgoingPackets.append(make_shared<EntityDestroyPacket>(entity->entityId(), move(finalNetState), andDie));
- }
-
- m_entityMap->removeEntity(entityId);
- entity->uninit();
-}
-
InteractiveEntityPtr WorldClient::getInteractiveInRange(Vec2F const& targetPosition, Vec2F const& sourcePosition, float maxRange) const {
if (!inWorld())
return {};
diff --git a/source/game/StarWorldClient.hpp b/source/game/StarWorldClient.hpp
index 34efc1c..6a241ae 100644
--- a/source/game/StarWorldClient.hpp
+++ b/source/game/StarWorldClient.hpp
@@ -108,6 +108,8 @@ public:
void reviveMainPlayer();
bool respawnInWorld() const;
+ void removeEntity(EntityId entityId, bool andDie);
+
WorldTemplateConstPtr currentTemplate() const;
SkyConstPtr currentSky() const;
@@ -214,8 +216,6 @@ private:
void sparkDamagedBlocks();
- void removeEntity(EntityId entityId, bool andDie);
-
Vec2I environmentBiomeTrackPosition() const;
AmbientNoisesDescriptionPtr currentAmbientNoises() const;
WeatherNoisesDescriptionPtr currentWeatherNoises() const;
diff --git a/source/game/items/StarUnlockItem.cpp b/source/game/items/StarUnlockItem.cpp
index 471c39c..ee067fb 100644
--- a/source/game/items/StarUnlockItem.cpp
+++ b/source/game/items/StarUnlockItem.cpp
@@ -40,7 +40,7 @@ void UnlockItem::fireTriggered() {
if (auto clientContext = player->clientContext()) {
if (m_shipUpgrade)
- clientContext->rpcInterface()->invokeRemote("ship.applyShipUpgrades", JsonObject{{"shipLevel", *m_shipUpgrade}});
+ player->applyShipUpgrades(JsonObject{ {"shipLevel", *m_shipUpgrade} });
}
if (!m_unlockMessage.empty()) {
diff --git a/source/game/scripting/StarPlayerLuaBindings.cpp b/source/game/scripting/StarPlayerLuaBindings.cpp
index fe1c06d..5980a1d 100644
--- a/source/game/scripting/StarPlayerLuaBindings.cpp
+++ b/source/game/scripting/StarPlayerLuaBindings.cpp
@@ -112,11 +112,8 @@ LuaCallbacks LuaBindings::makePlayerCallbacks(Player* player) {
player->interact(InteractAction(type, sourceEntityId.value(NullEntityId), configData));
});
- callbacks.registerCallback("shipUpgrades", [player]() { return player->clientContext()->shipUpgrades().toJson(); });
-
- callbacks.registerCallback("upgradeShip", [player](Json const& upgrades) {
- player->clientContext()->rpcInterface()->invokeRemote("ship.applyShipUpgrades", upgrades);
- });
+ callbacks.registerCallback("shipUpgrades", [player]() { return player->shipUpgrades().toJson(); });
+ callbacks.registerCallback("upgradeShip", [player](Json const& upgrades) { player->applyShipUpgrades(upgrades); });
callbacks.registerCallback("setUniverseFlag", [player](String const& flagName) {
player->clientContext()->rpcInterface()->invokeRemote("universe.setFlag", flagName);
diff --git a/source/rendering/StarTextPainter.cpp b/source/rendering/StarTextPainter.cpp
index 3e6c618..f0ef53d 100644
--- a/source/rendering/StarTextPainter.cpp
+++ b/source/rendering/StarTextPainter.cpp
@@ -1,98 +1,11 @@
#include "StarTextPainter.hpp"
#include "StarJsonExtra.hpp"
+#include "StarText.hpp"
#include <regex>
namespace Star {
-namespace Text {
- static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc));
- String stripEscapeCodes(String const& s) {
- return std::regex_replace(s.utf8(), stripEscapeRegex, "");
- }
-
- inline bool isEscapeCode(char c) {
- return c == CmdEsc || c == StartEsc;
- }
-
- static std::string escapeChars = strf("{:c}{:c}", CmdEsc, StartEsc);
-
- typedef function<bool(StringView text)> TextCallback;
- typedef function<bool(StringView commands)> CommandsCallback;
- bool processText(StringView text, TextCallback textFunc, CommandsCallback commandsFunc = CommandsCallback(), bool includeCommandSides = false) {
- std::string_view escChars(escapeChars);
-
- std::string_view str = text.utf8();
- while (true) {
- size_t escape = str.find_first_of(escChars);
- if (escape != NPos) {
- escape = str.find_first_not_of(escChars, escape) - 1; // jump to the last ^
-
- size_t end = str.find_first_of(EndEsc, escape);
- if (end != NPos) {
- if (escape && !textFunc(str.substr(0, escape)))
- return false;
- if (commandsFunc) {
- StringView commands = includeCommandSides
- ? str.substr(escape, end - escape + 1)
- : str.substr(escape + 1, end - escape - 1);
- if (!commands.empty() && !commandsFunc(commands))
- return false;
- }
- str = str.substr(end + 1);
- continue;
- }
- }
-
- if (!str.empty())
- return textFunc(str);
-
- return true;
- }
- }
-
- // The below two functions aren't used anymore, not bothering with StringView for them
- String preprocessEscapeCodes(String const& s) {
- bool escape = false;
- std::string result = s.utf8();
-
- size_t escapeStartIdx = 0;
- for (size_t i = 0; i < result.size(); i++) {
- auto& c = result[i];
- if (isEscapeCode(c)) {
- escape = true;
- escapeStartIdx = i;
- }
- if ((c <= SpecialCharLimit) && !(c == StartEsc))
- escape = false;
- if ((c == EndEsc) && escape)
- result[escapeStartIdx] = StartEsc;
- }
- return {result};
- }
-
- String extractCodes(String const& s) {
- bool escape = false;
- StringList result;
- String escapeCode;
- for (auto c : preprocessEscapeCodes(s)) {
- if (c == StartEsc)
- escape = true;
- if (c == EndEsc) {
- escape = false;
- for (auto command : escapeCode.split(','))
- result.append(command);
- escapeCode = "";
- }
- if (escape && (c != StartEsc))
- escapeCode.append(c);
- }
- if (!result.size())
- return "";
- return "^" + result.join(",") + ";";
- }
-}
-
TextPositioning::TextPositioning() {
pos = Vec2F();
hAnchor = HorizontalAnchor::LeftAnchor;
diff --git a/source/rendering/StarTextPainter.hpp b/source/rendering/StarTextPainter.hpp
index 7f132fd..381a358 100644
--- a/source/rendering/StarTextPainter.hpp
+++ b/source/rendering/StarTextPainter.hpp
@@ -10,17 +10,6 @@ namespace Star {
STAR_CLASS(TextPainter);
-namespace Text {
- unsigned char const StartEsc = '\x1b';
- unsigned char const EndEsc = ';';
- unsigned char const CmdEsc = '^';
- unsigned char const SpecialCharLimit = ' ';
-
- String stripEscapeCodes(String const& s);
- String preprocessEscapeCodes(String const& s);
- String extractCodes(String const& s);
-}
-
enum class FontMode {
Normal,
Shadow
diff --git a/source/windowing/StarItemGridWidget.cpp b/source/windowing/StarItemGridWidget.cpp
index 494d09c..3302ca6 100644
--- a/source/windowing/StarItemGridWidget.cpp
+++ b/source/windowing/StarItemGridWidget.cpp
@@ -198,12 +198,9 @@ void ItemGridWidget::updateItemState() {
updateAllItemSlots();
auto newState = slotItemNames();
for (size_t i = 0; i < newState.size(); ++i) {
- if (newState[i].empty()) {
+ if (newState[i].empty())
m_changedSlots.remove(i);
- continue;
- }
-
- if (newState[i].compare(m_itemNames[i]) != 0)
+ else if (newState[i].compare(m_itemNames[i]) != 0)
m_changedSlots.insert(i);
}
m_itemNames = newState;
diff --git a/source/windowing/StarWidget.hpp b/source/windowing/StarWidget.hpp
index a9ea4ac..f6e9928 100644
--- a/source/windowing/StarWidget.hpp
+++ b/source/windowing/StarWidget.hpp
@@ -5,6 +5,7 @@
#include "StarCasting.hpp"
#include "StarInputEvent.hpp"
#include "StarGuiContext.hpp"
+#include "StarText.hpp"
namespace Star {