diff --git a/changelog.txt b/changelog.txt index 4b2d029d5..a1a4e264c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 21/01/2016 == +Uleat: Disabled RoF+ clients from augmentation items not in their possessions slots (0-29, 9999, 251-330) to abate an exploit in the current code + == 10/17/2016 == Uleat: Moved namespace ItemField from item_instance.h to shareddb.cpp - the only place it is used Uleat: Separated class Inventory from item_instance files into inventory_profile files diff --git a/common/shareddb.cpp b/common/shareddb.cpp index fd55b3766..fc628ea6f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1700,6 +1700,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].pvpresistcalc=atoi(row[178]); sp[tempid].pvpresistcap=atoi(row[179]); sp[tempid].spell_category=atoi(row[180]); + sp[tempid].pcnpc_only_flag=atoi(row[183]); sp[tempid].cast_not_standing = atoi(row[184]) != 0; sp[tempid].can_mgb=atoi(row[185]); sp[tempid].dispel_flag = atoi(row[186]); diff --git a/common/spdat.h b/common/spdat.h index edb750f2a..ab00451e0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -735,7 +735,7 @@ struct SPDat_Spell_Struct /* 180 */ int spell_category; // -- GLOBAL_GROUP /* 181 */ //int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION /* 182 */ //int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP -/* 183 */ //int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG +/* 183 */ int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG /* 184 */ bool cast_not_standing; // this is checked in the client's EQ_Spell::IsCastWhileInvisSpell, this also blocks SE_InterruptCasting from affecting this spell -- CAST_NOT_STANDING /* 185 */ bool can_mgb; // 0=no, -1 or 1 = yes -- CAN_MGB /* 186 */ int dispel_flag; // -- NO_DISPELL diff --git a/common/version.h b/common/version.h index 0073d5415..93dfba189 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9100 +#define CURRENT_BINARY_DATABASE_VERSION 9101 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008 #else diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index dd0448ddc..e0960847e 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -43,8 +43,12 @@ if($Config{osname}=~/Win|MS/i){ $has_internet_connection = check_internet_connection(); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(); +if(-e "eqemu_server_skip_update.txt"){ + $skip_self_update_check = 1; +} + #::: Check for script self update -do_self_update_check_routine(); +do_self_update_check_routine() if !$skip_self_update_check; get_perl_version(); read_eqemu_config_xml(); get_mysql_path(); @@ -558,6 +562,7 @@ sub do_self_update_check_routine { } unlink("updates_staged/eqemu_server.pl"); + unlink("updates_staged"); } } @@ -720,13 +725,14 @@ sub show_menu_prompt { print " [utility_scripts] Download utility scripts to run and operate the EQEmu Server\n"; if($OS eq "Windows"){ print ">>> Windows\n"; - print " [windows_server_download] Updates server code from latest stable\n"; - print " [windows_server_download_bots] Updates server code (bots enabled) from latest\n"; + print " [windows_server_download] Updates server via latest 'stable' code\n"; + print " [windows_server_latest] Updates server via latest commit 'unstable'\n"; + print " [windows_server_download_bots] Updates server (bots) from latest 'stable'\n"; print " [fetch_dlls] Grabs dll's needed to run windows binaries\n"; print " [setup_loginserver] Sets up loginserver for Windows\n"; } print " \n> main - go back to main menu\n"; - print "Enter a command #> "; + print "Enter a command #> "; $last_menu = trim($input); } elsif($input eq "backup_database"){ database_dump(); $dc = 1; } @@ -741,6 +747,7 @@ sub show_menu_prompt { elsif($input eq "quests"){ quest_files_fetch(); $dc = 1; } elsif($input eq "lua_modules"){ lua_modules_fetch(); $dc = 1; } elsif($input eq "windows_server_download"){ fetch_latest_windows_binaries(); $dc = 1; } + elsif($input eq "windows_server_latest"){ fetch_latest_windows_appveyor(); $dc = 1; } elsif($input eq "windows_server_download_bots"){ fetch_latest_windows_binaries_bots(); $dc = 1; } elsif($input eq "fetch_dlls"){ fetch_server_dlls(); $dc = 1; } elsif($input eq "utility_scripts"){ fetch_utility_scripts(); $dc = 1; } @@ -1205,6 +1212,31 @@ sub copy_file { copy $l_source_file, $l_destination_file; } +sub fetch_latest_windows_appveyor { + print "[Update] Fetching Latest Windows Binaries (unstable) from Appveyor... \n"; + get_remote_file("https://ci.appveyor.com/api/projects/KimLS/server/artifacts/build_x86_pdb.zip", "updates_staged/master_windows_build_pdb.zip", 1); + get_remote_file("https://ci.appveyor.com/api/projects/KimLS/server/artifacts/build_x86.zip", "updates_staged/master_windows_build.zip", 1); + print "[Update] Fetched Latest Windows Binaries (unstable) from Appveyor... \n"; + print "[Update] Extracting... --- \n"; + unzip('updates_staged/master_windows_build.zip', 'updates_staged/binaries/'); + unzip('updates_staged/master_windows_build_pdb.zip', 'updates_staged/binaries/'); + my @files; + my $start_dir = "updates_staged/binaries"; + find( + sub { push @files, $File::Find::name unless -d; }, + $start_dir + ); + for my $file (@files) { + $destination_file = $file; + $destination_file =~s/updates_staged\/binaries\///g; + print "[Update] Installing :: " . $destination_file . "\n"; + copy_file($file, $destination_file); + } + print "[Update] Done\n"; + + rmtree('updates_staged'); +} + sub fetch_latest_windows_binaries { print "[Update] Fetching Latest Windows Binaries... \n"; get_remote_file($install_repository_request_url . "master_windows_build.zip", "updates_staged/master_windows_build.zip", 1); diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 5674271ae..aa72310ce 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -354,6 +354,7 @@ 9098|2016_08_26_object_size_tilt.sql|SHOW COLUMNS FROM `object` LIKE 'size'|empty| 9099|2016_08_27_ip_exemptions.sql|SHOW TABLES LIKE 'ip_exemptions'|empty| 9100|2016_08_27_object_display_name.sql|SHOW COLUMNS FROM `object` LIKE 'display_name'|empty| +9101|2016_12_01_pcnpc_only.sql|SHOW COLUMNS FROM `spells_new` LIKE 'pcnpc_only_flag'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2016_12_01_pcnpc_only.sql b/utils/sql/git/required/2016_12_01_pcnpc_only.sql new file mode 100644 index 000000000..836730e57 --- /dev/null +++ b/utils/sql/git/required/2016_12_01_pcnpc_only.sql @@ -0,0 +1,2 @@ +ALTER TABLE `spells_new` CHANGE `field183` `pcnpc_only_flag` INT(11) DEFAULT 0; +ALTER TABLE `spells_new` CHANGE `field184` `cast_not_standing` INT(11) DEFAULT 0; diff --git a/zone/client.cpp b/zone/client.cpp index 891591780..ddf10d9d5 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -424,6 +424,7 @@ Client::~Client() { void Client::SendZoneInPackets() { + ////////////////////////////////////////////////////// // Spawn Appearance Packet auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e05cc4f8c..0d4294132 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -493,6 +493,7 @@ int Client::HandlePacket(const EQApplicationPacket *app) // Finish client connecting state void Client::CompleteConnect() { + UpdateWho(); client_state = CLIENT_CONNECTED; SendAllPackets(); @@ -502,27 +503,6 @@ void Client::CompleteConnect() SetDuelTarget(0); SetDueling(false); - database.LoadPetInfo(this); - /* - This was moved before the spawn packets are sent - in hopes that it adds more consistency... - Remake pet - */ - if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { - MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); - if (GetPet() && GetPet()->IsNPC()) { - NPC *pet = GetPet()->CastToNPC(); - pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); - pet->CalcBonuses(); - pet->SetHP(m_petinfo.HP); - pet->SetMana(m_petinfo.Mana); - } - m_petinfo.SpellID = 0; - } - /* Moved here so it's after where we load the pet data. */ - if (!GetAA(aaPersistentMinion)) - memset(&m_suspendedminion, 0, sizeof(PetInfo)); - EnteringMessages(this); LoadZoneFlags(); @@ -1480,7 +1460,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadBuffs(this); uint32 max_slots = GetMaxBuffSlots(); - for (int i = 0; i < max_slots; i++) { + for (int i = 0; i < BUFF_COUNT; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { m_pp.buffs[i].spellid = buffs[i].spellid; m_pp.buffs[i].bard_modifier = buffs[i].instrument_mod; @@ -1649,6 +1629,23 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (m_pp.RestTimer) rest_timer.Start(m_pp.RestTimer * 1000); + /* Load Pet */ + database.LoadPetInfo(this); + if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { + MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); + if (GetPet() && GetPet()->IsNPC()) { + NPC *pet = GetPet()->CastToNPC(); + pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); + pet->CalcBonuses(); + pet->SetHP(m_petinfo.HP); + pet->SetMana(m_petinfo.Mana); + } + m_petinfo.SpellID = 0; + } + /* Moved here so it's after where we load the pet data. */ + if (!GetAA(aaPersistentMinion)) + memset(&m_suspendedminion, 0, sizeof(PetInfo)); + /* Server Zone Entry Packet */ outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; @@ -2933,6 +2930,19 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) bool deleteItems = false; if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) { + if ((in_augment->container_slot < 0 || in_augment->container_slot >= EQEmu::legacy::SLOT_CURSOR) && + in_augment->container_slot != EQEmu::legacy::SLOT_POWER_SOURCE && + (in_augment->container_slot < EQEmu::legacy::SLOT_PERSONAL_BAGS_BEGIN || in_augment->container_slot > EQEmu::legacy::SLOT_PERSONAL_BAGS_END)) + { + Message(13, "The server does not allow augmentation actions from this slot."); + auto cursor_item = m_inv[EQEmu::legacy::SLOT_CURSOR]; + auto augmented_item = m_inv[in_augment->container_slot]; + SendItemPacket(EQEmu::legacy::SLOT_CURSOR, cursor_item, ItemPacketCharInventory); + // this may crash clients on certain slots + SendItemPacket(in_augment->container_slot, augmented_item, ItemPacketCharInventory); + return; + } + EQEmu::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; //Log.Out(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", diff --git a/zone/effects.cpp b/zone/effects.cpp index 3e3f7a4bc..0cec65877 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -778,6 +778,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) continue; + // check PC/NPC only flag 1 = PCs, 2 = NPCs + if (spells[spell_id].pcnpc_only_flag == 1 && !curmob->IsClient() && !curmob->IsMerc()) + continue; + if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) + continue; if (spells[spell_id].targettype == ST_Ring) { dist_targ = DistanceSquared(static_cast(curmob->GetPosition()), caster->GetTargetRingLocation()); diff --git a/zone/entity.cpp b/zone/entity.cpp index 2bbcdef20..2ff83980e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4655,7 +4655,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) return ClosestMob; } -void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list) +void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list &m_list) { auto it = mob_list.begin(); while (it != mob_list.end()) { @@ -4664,6 +4664,14 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu ++it; continue; } + // check PC/NPC only flag 1 = PCs, 2 = NPCs + if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc()) { + ++it; + continue; + } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) { + ++it; + continue; + } float x_diff = ptr->GetX() - start->GetX(); float y_diff = ptr->GetY() - start->GetY(); float z_diff = ptr->GetZ() - start->GetZ(); diff --git a/zone/entity.h b/zone/entity.h index d2141d1b9..9a532cdb1 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -451,7 +451,7 @@ public: void GetObjectList(std::list &o_list); void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); - void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list); + void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list &m_list); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index f5b8c8382..d13ff4a52 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1806,6 +1806,11 @@ void Lua_Mob::ProcessSpecialAbilities(std::string str) { self->ProcessSpecialAbilities(str); } +uint32 Lua_Mob::GetAppearance() { + Lua_Safe_Call_Int(); + return self->GetAppearance(); +} + void Lua_Mob::SetAppearance(int app) { Lua_Safe_Call_Void(); self->SetAppearance(static_cast(app)); @@ -2291,6 +2296,7 @@ luabind::scope lua_register_mob() { .def("SetSpecialAbilityParam", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::SetSpecialAbilityParam) .def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities) .def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities) + .def("GetAppearance", (uint32(Lua_Mob::*)(void))&Lua_Mob::GetAppearance) .def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance) .def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance) .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 48d1f3e14..4fd1f0c69 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -348,6 +348,7 @@ public: void ClearSpecialAbilities(); void ProcessSpecialAbilities(std::string str); void SetAppearance(int app); + uint32 GetAppearance(); void SetAppearance(int app, bool ignore_self); void SetDestructibleObject(bool set); bool IsImmuneToSpell(int spell_id, Lua_Mob caster); diff --git a/zone/spells.cpp b/zone/spells.cpp index 3b232f9cd..814f01be6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3405,6 +3405,17 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(!IsValidSpell(spell_id)) return false; + // these target types skip pcnpc only check (according to dev quotes) + // other AE spells this is redundant, oh well + // 1 = PCs, 2 = NPCs + if (spells[spell_id].pcnpc_only_flag && spells[spell_id].targettype != ST_AETargetHateList && + spells[spell_id].targettype != ST_HateList) { + if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc()) + return false; + else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc())) + return false; + } + uint16 caster_level = level_override > 0 ? level_override : GetCasterLevel(spell_id); Log.Out(Logs::Detail, Logs::Spells, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level); @@ -5623,7 +5634,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) std::list targets_in_range; entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].range, - spells[spell_id].range / 2, targets_in_range); + spells[spell_id].range / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); auto iter = targets_in_range.begin(); float dX = 0; @@ -5698,7 +5709,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) std::list targets_in_range; entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, - spells[spell_id].aoerange / 2, targets_in_range); + spells[spell_id].aoerange / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); auto iter = targets_in_range.begin(); while (iter != targets_in_range.end()) {