FEAT: Player data saving/loading
This commit is contained in:
@@ -68,6 +68,7 @@ bool SurvivalMode::destroyBlock( int x, int y, int z, int face ) {
|
||||
minecraft->player->inventory->clearSlot(minecraft->player->inventory->selected);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed && couldDestroy) {
|
||||
ItemInstance instance(t, 1, data);
|
||||
Tile::tiles[t]->playerDestroy(minecraft->level, minecraft->player, x, y, z, data);
|
||||
|
||||
@@ -395,6 +395,11 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SendInve
|
||||
auto items = packet->items;
|
||||
|
||||
minecraft->player->inventory->replace(items);
|
||||
|
||||
for (int i = 0; i < packet->linkedSlot.size(); i++) {
|
||||
minecraft->player->inventory->linkSlot(i, packet->linkedSlot[i].inventorySlot, true);
|
||||
LOGI("%i -> %i\n", packet->linkedSlot[i].inventorySlot, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "../raknet/PacketPriority.h"
|
||||
#include "platform/log.h"
|
||||
#include "world/item/ItemInstance.h"
|
||||
#include "world/level/storage/LevelStorage.h"
|
||||
#include "world/phys/Vec3.h"
|
||||
#include "world/item/crafting/Recipe.h"
|
||||
#include "world/item/crafting/Recipes.h"
|
||||
@@ -169,6 +170,8 @@ void ServerSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid)
|
||||
|
||||
if (player->owner == guid)
|
||||
{
|
||||
minecraft->level->getLevelStorage()->savePlayer(*player);
|
||||
|
||||
std::string message = player->name;
|
||||
message += " disconnected from the game";
|
||||
displayGameMessage(message);
|
||||
@@ -319,6 +322,36 @@ void ServerSideNetworkHandler::onReady_ClientGeneration(const RakNet::RakNetGUID
|
||||
}
|
||||
}
|
||||
|
||||
if (!minecraft->level->getLevelStorage()->loadPlayer(*newPlayer)) {
|
||||
LOGW("Failed to load %s data\n", newPlayer->name.c_str());
|
||||
}
|
||||
|
||||
// Credits to EpikIzCool
|
||||
bitStream.Reset();
|
||||
MovePlayerPacket mv(newPlayer->entityId, newPlayer->x, newPlayer->y - newPlayer->heightOffset,
|
||||
newPlayer->z, newPlayer->xRot, newPlayer->yRot);
|
||||
mv.write(&bitStream);
|
||||
|
||||
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
|
||||
|
||||
bitStream.Reset();
|
||||
SetHealthPacket hp(newPlayer->health);
|
||||
hp.write(&bitStream);
|
||||
|
||||
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
|
||||
|
||||
if (newPlayer->hasRespawnPosition()) {
|
||||
bitStream.Reset();
|
||||
SetSpawnPositionPacket sp(newPlayer->getRespawnPosition());
|
||||
sp.write(&bitStream);
|
||||
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
|
||||
}
|
||||
|
||||
bitStream.Reset();
|
||||
SendInventoryPacket(newPlayer, false).write(&bitStream);
|
||||
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
|
||||
|
||||
|
||||
// Additional packets
|
||||
// * set spawn
|
||||
/*
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
#include "../Packet.h"
|
||||
#include "world/entity/player/Inventory.h"
|
||||
#include "world/inventory/FillingContainer.h"
|
||||
#include <array>
|
||||
|
||||
class SendInventoryPacket: public Packet
|
||||
{
|
||||
public:
|
||||
SendInventoryPacket()
|
||||
{
|
||||
}
|
||||
SendInventoryPacket() {}
|
||||
|
||||
SendInventoryPacket(Player* player, bool dropItems)
|
||||
: entityId(player->entityId),
|
||||
@@ -22,10 +22,15 @@ public:
|
||||
ItemInstance* item = inv->getItem(i);
|
||||
items.push_back(item? *item : ItemInstance());
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumArmorItems; ++i) {
|
||||
ItemInstance* item = player->getArmor(i);
|
||||
items.push_back(item? *item : ItemInstance());
|
||||
}
|
||||
|
||||
for (int i = 0; i < inv->numLinkedSlots; ++i) {
|
||||
linkedSlot[i] = inv->linkedSlots[i];
|
||||
}
|
||||
}
|
||||
|
||||
void write(RakNet::BitStream* bitStream)
|
||||
@@ -40,6 +45,13 @@ public:
|
||||
// Armor
|
||||
for (int i = 0; i < NumArmorItems; ++i)
|
||||
PacketUtil::writeItemInstance(items[i + numItems], bitStream);
|
||||
|
||||
int lSlots = Inventory::MAX_SELECTION_SIZE;
|
||||
|
||||
// Linked slots
|
||||
bitStream->Write(lSlots);
|
||||
for (int i = 0; i < lSlots; ++i)
|
||||
bitStream->Write(linkedSlot[i]);
|
||||
}
|
||||
|
||||
void read(RakNet::BitStream* bitStream)
|
||||
@@ -51,6 +63,12 @@ public:
|
||||
// Inventory, Armor
|
||||
for (int i = 0; i < numItems + NumArmorItems; ++i)
|
||||
items.push_back(PacketUtil::readItemInstance(bitStream));
|
||||
|
||||
// Linked slots
|
||||
int lSlots = 0;
|
||||
bitStream->Read(lSlots);
|
||||
for (int i = 0; i < lSlots; ++i)
|
||||
bitStream->Read(linkedSlot[i]);
|
||||
}
|
||||
|
||||
void handle(const RakNet::RakNetGUID& source, NetEventCallback* callback)
|
||||
@@ -63,6 +81,8 @@ public:
|
||||
short numItems;
|
||||
unsigned char extra;
|
||||
|
||||
std::array<FillingContainer::LinkedSlot, Inventory::MAX_SELECTION_SIZE> linkedSlot;
|
||||
|
||||
static const int ExtraDrop = 1;
|
||||
static const int NumArmorItems = 4;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#include <cstddef>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#if !defined(DEMO_MODE) && !defined(APPLE_DEMO_PROMOTION)
|
||||
|
||||
#include "LevelData.h"
|
||||
@@ -88,6 +91,9 @@ ExternalFileLevelStorage::ExternalFileLevelStorage(const std::string& levelId, c
|
||||
{
|
||||
createFolderIfNotExists(levelPath.c_str());
|
||||
|
||||
std::string playerFolder = levelPath + "/players";
|
||||
createFolderIfNotExists(playerFolder.c_str());
|
||||
|
||||
std::string datFileName = levelPath + "/" + fnLevelDat;
|
||||
std::string levelFileName = levelPath + "/" + fnPlayerDat;
|
||||
loadedLevelData = new LevelData();
|
||||
@@ -113,6 +119,7 @@ void ExternalFileLevelStorage::saveLevelData(LevelData& levelData, std::vector<P
|
||||
|
||||
void ExternalFileLevelStorage::saveLevelData( const std::string& levelPath, LevelData& levelData, std::vector<Player*>* players )
|
||||
{
|
||||
// @todo: completely rewrite
|
||||
std::string directory = levelPath + "/";
|
||||
std::string tmpFile = directory + fnLevelDatNew;
|
||||
std::string datFile = directory + fnLevelDat;
|
||||
@@ -141,6 +148,67 @@ void ExternalFileLevelStorage::saveLevelData( const std::string& levelPath, Leve
|
||||
|
||||
// Remove the temporary save, if the rename didn't do it
|
||||
remove(tmpFile.c_str());
|
||||
|
||||
// Save players
|
||||
// fuck mojang for that
|
||||
if (!players || players->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& player : *players) {
|
||||
if (player != NULL) {
|
||||
savePlayer(*player, directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::savePlayer(Player& player, const std::string& worldDir) {
|
||||
std::string playerPath = worldDir + "/players/" + player.name + ".dat";
|
||||
|
||||
LOGI("Saving player %s to %s...\n", player.name.c_str(), playerPath.c_str());
|
||||
|
||||
RakNet::BitStream data;
|
||||
RakDataOutput buf(data);
|
||||
CompoundTag playerTag;
|
||||
player.saveWithoutId(&playerTag);
|
||||
|
||||
NbtIo::write(&playerTag, &buf);
|
||||
|
||||
std::ofstream file(playerPath, std::ios::out | std::ios::binary);
|
||||
file.write((const char*)data.GetData(), (size_t)data.GetNumberOfBytesUsed());
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorage::loadPlayer(Player& player, const std::string& worldDir) {
|
||||
std::string playerPath = worldDir + "/players/" + player.name + ".dat";
|
||||
|
||||
LOGI("Loading player %s from %s...\n", player.name.c_str(), playerPath.c_str());
|
||||
|
||||
std::ifstream file(playerPath, std::ios::in | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
RakNet::BitStream bitStream(data.data(), data.size(), false);
|
||||
RakDataInput stream(bitStream);
|
||||
|
||||
CompoundTag* tag = NbtIo::read(&stream);
|
||||
if (tag) {
|
||||
player.load(tag);
|
||||
tag->deleteChildren();
|
||||
delete tag;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExternalFileLevelStorage::savePlayer(Player& player) {
|
||||
ExternalFileLevelStorage::savePlayer(player, levelPath);
|
||||
}
|
||||
|
||||
bool ExternalFileLevelStorage::loadPlayer(Player& player) {
|
||||
return ExternalFileLevelStorage::loadPlayer(player, levelPath);
|
||||
}
|
||||
|
||||
LevelData* ExternalFileLevelStorage::prepareLevel(Level* _level)
|
||||
|
||||
@@ -67,6 +67,19 @@ public:
|
||||
void saveGame(Level* level);
|
||||
void saveAll(Level* level, std::vector<LevelChunk*>& levelChunks);
|
||||
|
||||
/**
|
||||
* @brief Save player to <world name>/player/<player name>.dat file
|
||||
*/
|
||||
static void savePlayer(Player& player, const std::string& worldDir);
|
||||
|
||||
/**
|
||||
* @brief Load player from <world name>/player/<player name>.dat file
|
||||
*/
|
||||
static bool loadPlayer(Player& player, const std::string& worldDir);
|
||||
|
||||
virtual void savePlayer(Player& player);
|
||||
virtual bool loadPlayer(Player& player);
|
||||
|
||||
virtual void tick();
|
||||
virtual void flush() {}
|
||||
private:
|
||||
|
||||
@@ -32,6 +32,9 @@ public:
|
||||
virtual void saveGame(Level* level) {}
|
||||
virtual void loadEntities(Level* level, LevelChunk* levelChunk) {}
|
||||
|
||||
virtual void savePlayer(Player& player) = 0;
|
||||
virtual bool loadPlayer(Player& player) = 0;
|
||||
|
||||
//void checkSession() throws LevelConflictException;
|
||||
//PlayerIO getPlayerIO();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user