Keep player at same Y position and grid_id (added new column to characters table) to address falling through objects after logging out

DB Update:
alter table characters add column grid_id int(10) unsigned not null default 0;
This commit is contained in:
Emagi 2025-08-13 09:57:27 -04:00
parent 2f558184b9
commit a7260e9f72
4 changed files with 15 additions and 11 deletions

View File

@ -2183,7 +2183,7 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
packet->setDataByName("pos_grid_id", 0);
}
else {
packet->setDataByName("pos_grid_id", new_grid_id != 0 ? new_grid_id : GetLocation());
packet->setDataByName("pos_grid_id", (new_grid_id != 0 && player != this) ? new_grid_id : GetLocation());
}
bool include_heading = true;
@ -2244,7 +2244,7 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b
else {
packet->setDataByName("pos_x", appearance.pos.X);
float result_y = appearance.pos.Y;
if(!IsWidget() && !IsSign() && !IsObject() && !(IsFlyingCreature() || IsWaterCreature() || InWater())) {
if(!IsWidget() && !IsSign() && !IsObject() && !(IsFlyingCreature() || IsWaterCreature() || InWater()) && player != this) {
result_y = new_y;
}
if(GetMap() != player->GetMap()) {

View File

@ -1832,7 +1832,7 @@ bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client*
MYSQL_ROW row, row4;
int32 id = 0;
query.escaped_name = getEscapeString(ch_name);
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login, zone_duplicating_id FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id);
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login, zone_duplicating_id, grid_id FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id);
// no character found
if ( result == NULL ) {
LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
@ -1850,7 +1850,7 @@ bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client*
client->SetAccountID(account_id);
client->GetPlayer()->SetName(ch_name);
client->GetPlayer()->SetX(atof(row[2]));
client->GetPlayer()->SetY(atof(row[3]));
client->GetPlayer()->SetY(atof(row[3]), false, true);
client->GetPlayer()->SetZ(atof(row[4]));
client->GetPlayer()->SetHeading(atof(row[5]));
client->SetAdminStatus(atoi(row[6]));
@ -1910,6 +1910,8 @@ SOGA chars looked ok in LoginServer screen tho... odd.
client->GetPlayer()->GetInfoStruct()->set_first_world_login(atoi(row[31]));
client->GetPlayer()->SetLocation(atoul(row[33]));
LoadCharacterFriendsIgnoreList(client->GetPlayer());
MYSQL_RES* result4 = query4.RunQuery2(Q_SELECT, "SELECT `guild_id` FROM `guild_members` WHERE `char_id`=%u", id);
if (result4 && (row4 = mysql_fetch_row(result4))) {
@ -4489,7 +4491,7 @@ void WorldDatabase::Save(Client* client){
char charName[64];
strncpy(charName, client->GetPlayer()->GetName(),64);
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set name='%s',current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u, zone_duplicating_id = %u where id = %u", getSafeEscapeString(charName).c_str(), zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetDuplicatingZoneID(), client->GetCharacterID());
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set name='%s',current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u, zone_duplicating_id = %u, grid_id = %u where id = %u", getSafeEscapeString(charName).c_str(), zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetDuplicatingZoneID(), client->GetPlayer()->GetLocation(), client->GetCharacterID());
Query query2;
query2.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u",
player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),

View File

@ -739,11 +739,13 @@ void Client::SendControlGhost(int32 send_id, int8 unknown2) {
void Client::BeginPreCharInfo() {
if (!IsReadyForSpawns()) {
if (GetPlayer()->GetMap()) {
if(!GetPlayer()->GetLocation()) { // find the appropriate grid id
auto loc = glm::vec3(GetPlayer()->GetX(), GetPlayer()->GetZ(), GetPlayer()->GetY());
uint32 GridID = 0;
float new_z = GetPlayer()->FindBestZ(loc, nullptr, &GridID);
GetPlayer()->SetLocation(GridID);
}
}
SetReadyForSpawns(true);
}
else {
@ -4953,7 +4955,7 @@ bool Client::GotoSpawn(const char* search_name, bool forceTarget) {
}
if (target && target != GetPlayer()) {
GetPlayer()->SetX(target->GetX());
GetPlayer()->SetY(y);
GetPlayer()->SetY(y, false, true);
GetPlayer()->SetZ(target->GetZ());
GetPlayer()->SetHeading(target->GetHeading());
GetPlayer()->SetLocation(target->GetLocation());

View File

@ -3393,7 +3393,7 @@ void ZoneServer::CheckTransporters(Client* client) {
bool foundZone = zone_list.GetZone(&zone_details, loc->destination_zone_id);
if(foundZone){
client->GetPlayer()->SetX(loc->destination_x);
client->GetPlayer()->SetY(loc->destination_y);
client->GetPlayer()->SetY(loc->destination_y, false, true);
client->GetPlayer()->SetZ(loc->destination_z);
client->GetPlayer()->SetHeading(loc->destination_heading);
client->Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false);