Пример #1
0
bool redisGetSession (Redis *self, RedisSessionKey *key, Session *session) {

    GameSession *gameSession = &session->game;
    SocketSession *socketSession = &session->socket;
    AccountSession *accountSession = &gameSession->accountSession;

    RedisSocketSessionKey *socketKey = &key->socketKey;

    // Search for the Socket Session
    if (!redisGetSocketSession(self, socketKey, socketSession)) {
        error("Cannot get Socket Session.");
        return false;
    }

    if (!socketSession->authenticated) {
        // This is the first time the client connect.
        // Initialize an empty game session
        CommanderInfo commanderInfo;
        commanderInfoInit (&commanderInfo);
        gameSessionInit (gameSession, &commanderInfo);
        dbg("Welcome, SOCKET_%s ! A new session has been initialized for you.", socketKey->sessionKey);
    } else {
        // Get account session
        RedisAccountSessionKey accountKey = {
            .accountId = socketSession->accountId
        };

        if (!redisGetAccountSession(self, &accountKey, accountSession)) {
            error("Cannot get Account Session.");
            return false;
        }

        // The client already exist in the game, get Game Session
        RedisGameSessionKey gameKey = {
            .routerId  = socketSession->routerId,
            .mapId     = socketSession->mapId,
            .accountId = socketSession->accountId
        };
        if (!redisGetGameSession(self, &gameKey, gameSession)) {
            error("Cannot get Game Session.");
            return false;
        }
        // dbg("Welcome back, SOCKET_%s !", sessionKey);
    }

    return true;
}

bool redisUpdateSession (Redis *self, Session *session) {

    RedisSocketSessionKey socketKey = {
        .routerId = session->socket.routerId,
        .sessionKey = session->socket.sessionKey
    };
    if (!redisUpdateSocketSession (self, &socketKey, &session->socket)) {
        error("Cannot update the socket session.");
        return false;
    }
    RedisGameSessionKey gameKey = {
        .routerId  = session->socket.routerId,
        .mapId     = session->socket.mapId,
        .accountId = session->socket.accountId
    };

    if (!(redisUpdateGameSession(self, &gameKey, session->socket.sessionKey, &session->game))) {
        error("Cannot update the game session");
        return false;
    }

    if (session->socket.accountId > 0) {
        RedisAccountSessionKey accountKey = {
            .accountId = session->socket.accountId
        };

        if (!(redisUpdateAccountSession(self, &accountKey, &session->game.accountSession))) {
            error("Cannot update the account session");
            return false;
        }
    }

    return true;
}

bool redisFlushSession (Redis *self, RedisSessionKey *key) {

    RedisSocketSessionKey *socketKey = &key->socketKey;
    // Retrieve the entire SocketSession
    SocketSession socketSession;

    if (!(redisGetSocketSession(self, socketKey, &socketSession))) {
        error("Cannot get the SocketSession for %s.", socketKey->sessionKey);
        return false;
    }

    // Flush Account Session
    RedisAccountSessionKey accountKey = {
        .accountId = socketSession.accountId
    };
    if (!(redisFlushAccountSession (self, &accountKey))) {
        error("Cannot flush the Account Session associated with the Socket Session.");
        return false;
    }

    // Flush the game session
    RedisGameSessionKey gameKey = {
        .routerId = socketSession.routerId,
        .mapId = socketSession.mapId,
        .accountId = socketSession.accountId
    };
    if (!(redisFlushGameSession (self, &gameKey))) {
        error("Cannot flush the Game Session associated with the Socket Session.");
        return false;
    }

    // Flush the socket session
    if (!(redisFlushSocketSession (self, socketKey))) {
        error("Cannot flush the Game Session associated with the Socket Session.");
        return false;
    }


    return true;
}
Пример #2
0
static PacketHandlerState zoneHandlerConnect(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    #pragma pack(push, 1)
    struct {
        uint32_t unk1;
        uint64_t accountId;
        uint64_t zoneServerId;
        uint8_t login[ACCOUNT_SESSION_LOGIN_MAXSIZE];
        uint8_t unk4;
        uint32_t zoneServerIndex;
        uint16_t unk3;
        uint8_t channelListId;
    } *clientPacket = (void *) packet;
    #pragma pack(pop)

    // TODO : Reverse CZ_CONNECT correctly
    // CHECK_CLIENT_PACKET_SIZE(*clientPacket, packetSize, CZ_CONNECT);

    // Get the Game Session that the Barrack Server moved
    RedisGameSessionKey gameKey = {
        .routerId = self->info.routerId,
        .mapId = -1,
        .accountId = clientPacket->accountId
    };
    if (!(redisGetGameSession(self->redis, &gameKey, &session->game))) {
        error("Cannot retrieve the game session.");
        return PACKET_HANDLER_ERROR;
    }

    // Check the client packet here(authentication)
    if (strncmp(session->game.accountSession.login,
                clientPacket->login,
                sizeof(session->game.accountSession.login)) != 0)
    {
        error("Wrong account authentication.(clientPacket account = <%s>, Session account = <%s>",
            clientPacket->login, session->game.accountSession.login);
        return PACKET_HANDLER_ERROR;
    }

    // Authentication OK !
    // Update the Socket Session
    socketSessionInit(&session->socket,
        clientPacket->accountId,
        self->info.routerId,
        session->game.commanderSession.mapId,
        session->socket.sessionKey,
        true
    );

    // Move the game Session to the current mapId
    RedisGameSessionKey fromKey = {
        .routerId = session->socket.routerId,
        .mapId = -1,
        .accountId = session->socket.accountId
    };
    RedisGameSessionKey toKey = {
        .routerId = session->socket.routerId,
        .mapId = session->socket.mapId,
        .accountId = session->socket.accountId
    };

    if (!(redisMoveGameSession(self->redis, &fromKey, &toKey))) {
        error("Cannot move the game session to the current mapId.");
        return PACKET_HANDLER_ERROR;
    }

    session->game.commanderSession.currentCommander.pos = PositionXYZ_decl(76.0f, 1.0f, 57.0f);

    zoneBuilderConnectOk(
        session->game.commanderSession.currentCommander.pcId,
        0, // GameMode
        0, // accountPrivileges
        &session->game.commanderSession.currentCommander, // Current commander
        replyMsg
    );

    return PACKET_HANDLER_UPDATE_SESSION;
}

static PacketHandlerState zoneHandlerJump(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    #pragma pack(push, 1)
    struct {
        uint8_t unk1;
    } *clientPacket = (void *) packet;
    #pragma pack(pop)

    CHECK_CLIENT_PACKET_SIZE(*clientPacket, packetSize, CZ_JUMP);

    // Notify the players around
    GameEventJump event = {
        .updatePosEvent = {
            .mapId = session->socket.mapId,
            .commanderInfo = session->game.commanderSession.currentCommander
        },
        .height = COMMANDER_HEIGHT_JUMP
    };
    workerDispatchEvent(self, session->socket.sessionKey, EVENT_TYPE_JUMP, &event, sizeof(event));

    return PACKET_HANDLER_OK;
}

static PacketHandlerState zoneHandlerOnAir(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    warning("CZ_ON_AIR not implemented yet.");
    return PACKET_HANDLER_OK;
}

static PacketHandlerState zoneHandlerOnGround(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg) {
    warning("CZ_ON_GROUND not implemented yet.");
    return PACKET_HANDLER_OK;
}
Пример #3
0
bool redisGetGameSessionBySocketId (Redis *self, uint16_t routerId, uint8_t *sessionKey, GameSession *gameSession) {

    SocketSession socketSession;
    RedisSocketSessionKey socketKey = {
        .routerId = routerId,
        .sessionKey = sessionKey
    };
    if (!(redisGetSocketSession(self, &socketKey, &socketSession))) {
        error("Cannot get the socket session of the client.");
        return false;
    }

    RedisGameSessionKey gameKey = {
        .routerId = socketSession.routerId,
        .mapId = socketSession.mapId,
        .accountId = socketSession.accountId
    };

    if (!(redisGetGameSession(self, &gameKey, gameSession))) {
        error("Cannot get the game session of the client.");
        return false;
    }

    return true;
}

bool redisUpdateGameSession(Redis *self, RedisGameSessionKey *key, uint8_t *socketId, GameSession *gameSession) {

    bool result = true;
    redisReply *reply = NULL;

    CommanderInfo *cInfo = &gameSession->commanderSession.currentCommander.info;
    CommanderAppearance *appearance = &cInfo->appearance;
    CommanderEquipment *equipment = &appearance->equipment;

    reply = redisCommandDbg(self,
        "HMSET zone%x:map%x:acc%llx"
        // Account
        " " REDIS_GAME_SESSION_account_sessionKey_str " %s"
        " " REDIS_GAME_SESSION_account_login_str " %s"
        " " REDIS_GAME_SESSION_account_privilege_str " %x"
        // Barrack
        " " REDIS_GAME_SESSION_barrack_charactersCreatedCount_str " %x"
        // Commander
        " " REDIS_GAME_SESSION_commander_mapId_str " %x"
        " " REDIS_GAME_SESSION_commander_commanderName_str " %s"
        " " REDIS_GAME_SESSION_commander_familyName_str " %s"
        " " REDIS_GAME_SESSION_commander_accountId_str " %llx"
        " " REDIS_GAME_SESSION_commander_classId_str " %x"
        " " REDIS_GAME_SESSION_commander_unk4_str " %x"
        " " REDIS_GAME_SESSION_commander_jobId_str " %x"
        " " REDIS_GAME_SESSION_commander_gender_str " %x"
        " " REDIS_GAME_SESSION_commander_unk5_str " %x"
        " " REDIS_GAME_SESSION_commander_level_str " %x"
        " " REDIS_GAME_SESSION_commander_hairId_str " %x"
        " " REDIS_GAME_SESSION_commander_pose_str " %x"
        // Equipment
        " " REDIS_GAME_SESSION_equipment_head_top_str " %x"
        " " REDIS_GAME_SESSION_equipment_head_middle_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk1_str " %x"
        " " REDIS_GAME_SESSION_equipment_body_armor_str " %x"
        " " REDIS_GAME_SESSION_equipment_gloves_str " %x"
        " " REDIS_GAME_SESSION_equipment_boots_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk2_str " %x"
        " " REDIS_GAME_SESSION_equipment_bracelet_str " %x"
        " " REDIS_GAME_SESSION_equipment_weapon_str " %x"
        " " REDIS_GAME_SESSION_equipment_shield_str " %x"
        " " REDIS_GAME_SESSION_equipment_costume_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk3_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk4_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk5_str " %x"
        " " REDIS_GAME_SESSION_equipment_leg_armor_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk6_str " %x"
        " " REDIS_GAME_SESSION_equipment_itemUnk7_str " %x"
        " " REDIS_GAME_SESSION_equipment_ring_left_str " %x"
        " " REDIS_GAME_SESSION_equipment_ring_right_str " %x"
        " " REDIS_GAME_SESSION_equipment_necklace_str " %x"
        // CommanderInfo Info
        " " REDIS_GAME_SESSION_info_posX_str " %f"
        " " REDIS_GAME_SESSION_info_posY_str " %f"
        " " REDIS_GAME_SESSION_info_posZ_str " %f"
        " " REDIS_GAME_SESSION_info_currentXP_str " %x"
        " " REDIS_GAME_SESSION_info_maxXP_str " %x"
        " " REDIS_GAME_SESSION_info_pcId_str " %x"
        " " REDIS_GAME_SESSION_info_socialInfoId_str " %llx"
        " " REDIS_GAME_SESSION_info_commanderId_str " %llx"
        " " REDIS_GAME_SESSION_info_currentHP_str " %x"
        " " REDIS_GAME_SESSION_info_maxHP_str " %x"
        " " REDIS_GAME_SESSION_info_currentSP_str " %x"
        " " REDIS_GAME_SESSION_info_maxSP_str " %x"
        " " REDIS_GAME_SESSION_info_currentStamina_str " %x"
        " " REDIS_GAME_SESSION_info_maxStamina_str " %x"
        " " REDIS_GAME_SESSION_info_unk6_str " %x"
        " " REDIS_GAME_SESSION_info_unk7_str " %x"
        , key->routerId, key->mapId, key->accountId,

        // Account
        socketId,
        gameSession->accountSession.login,
        gameSession->accountSession.privilege,
        // Barrack
        gameSession->barrackSession.charactersCreatedCount,
        // Commander
        gameSession->commanderSession.mapId,
        CHECK_REDIS_EMPTY_STRING(appearance->commanderName),
        CHECK_REDIS_EMPTY_STRING(appearance->familyName),
        key->accountId,
        appearance->classId,
        appearance->unk4,
        appearance->jobId,
        appearance->gender,
        appearance->unk5,
        appearance->level,
        appearance->hairId,
        appearance->pose,

        // Equipment
        equipment->head_top,
        equipment->head_middle,
        equipment->itemUnk1,
        equipment->body_armor,
        equipment->gloves,
        equipment->boots,
        equipment->itemUnk2,
        equipment->bracelet,
        equipment->weapon,
        equipment->shield,
        equipment->costume,
        equipment->itemUnk3,
        equipment->itemUnk4,
        equipment->itemUnk5,
        equipment->leg_armor,
        equipment->itemUnk6,
        equipment->itemUnk7,
        equipment->ring_left,
        equipment->ring_right,
        equipment->necklace,

        // CommanderInfo info
        cInfo->pos.x,
        cInfo->pos.y,
        cInfo->pos.z,
        cInfo->currentXP,
        cInfo->maxXP,
        cInfo->pcId,
        cInfo->socialInfoId,
        cInfo->commanderId,
        cInfo->currentHP,
        cInfo->maxHP,
        cInfo->currentSP,
        cInfo->maxSP,
        cInfo->currentStamina,
        cInfo->maxStamina,
        cInfo->unk6,
        cInfo->unk7
    );

    if (!reply) {
        error("Redis error encountered : The request is invalid.");
        result = false;
        goto cleanup;
    }

    switch (reply->type)
    {
        case REDIS_REPLY_ERROR:
            error("Redis error encountered : %s", reply->str);
            result = false;
            goto cleanup;
            break;

        case REDIS_REPLY_STATUS:
            // info("Redis status : %s", reply->str);
            break;

        default :
            error("Unexpected Redis status. (%d)", reply->type);
            result = false;
            goto cleanup;
            break;
    }

cleanup:
    if (reply) {
        redisReplyDestroy(&reply);
    }

    return result;
}
Пример #4
0
static PacketHandlerState zoneHandlerConnect(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    PacketHandlerState status = PACKET_HANDLER_ERROR;

    #pragma pack(push, 1)
    struct {
        uint32_t unk1;
        uint64_t accountId;
        uint64_t zoneServerId;
        uint8_t accountName[ACCOUNT_SESSION_ACCOUNT_NAME_MAXSIZE];
        uint8_t unk4;
        uint32_t zoneServerIndex;
        uint16_t unk3;
        uint8_t channelListId;
    } *clientPacket = (void *) packet;
    #pragma pack(pop)

    dbg("zoneHandlerConnect");
    dbg("unk1 %x", clientPacket->unk1);
    dbg("accountId %d", clientPacket->accountId);
    dbg("zoneServerId %d", clientPacket->zoneServerId);
    dbg("zoneServerIndex %d", clientPacket->zoneServerIndex);
    dbg("unk3 %x", clientPacket->unk3);
    dbg("unk4 %x", clientPacket->unk4);
    dbg("channelListId %d", clientPacket->channelListId);
    dbg("accountName %s", clientPacket->accountName);

    // TODO : Reverse CZ_CONNECT correctly
    // CHECK_CLIENT_PACKET_SIZE(*clientPacket, packetSize, CZ_CONNECT);

    GameSession tmpGameSession;
    AccountSession *tmpAccountSession = &tmpGameSession.accountSession;
    CommanderSession *tmpCommanderSession = &tmpGameSession.commanderSession;

    // TODO : Get the account session from Db.
    RedisAccountSessionKey accountKey = {
        .routerId = self->info.routerId,
        .mapId = SOCKET_SESSION_UNDEFINED_MAP,
        .accountId = clientPacket->accountId
    };
    if (!(redisGetAccountSession(self->redis, &accountKey, tmpAccountSession))) {
        error("Cannot retrieve the account session.");
        goto cleanup;
    }

    // Check the client packet here (authentication)
    // TODO improve it
    if (strncmp(tmpAccountSession->accountName,
                clientPacket->accountName,
                sizeof(tmpAccountSession->accountName)) != 0)
    {
        error("Wrong account authentication. (clientPacket account = <%s>, Session account = <%s>",
            clientPacket->accountName, tmpAccountSession->accountName);
        goto cleanup;
    }

    // === Authentication OK ! ===

    // Get list of Commanders for this AccountId
    size_t commandersCount;
    if (!(mySqlLoadAccountCommanders(self->sqlConn, tmpAccountSession, clientPacket->accountId, &commandersCount))) {
        error("Cannot load commanders.");
        goto cleanup;
    }

    // FIXME : Determine how to get the correct commander in the commander array
    tmpCommanderSession->currentCommander = commanderDup(tmpAccountSession->commanders[0]);

    // Get the Game Session that the Barrack Server moved
    // TODO : Should be replaced by Db
    RedisGameSessionKey gameKey = accountKey;
    if (!(redisGetGameSession(self->redis, &gameKey, &tmpGameSession))) {
        error("Cannot retrieve the game session.");
        goto cleanup;
    }

    // Update the Socket Session
    socketSessionInit(&session->socket,
        clientPacket->accountId,
        self->info.routerId,
        tmpCommanderSession->currentCommander->mapId,
        session->socket.sessionKey,
        true
    );

    // Move the game Session to the current mapId
    RedisGameSessionKey fromKey = {
        .routerId = session->socket.routerId,
        .mapId = -1,
        .accountId = session->socket.accountId
    };
    RedisGameSessionKey toKey = {
        .routerId = session->socket.routerId,
        .mapId = session->socket.mapId,
        .accountId = session->socket.accountId
    };
    // TODO : Should be replaced by Db
    if (!(redisMoveGameSession(self->redis, &fromKey, &toKey))) {
        error("Cannot move the game session to the current mapId.");
        goto cleanup;
    }

    // FIXME
    // Set a default position
    tmpCommanderSession->currentCommander->pos = PositionXYZ_decl(76.0f, 1.0f, 57.0f);

    // Update the session
    session->game = tmpGameSession;

    // Build a reply packet
    zoneBuilderConnectOk(
        0, // GameMode
        0, // accountPrivileges
        session->game.commanderSession.currentCommander,
        replyMsg
    );

    status = PACKET_HANDLER_UPDATE_SESSION;

cleanup:
    return status;
}

static PacketHandlerState zoneHandlerJump(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    #pragma pack(push, 1)
    struct {
        uint8_t unk1;
    } *clientPacket = (void *) packet;
    #pragma pack(pop)

    CHECK_CLIENT_PACKET_SIZE(*clientPacket, packetSize, CZ_JUMP);

    // Notify the players around
    GameEventJump event = {
        .updatePosEvent = {
            .mapId = session->socket.mapId,
            .commander = *session->game.commanderSession.currentCommander
        },
        .height = COMMANDER_HEIGHT_JUMP
    };
    workerDispatchEvent(self, session->socket.sessionKey, EVENT_TYPE_JUMP, &event, sizeof(event));

    return PACKET_HANDLER_OK;
}

static PacketHandlerState zoneHandlerOnAir(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg)
{
    warning("CZ_ON_AIR not implemented yet.");
    return PACKET_HANDLER_OK;
}

static PacketHandlerState zoneHandlerOnGround(
    Worker *self,
    Session *session,
    uint8_t *packet,
    size_t packetSize,
    zmsg_t *replyMsg) {
    warning("CZ_ON_GROUND not implemented yet.");
    return PACKET_HANDLER_OK;
}
Пример #5
0
bool redisGetGameSessionBySocketId(Redis *self, RouterId_t routerId, uint8_t *sessionKey, GameSession *gameSession) {

    SocketSession socketSession;
    RedisSocketSessionKey socketKey = {
        .routerId = routerId,
        .sessionKey = sessionKey
    };
    if (!(redisGetSocketSession(self, &socketKey, &socketSession))) {
        error("Cannot get the socket session of the client.");
        return false;
    }

    RedisGameSessionKey gameKey = {
        .routerId = socketSession.routerId,
        .mapId = socketSession.mapId,
        .accountId = socketSession.accountId
    };

    if (!(redisGetGameSession(self, &gameKey, gameSession))) {
        error("Cannot get the game session of the client.");
        return false;
    }

    return true;
}

bool redisUpdateGameSession(Redis *self, RedisGameSessionKey *key, uint8_t *socketId, GameSession *gameSession) {

    bool result = true;
    size_t repliesCount = 3;
    redisReply *replies[repliesCount];

    Commander *commander = NULL;

    // Initialize replies
    void *noReply = (void *) -1;
    for (int i = 0; i < repliesCount; i++) {
        replies[i] = noReply;
    }

    commander = gameSession->commanderSession.currentCommander;

    // Account
    replies[0] = redisCommandDbg(self,
        "HMSET zone%x:map%x:acc%llx"
        " " REDIS_SESSION_account_sessionKey_str " %s"
        " " REDIS_SESSION_account_accountName_str " %s"
        " " REDIS_SESSION_account_privilege_str " %x"
        " " REDIS_SESSION_account_commandersCountMax_str " %x",
        key->routerId, key->mapId, key->accountId,

        socketId,
        gameSession->accountSession.accountName,
        gameSession->accountSession.privilege,
        gameSession->accountSession.commandersCountMax
    );

    // Commander
    if (commander) {
        replies[1] = redisCommandDbg(self,
            "HMSET zone%x:map%x:acc%llx"
            " " REDIS_SESSION_commander_mapId_str " %x"
            " " REDIS_SESSION_commander_commanderName_str " %s"
            " " REDIS_SESSION_commander_familyName_str " %s"
            " " REDIS_SESSION_commander_accountId_str " %llx"
            " " REDIS_SESSION_commander_classId_str " %x"
            " " REDIS_SESSION_commander_jobId_str " %x"
            " " REDIS_SESSION_commander_gender_str " %x"
            " " REDIS_SESSION_commander_level_str " %x"
            " " REDIS_SESSION_commander_hairId_str " %x"
            " " REDIS_SESSION_commander_pose_str " %x"
            " " REDIS_SESSION_commander_posX_str " %f"
            " " REDIS_SESSION_commander_posY_str " %f"
            " " REDIS_SESSION_commander_posZ_str " %f"
            " " REDIS_SESSION_commander_currentXP_str " %x"
            " " REDIS_SESSION_commander_maxXP_str " %x"
            " " REDIS_SESSION_commander_pcId_str " %x"
            " " REDIS_SESSION_commander_socialInfoId_str " %llx"
            " " REDIS_SESSION_commander_commanderId_str " %llx"
            " " REDIS_SESSION_commander_currentHP_str " %x"
            " " REDIS_SESSION_commander_maxHP_str " %x"
            " " REDIS_SESSION_commander_currentSP_str " %x"
            " " REDIS_SESSION_commander_maxSP_str " %x"
            " " REDIS_SESSION_commander_currentStamina_str " %x"
            " " REDIS_SESSION_commander_maxStamina_str " %x",
            key->routerId, key->mapId, key->accountId,

            commander->mapId,
            CHECK_REDIS_EMPTY_STRING(commander->commanderName),
            CHECK_REDIS_EMPTY_STRING(commander->familyName),
            key->accountId,
            commander->classId,
            commander->jobId,
            commander->gender,
            commander->level,
            commander->hairId,
            commander->pose,
            commander->pos.x,
            commander->pos.y,
            commander->pos.z,
            commander->currentXP,
            commander->maxXP,
            commander->pcId,
            commander->socialInfoId,
            commander->commanderId,
            commander->currentHP,
            commander->maxHP,
            commander->currentSP,
            commander->maxSP,
            commander->currentStamina,
            commander->maxStamina
        );

        replies[2] = redisCommandDbg(self,
            "HMSET zone%x:map%x:acc%llx"
            " " REDIS_SESSION_EQSLOT_HAT_str " %x"
            " " REDIS_SESSION_EQSLOT_HAT_L_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN1_str " %x"
            " " REDIS_SESSION_EQSLOT_BODY_ARMOR_str " %x"
            " " REDIS_SESSION_EQSLOT_GLOVES_str " %x"
            " " REDIS_SESSION_EQSLOT_BOOTS_str " %x"
            " " REDIS_SESSION_EQSLOT_HELMET_str " %x"
            " " REDIS_SESSION_EQSLOT_BRACELET_str " %x"
            " " REDIS_SESSION_EQSLOT_WEAPON_str " %x"
            " " REDIS_SESSION_EQSLOT_SHIELD_str " %x"
            " " REDIS_SESSION_EQSLOT_COSTUME_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN3_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN4_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN5_str " %x"
            " " REDIS_SESSION_EQSLOT_LEG_ARMOR_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN6_str " %x"
            " " REDIS_SESSION_EQSLOT_UNKOWN7_str " %x"
            " " REDIS_SESSION_EQSLOT_RING_LEFT_str " %x"
            " " REDIS_SESSION_EQSLOT_RING_RIGHT_str " %x"
            " " REDIS_SESSION_EQSLOT_NECKLACE_str " %x"
            , key->routerId, key->mapId, key->accountId,

            // Equipment
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_HAT]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_HAT_L]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN1]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_BODY_ARMOR]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_GLOVES]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_BOOTS]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_HELMET]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_BRACELET]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_WEAPON]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_SHIELD]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_COSTUME]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN3]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN4]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN5]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_LEG_ARMOR]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN6]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_UNKOWN7]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_RING_LEFT]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_RING_RIGHT]),
            itemGetId((Item *) commander->inventory.equippedItems[EQSLOT_NECKLACE])
        );
    }

    for (int i = 0; i < repliesCount; i++) {
        redisReply *reply = replies[i];

        if (reply != noReply) {
            if (!reply) {
                error("Redis error encountered : The request is invalid.");
                result = false;
                goto cleanup;
            }
            switch (reply->type)
            {
                case REDIS_REPLY_ERROR:
                    error("Redis error encountered : %s", reply->str);
                    result = false;
                    goto cleanup;
                    break;

                case REDIS_REPLY_STATUS:
                    // Ok
                    break;

                default :
                    error("Unexpected Redis status. (%d)", reply->type);
                    result = false;
                    goto cleanup;
                    break;
            }
        }
    }

cleanup:
    for (int i = 0; i < repliesCount; i++) {
        redisReply *reply = replies[i];

        if (reply && reply != noReply) {
            redisReplyDestroy(&reply);
        }
    }
    return result;
}