Compare commits

...

243 Commits

Author SHA1 Message Date
Xackery cd01ed87df Added missed pet summons 2021-11-23 18:16:22 -08:00
Xackery 8a79994b4e Added more elixir error constants 2021-11-18 09:51:55 -08:00
Xackery 93a661a434 Removed debug line 2021-11-16 17:19:37 -08:00
Xackery 3e8796bb4c Added ElixirSpellCache, #elixircheck 2021-11-16 17:03:42 -08:00
Xackery c23fc4ade7 Added Elixir 2021-11-15 20:55:40 -08:00
Xackery 3b628c1ace Added tweaks to dev containers 2021-11-15 20:54:28 -08:00
JJ 27f8ae3999 [Hotfix] Optional SQL for existing servers (#1756) 2021-11-13 22:15:49 -05:00
JJ 80a891e541 Make room for host names.
Since m_remote_ip_address does not resolve IP address, world server may still use host name.
2021-11-13 20:14:39 -05:00
KayenEQ 776449aa3d [Spells] Update to IsCombatProc checks (#1741)
* Update spells.cpp

* Update spells.cpp

* Update spells.cpp

* Update spells.cpp
2021-11-13 14:47:42 -05:00
JJ cef352f0ac [Bug Fix] Removed unused pointer. Fixes #157. (#1748) 2021-11-13 10:39:35 -05:00
Kinglykrab e8607a0c78 [Commands] Cleanup #checklos Command. (#1744)
- Cleanup message and logic.
2021-11-13 05:25:58 -05:00
Kinglykrab 9c55cf9a8e [Bug Fix] Loginserver Error String Constants. (#1747)
- Constant was named after Windows macro.
2021-11-13 02:00:45 -05:00
Chris Miles a9d1034298 [Loginserver] Worldserver Name Sanitization (#1739)
* Sanitize bad words in server names

* Add config options and enforcement for dev/test servers and servers starting with a special character

* Refine bad word logic

* Add installer to dev/test servers

* Change server prefixes

* Special char prefix

* Formatting

* Remove multi words

* Add server types enum

* Add error constants

* Remove sanitize from world level

* Use strn0cpy
2021-11-12 23:02:05 -06:00
Kinglykrab 8b83a13560 [Rules] Add Archery/Throwing Ammo Consumption Rules. (#1743)
- Add RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
- Add RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
2021-11-12 23:04:09 -05:00
hg 3c87480553 [Quest API] Add back removed lua class properties (#1742)
Fixes regression from 7b6decae
2021-11-12 21:16:39 -05:00
Kinglykrab 1a69218045 [Commands] Cleanup #faction Command. (#1716)
* [Commands] Cleanup #faction Command.
- Remove find subcommand as we have #findfaction now.
- Cleanup message.

* Remove #setfaction message.
2021-11-12 18:28:14 -05:00
Kinglykrab e870ee5e0e [Commands] Remove #logtest Command. (#1731)
- Remove unused/deprecated command.
2021-11-12 14:46:05 -06:00
Kinglykrab 908c6c18af [Commands] Cleanup #findnpctype Command. (#1714)
* [Commands] Cleanup #findnpctype Command.
- Cleanup message and logic.

* Logic cleanup, found_count is always greater than 0.

* Fix order.

* Add return.
2021-11-12 09:19:43 -05:00
Kinglykrab f591378ed3 [Commands] Cleanup #viewnpctype Command. (#1713)
* [Commands] Cleanup #viewnpctype Command.
- Create a temporary NPC to use ShowStats() instead.
- Cleanup message.

* Cleanup spawn/emote/textures logic in ShowStats() when unused.

* Formatting.
2021-11-12 08:58:43 -05:00
Kinglykrab 0997a8a31e [Commands] Remove #bug Command. (#1737)
- Remove unused command.
2021-11-12 08:23:22 -05:00
Kinglykrab 0bf6627fb0 [Commands] Remove #crashtest Command. (#1734)
- Remove unused/deprecated command.
2021-11-12 08:19:58 -05:00
Kinglykrab fb8539e679 [Commands] Cleanup #endurance Command. (#1719)
- Add message.
2021-11-12 08:11:45 -05:00
Kinglykrab f8c2e85f3e [Commands] Cleanup #mana Command. (#1718)
* [Commands] Cleanup #mana Command.
- Add message.

* Add self message.
2021-11-12 08:10:43 -05:00
Kinglykrab 110d2a0e10 [Commands] Cleanup #heal Command. (#1717)
* [Commands] Cleanup #heal Command.
- Add message.

* Remove target requirement.

* Add self message.

* Typo.
2021-11-12 08:06:53 -05:00
Kinglykrab 8d9415191a [Commands] Cleanup #reloadquest Command. (#1712)
- Cleanup message and logic.
2021-11-12 07:42:22 -05:00
Kinglykrab 7178a7e55d [Commands] Remove #clearinvsnapshots Command. (#1736)
* [Commands] Remove #clearinvsnapshots Command.
- Remove unused command.

* Web editor conflict fail

Co-authored-by: JJ <3617814+joligario@users.noreply.github.com>
2021-11-12 07:41:46 -05:00
Kinglykrab 9e8d03d92d [Commands] Remove #connectworldserver Command. (#1735)
- Remove unused command.
2021-11-12 05:58:18 -05:00
Kinglykrab f4a70eff43 [Commands] Remove #d1 Command. (#1733)
- Remove unused command.
2021-11-12 05:58:01 -05:00
Kinglykrab f9ec45c7ff [Commands] Remove #ipc Command. (#1732)
- Remove unused command.
2021-11-12 05:57:43 -05:00
Kinglykrab cf8bf9e4fc [Commands] Remove #manastat Command. (#1730)
- Remove unused command.
2021-11-12 05:57:25 -05:00
Kinglykrab 575237d764 [Commands] Remove #mysqltest Command. (#1729)
- Removed unused/deprecated command.
2021-11-12 05:57:09 -05:00
Kinglykrab e12e8df3ef [Commands] Remove #numauths Command. (#1728)
- Remove unused command.
2021-11-12 05:56:50 -05:00
Kinglykrab 8d7b7d6cc4 [Commands] Remove #optest Command. (#1727)
- Remove unused command.
2021-11-12 05:56:32 -05:00
Kinglykrab 6f79ea117c [Commands] Remove #refundaa Command. (#1726)
- Remove unused command.
2021-11-12 05:56:11 -05:00
Kinglykrab 7b022502da [Commands] Remove #qtest Command. (#1725)
- Remove unused command.
2021-11-12 05:55:54 -05:00
Kinglykrab 4a376b7859 [Commands] Remove #testspawn and #testspawnkill Commands. (#1724)
- Remove unused commands.
2021-11-12 05:55:35 -05:00
Kinglykrab 87cdf7feb1 [Commands] Remove #synctod Command. (#1723)
- Remove unused command.
2021-11-12 05:55:06 -05:00
Kinglykrab caf32290b8 [Commands] Remove #sendop Command. (#1722)
- Remove unused command.
2021-11-12 05:54:44 -05:00
JJ 7d495c56b3 [Logs] Show local_address in correct location (#1721) 2021-11-12 05:54:21 -05:00
Kinglykrab dc1c7bb284 [Commands] Remove #serversidename Command. (#1720)
- Remove unused command.
2021-11-12 05:53:44 -05:00
Chris Miles a6c85babfc [Loginserver] Add config option to display player count (#1738)
* [Loginserver] Add config option to display player count

* Update name
2021-11-11 22:38:41 -06:00
hg cbea7045fa [Loginserver] Identify unknown login client packet fields (#1680)
* Add player login reply struct

* Use player login reply struct for failed logins

* Use base message struct for login requests

* Refactor server list reply serialization

Use BaseMessage and BaseReplyMessage structs for server list
and add flags for server type and status

* Use reply message struct for login handshake

Remove client version checks, the packets are the same for titanium and rof2

* Use base headers for join server requests

* Log correct server list ip

* Add compressed flag to base message header

Document encrypt type flag more
2021-11-11 20:13:30 -06:00
KayenEQ 099759c477 [Commands] #tune command rewrite (#1677)
* tune updates

* Update tune.cpp

* tune update

* updates

* updates

* less zero

* update

* up

* u

* Update tune.cpp

* Update tune.cpp

* avoidance working

* accuracy

* save1

* Update tune.cpp

* override

* Removed Old Tune Code

* cleanup1

* up

* finished v1

* Update command.cpp

* Update command.cpp

* spellfix

* Update command.cpp

* remove test command

* added SYNC comments

Hopefully if anyone changes these functions they will change the corresponding tune

* Tune_ to Tune

Co-authored-by: Akkadius <akkadius1@gmail.com>
2021-11-11 19:41:59 -06:00
cybernine186 65197ac027 [Rules] Gate /tgb, /autofire and /melody (#1679)
* Rules to negate /tgb, /autofire, and /melody

Created new rules to negate server and client side effects of commands: /tgb, /autofire, and /melody. These commands are enabled by default and can be disabled to enforce a classic EQ experience if using progression style play for example.

Rules
--------------
RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.")
RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.")
RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.")

* Removed sql query for rules per Mackal recommendation.
2021-11-11 19:41:03 -06:00
Alex e4bd6f5bd2 [Networking] Servertalk Legacy World Connections for Login (#1662)
* servertalk server connections will now attempt to parse legacy connections as well as modern ones

* Some fixes for legacy connections

* Change legacy default from local to eqemu
2021-11-11 19:37:35 -06:00
mmcgarvey acf5836253 [Rules] Add optional rule for lifetap heals (#1689)
What:
	Add toggle for compounding bonuses for lifetap heals.

Why:
	When spell damage and heal amount bonuses are scaled to ludicrous
	levels, this double dip results in very high heals from
	relatively weak lifetaps.

Created new rule:  Spells:CompoundLifetapHeals

If true (default):
	Apply spell damage bonuses to lifetap damage
	Pass that amount through heal bonuses
	Heal for this resulting amount
If false:
	Apply spell damage bonuses to lifetap damage
	Heal for this resulting amount
2021-11-11 19:37:14 -06:00
KayenEQ 17c8e8414c [Spells] Fixed proc rate for Ranged procs (#1715) 2021-11-11 19:27:50 -06:00
KayenEQ 239033a269 [Bug Fix] Prevent critical DOTs from affecting beneficial damage over time (#1710)
* no critical from lich

* better
2021-11-11 18:32:16 -05:00
Kinglykrab fa07064466 [Commands] Cleanup #cvs Command. (#1709)
* [Commands] Cleanup #cvs Command.
- Cleanup message and display.
- Add Total Clients to message.
- Add Unique IPs to message.

* Formatting.

* Formatting.
2021-11-11 16:48:50 -05:00
Kinglykrab 994ef712b2 [Commands] Cleanup #cast Command. (#1706)
* [Commands] Cleanup #cast Command.
- Cleanup message.

* Add optional cast non-instant parameter.
- Fix cast time.

* Fix message.
2021-11-11 16:48:35 -05:00
KayenEQ 33c30d3cbb [Bug Fix] Fix for dual wield animation when same delay weapons. (#1671)
* DW animation fix

* spelling

* better animation

looks better for low skill where dw doesn't fire as often.

* Update attack.cpp
2021-11-10 21:27:51 -05:00
KayenEQ d9c8e80bca [Spells] Allow item click effects to have cast time and recast time modified by focus effects. (#1695)
* prelim

* Spell Focus implemented

* AA implemented

* Update spdat.h

* Update spdat.h

* working

* Update spells.cpp

* prelim excludes

* enum limit expansion

* overhaul

* v2 testing

* updates

* working

* Fin

* Update spell_effects.cpp

* Update spell_effects.cpp

* update

* Update spells.cpp

* fix

* fix

* Update spell_effects.cpp

* remove debugs

* Update spells.cpp
2021-11-10 21:23:49 -05:00
Kinglykrab 990729fe21 [Commands] Cleanup #distance Command. (#1707)
- Cleanup message.
2021-11-10 21:21:06 -05:00
Kinglykrab aac0dd2993 [Commands] Cleanup #setlanguage Command. (#1705)
* [Commands] Cleanup #setlanguage Command.
- Cleanup message and lofic.
- Add GetLanguageName() helper and GetLanguageMap() for future use.

* Optimize GetLanguageName().
2021-11-10 21:20:51 -05:00
Kinglykrab b17c24d2df [Commands] Cleanup #setskill Command. (#1704)
* [Commands] Cleanup #setskill Command.
- Cleanup message and logic.

* Optimize GetSkillName().
2021-11-10 21:20:40 -05:00
Kinglykrab 32d606c667 [Bug Fix] Fix Mob::ShowStats() Proximity Display. (#1708) 2021-11-10 19:48:02 -05:00
Kinglykrab 6661672e2d [Commands] Cleanup #showskills Command. (#1698)
* [Commands] Cleanup #showskills Command.
- Cleanup display and use GetSkillName() helper method.

* Add optional "all" parameter to show all skills.

* Formatting.

* Formatting.

* Target, not c.
2021-11-09 23:24:46 -05:00
Kinglykrab b5391b9110 [Commands] Cleanup #showstats Command. (#1700)
- Convert Mob::ShowStats() to use the #npcstats code and make #npcstats use Mob::ShowStats().
2021-11-09 21:25:42 -05:00
Kinglykrab e306059f43 [Commands] Cleanup #showspellslist Command. (#1703)
- Cleanup messages and display.
2021-11-09 21:24:34 -05:00
Kinglykrab a64e326c68 [Commands] Cleanup #viewzoneloot Command. (#1702)
- Cleanup message logic.
2021-11-09 21:24:25 -05:00
Kinglykrab 605b3d3a27 [Commands] Cleanup #fov Command. (#1701)
- Cleanup message.
2021-11-09 21:24:17 -05:00
Kinglykrab 248e6d44db [Commands] Cleanup #npccast Command. (#1699)
- Cleanup messages and display.
2021-11-09 21:23:48 -05:00
Kinglykrab 328a94e2d4 [Commands] Add #findfaction Command. (#1697)
- Add #findfaction [search criteria] command.
- Cleanup other #find command messages/logic.
- Add GetMaxFaction() helper method.
- Add races.h defines for races 725-732.
2021-11-09 21:23:38 -05:00
Michael Cook (mackal) 211196a722 Fix Channel TellEcho issues (#1676)
These were missed switching them to TellEcho from a previous change
2021-11-09 10:54:54 -05:00
Kinglykrab 0b283e60db [Commands] Remove #listnpcs Command. (#1696)
- Unused command.
2021-11-07 18:32:33 -05:00
Kinglykrab 90871cb3d9 [Commands] Cleanup #worldshutdown Command. (#1694)
- Cleanup system messages and magic numbers.
2021-11-07 17:21:42 -05:00
Kinglykrab bf92845a4a [Rules] Add Resurrection Sickness rules for Characters/Bots. (#1692)
* [Rules] Add Resurrection Sickness rule for Characters/Bots.
- Add RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")
- Add RULE_BOOL(Bots, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore")
- Add RULE_BOOL(Bots, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")

* Add rules for spell IDs.

* Fix bot health on spawn when resurrection sickness is disabled.
- Formatting.

* Remove 'this' keyword.
2021-11-07 17:21:34 -05:00
Kinglykrab f8cbc2faed [Commands] Further implement #worldwide functionality. (#1693)
- Add #worldwide remove [Spell ID] - Removes a spell from player buffs worldwide.
- Add #worldwide message [Message] - Sends a worldwide message in Chat::Yellow.
- Add #worldwide move [Zone ID] or #worldwide move [Zone Short Name] - Moves every player in the game to the specified zone.
- Add #worldwide moveinstance [Instance ID] - Moves every player in the game to the specified instance.
- All but `#worldwide message` send a message to sender client.
2021-11-07 17:21:22 -05:00
Kinglykrab 30fdb18945 [Bug Fix] Fix Elemental Illusion spells not using proper texture. (#1691) 2021-11-07 17:21:04 -05:00
Kinglykrab 062fb73f03 [Commands] Remove #test, #spon, and #spoff Commands. (#1686)
- These commands are unused or outdated.
2021-11-07 17:20:55 -05:00
Kinglykrab 194c71727d [Commands] Cleanup #npcstats Command. (#1690)
- Cleanup menu and add stats that were not there before.
- Only display some data if necessary (i.e only show loot/money if they have loot/money)
- Add skill name helper method.
- Add faction name helper method.
- Add Charmed stats and other getter methods.
- Cleanup QueryLoot() method.
2021-11-07 17:20:43 -05:00
Kinglykrab e1de3d2ae0 [Commands] Cleanup #zstats Command. (#1687)
- Add new data from NewZone_Struct to command and clean up display.
2021-11-07 17:15:03 -05:00
KayenEQ 7f497f9d32 [Spells] Implemented SPA 415 SE_FFItemClass (#1688)
* prelim

* Spell Focus implemented

* AA implemented

* Update spdat.h

* Update spdat.h

* prelim excludes

* enum limit expansion

* overhaul

* v2 testing

* updates

* working

* Fin

* Update spell_effects.cpp

* Update spell_effects.cpp

* var fix

* Update spell_effects.cpp

make it not apply to casted spells... oops

* Update spell_effects.cpp

* Update spell_effects.cpp
2021-11-07 16:35:30 -05:00
KayenEQ 1cdb1816a2 [Bug Fix] SOF+ clients item click recast timer not met check (#1682)
* Update client_packet.cpp

* Update spells.cpp

* augs working too
2021-11-06 23:14:36 -04:00
Kinglykrab bc82b897c5 [Commands] Add #emptyinventory Command. (#1684)
* [Commands] Add #emptyinventory Command.
- Allows you empty you or your target's inventory completely. (Equipment, General, Bank, and Shared Bank)
- Fixed an issue not allowing quest::removeitem(item_id, quanity) to remove 0 charge items.
- Fixed an issue not allowing eq.remove_item(item_id, quanity) to remove 0 charge items.

* Update command.cpp

* Update client.cpp
2021-11-06 22:34:04 -04:00
hg beb4de0b45 [Cleanup] Remove unused door variable. (#1685) 2021-11-06 21:57:05 -04:00
KayenEQ 785926a584 [Quest API] Added NPC special ability to modify Riposte/Dodge/Parry/Block chance (#1683)
* Update attack.cpp

* u

* Update attack.cpp

* spellchecked
2021-11-06 21:06:14 -04:00
Natedog2012 5c7972345a [Bug Fix] Fix startzone rule to default players to correct zone when not found … (#1669)
* Fix startzone rule to default players to correct zone when not found in database

* Formatting

Co-authored-by: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com>
2021-11-06 18:22:52 -04:00
Natedog2012 886f00ed50 Fix resetAA to actually remove all AAs except granted AAs (#1681) 2021-11-06 17:36:19 -04:00
Kinglykrab b983fac860 [Quest API] Alphabetize Perl method exports. (#1672)
- Keeps things tidier.
Perl script was used to get this in order easily.
```pl
my @perl_file_types = (
	"bot",
	"client",
	"doors",
	"entity",
	"expedition",
	"groups",
	"hateentry",
	"inventory",
	"mob",
	"npc",
	"object",
	"perlpacket",
	"player_corpse",
	"questitem",
	"raids",
	"spell"
);

foreach my $file_type (sort {$a cmp $b} @perl_file_types) {
	my $perl_file = "perl_$file_type.cpp";
	open my $client_file, '<', $perl_file or die "Cannot open file_name $perl_file";
	{
		local $/;
		$content = <$client_file>;
	}
	close $client_file;

	open my $perl_data_file, ">", "perl_$file_type\_data.cpp";

	my @variables = ();

	foreach my $line (split("\n", $content)) {
		if ($line=~/newXSproto\(/i) {
			$line =~ s/\s+/ /g;
			my @line_data = split(/ /, $line);
			push(@variables, join(" ", @line_data));
		}
	}

	foreach my $variable (sort {$a cmp $b} @variables) {
		$variable =~ s/^ //ig;
		print $perl_data_file "\t$variable\n";
	}

	close $perl_data_file;
}```
2021-11-06 17:36:06 -04:00
Kinglykrab 7b6decaef3 [Quest API] Alphabetize Lua method exports. (#1673)
- Keeps things tidier.
- Removes unnecessary/outdated comments at the top of files.
2021-11-06 17:36:00 -04:00
Kinglykrab 8d8301fbd7 [Commands] Add #findskill [search criteria] Command. (#1674)
* [Commands] Add #findskill [search criteria] Command.
- Allows you to search for skills by ID or partial name.

* Add error message.

* Update command.cpp

* Update command.cpp

* Update command.cpp
2021-11-06 17:35:43 -04:00
Kinglykrab b4aa401210 [Commands] Add #findtask [search criteria] Command. (#1675)
* [Commands] Add #findtask [search criteria] Command.
- Allows you to search for Tasks by ID or partial name.

* Update command.cpp
2021-11-06 17:35:37 -04:00
KayenEQ f1bfd6bc2a [Spells] Implemented SPA 512 SE_Proc_Timer_Modifier, Fixed AA procs not working (#1646)
* update for SPA 511

* remove debugs, AA implemented

* update

* twinprocfix

* AA procs added

* format update

* update

* proctimer limits

* update

* rename function

renamed function
only check for buffs value > 0, don't need to check for AA's which are negative ID's

* pre merge

* variable updates

* Update spell_effects.cpp

* var rename

update var name to better represent its function.

* updated proc struct

added reuse timer

* reuse timer to spell procs

* updates

* debug remove

* Update mob.cpp

* fix

* merge
2021-11-05 14:14:11 -04:00
KayenEQ 8c95323728 [Spells] Update to Charm target restriction code (#1666)
* charm target restrictions

* fixed

* Update spells.cpp

* Update spells.cpp

* Update spells.cpp

only send spell bar when we have to, avoid potential exploit.

* logs
2021-11-05 10:39:17 -04:00
KayenEQ 5874deeffc Update spell_effects.cpp (#1670) 2021-11-03 21:07:45 -04:00
KayenEQ 18cc648c8d Update spell_effects.cpp (#1668) 2021-11-03 19:01:08 -04:00
Kinglykrab 9d515b20f2 [Quest API] Simplify bulk Scribe and Train logic. (#1660)
* [Quest API] Simplify bulk Scribe and Train logic.
- Add $client->GetFreeDisciplineSlot(starting_slot) to Perl.
- Add $client->ScribeSpells(min_level, max_level) to Perl.
- Add $client->LearnDisciplines(min_level, max_level) to Perl.
- Add client:GetNextAvailableDisciplineSlot(starting_slot) to Lua.
- Add client:ScribeSpells(min_level, max_level) to Lua.
- Add client:LearnDisciplines(min_level, max_level) to Lua.
Convert quest::scribespells() and quest::traindisc() to use new ScribeSpells and LearnDisciplines methods for consistency.

* Update command.cpp
2021-11-03 18:31:13 -04:00
Kinglykrab 17aaab1f9d [Quest API] Add Spell methods to Perl. (#1631)
* [Quest API] Add Spell methods to Perl.
- Add quest::getspell(spell_id) to Perl.
- Add eq.get_spell(spell_id) to Lua.
These methods return a spell object.

Exports $spell object references to spell events.

* Formatting.

* Remove comment.

* Update spdat.cpp

* Amplication typo.

* Fix conflicts.

* Remove repository changes.

* Fix typing.

* Update spell_effects.cpp
2021-11-03 17:47:15 -04:00
Chris Miles 6e26e8953c [Loginserver] Health Checks (#1665)
* Health checks stash

* Healthcheck work
2021-11-03 14:39:51 -05:00
Natedog2012 e4138b871b [Rules] Add optional rules for HealAmt and SpellAmt to scale DoTs/HoTs. (#1661)
* Add optional rules for itembonuses HealAmt and SpellAmt to scale for DoTs/HoTs

* Fix typo

* Only 1 rulecheck

* Apply +healingitems and +dmgitems after focus effects so they scale properly

* Fix dots / hots to not always use PVPScaling for extra_dmg / extra_healing

Adjust +healamt and +spelldmg to scale over the full duration of the spell, Thanks Kayen
2021-11-02 15:12:07 -05:00
splose 4ac525afc2 [Rules] Missing Character:TradeskillUpTailoring (#1667) 2021-11-02 12:57:16 -05:00
Chris Miles 05782433b8 [Loginserver] Add some resiliency to LS requests (#1663) 2021-11-02 00:19:13 -05:00
KayenEQ 9af7122b1d [BugFix] Remove potential nullptrs in Virus Code (#1658) 2021-10-31 00:06:32 -05:00
Chris Miles 9e7a763482 [Charm] Push up fragments of Kayen's PR back up (#1659) 2021-10-31 00:04:48 -05:00
Akkadius 1231d44b55 Revert "[BugFix] Charm Targeting and other issues. (#1655)"
This reverts commit df3161455a.
2021-10-30 22:18:40 -05:00
hg 873f1f7f34 [Hotfix] Add include to fix windows build (#1657) 2021-10-30 21:11:04 -05:00
Chris Miles d87db648c3 [Loginserver] Code Cleanup and Tweaks (#1653)
* if for whatever reason the world server is not sending an address, use the local address it sends

* Log when world is sending loginserver info

* Force legacy mode when login host is login.eqemulator.net to avoid misconfigurations at least until things change

* Add human IP translation to log messages

* Sanitize world server name

* Code cleanup and renaming member variables

* More cleanup

* Remove this->

* Validation constants

* Key worldserver lookups by both longname and shortname both

* Update allowed character list

* Fix short_name API response field; add world_id to response

* Shorten receiver verbosity

* Remove unnecessary member variables from database and rename database to m_database

* Adjust MAX_SERVER_VERSION_LENGTH

* Fix indents
2021-10-30 19:09:42 -05:00
Chris Miles 119c3d14b7 [Hotfix] Gate some new shared task logic behind task rule (#1656) 2021-10-30 19:06:38 -05:00
Chris Miles 3cda32c213 [Saylinks] In-Memory Saylink Lookups (#1644)
* Implement saylink memory lookups (performance)

* Ignore commands
2021-10-30 17:32:59 -05:00
KayenEQ df3161455a [BugFix] Charm Targeting and other issues. (#1655)
* fix for target change bug on client

* Update spell_effects.cpp
2021-10-30 16:48:55 -05:00
KayenEQ 4389f84ea5 [BugFix] Fix for bard song instrument mod formula from recent update (#1654)
* Update spell_effects.cpp

* Update spell_effects.cpp

* Update spell_effects.cpp
2021-10-30 08:50:15 -04:00
Chris Miles f9855fd097 [Rez] Fix Z during Resurrection (#1648) 2021-10-30 00:54:44 -05:00
Chris Miles f912814e13 [Commands] Fix Z on #spawnfix (#1647)
* Fix Z on spawnfix

* Slight adjustment
2021-10-30 00:54:33 -05:00
Paul Coene 5738958a2a Fix issue with droplimit code (#1650) 2021-10-28 14:43:40 -04:00
Michael Cook (mackal) d36d11653a Fix issue with new summmon method putting players OOB (#1649)
The FindClosestZ was finding the Z above them ... lets try just not
doing that for now :)
2021-10-27 23:42:31 -04:00
Chris Miles 7230714cbc [Spells/Disciplines] Bulk Train / Scribe (#1640)
* Bulk scribe spells

* Add bulk disc training

* Remove bulk from non bulk method

* PR adjustments
2021-10-27 21:45:27 -04:00
Chris Miles 6e5bf4b941 [Saylinks] Multiple saylinks in brackets (#1643)
* Saylink edge case where multiple saylinks show up within a bracket

* Update partial
2021-10-27 00:01:37 -05:00
KayenEQ fb66afd565 [Spells] Implemented SPA 511 SE_Ff_FocusTimerMin (#1645)
* update for SPA 511

* remove debugs, AA implemented

* update

* format update

* rename function

renamed function
only check for buffs value > 0, don't need to check for AA's which are negative ID's

* var rename

update var name to better represent its function.
2021-10-26 21:36:10 -04:00
Chris Miles ef5124d756 [Shared Tasks] World Reload Task Data on #task reloadall (#1641) 2021-10-24 21:53:29 -05:00
KayenEQ 987de17e93 [Spells] Rework for SPA 413 SE_FcBaseEffects and Bard updates (#1629)
* baseline start

* update1

* updates

* base effect implemented for bard

* instrument mod updates

amplification amps itself

* updates

* updates

* debug

* base effect updates

* baseeffects for spell focus updated

* update skill attack baseeffects

* focus will remain for quest functions

* song cap mod added back in

* remove debugs1

* fix cr

* base effects functionalish

* remove debug

* Update client_mods.cpp

* spdat instrumentmod

* Update spell_effects.cpp

* Update spdat.h

* remove new instrument mod check

split PR
2021-10-24 18:38:28 -05:00
KayenEQ 060be606e7 [Spells] Rework of Virus Effect code (#1593)
* start of rework

* functional

* virus updates

* Update npc.cpp

* updates

* updates

* update v2

* pre remove old code

* removed old code1

* remove debugs

* description

* Update spell_effects.cpp

* changed function name

* remove unused var

* merge error fix

* fix formating issue

* Update spdat.cpp

* Update spell_effects.cpp

* Convert virus entity range code to use vectors and GetCloseMobList

* Formatting [skip ci]

Co-authored-by: Akkadius <akkadius1@gmail.com>
2021-10-24 18:27:51 -05:00
Kinglykrab 1c5f9f2e0f [Bug Fix] Fix possible #proximity crash. (#1639) 2021-10-24 17:50:43 -04:00
Paul Coene 62253cc016 [Bug Fix] Edge cases where min_drop 1 not honored with valid choices (#1617)
* [Bug Fix] Edge cases where min_drop 1 not honored with valid choices

* Forgot header file change.

* Remove verbose option from MeetsLootDropLevelRequirements per @akka

* Fix spacing

* Restore verbose mode after further consideration

* Remove logging on counting of valid items

Co-authored-by: Noudess <noudess@gmail.com>
2021-10-24 16:17:42 -05:00
KayenEQ 0b18671e91 [Spells] Update to how Bard Instrument mods are applied to spell effects (#1628)
* new instrument mod spell effect checks

PR split

* format

* Update spdat.cpp

correction, all direct damage spells get modifiers. Made a mistake with the parse, was using wrong mod.

* restriction changes

cure effects can be modified.
decided to keep a list of known effects that are not modified to return false. and will keep the default to be true for anything as to not inhibit custom bard song development

* SE_ProcChance is modified

* Update spdat.cpp

* update

* Update spell_effects.cpp
2021-10-24 16:07:25 -05:00
Kinglykrab c98f3cfb4c [Quest API] Further char array cleanup. (#1634)
- Cleans up the rest of the char arrays used when exporting to events.
- Converts all events to use a similar variable name for export `export_string`.
- Needless calls to .c_str() removed.
2021-10-24 16:06:22 -05:00
Kinglykrab 624d11de4e [Bug Fix] Fix missing format in client message. (#1637) 2021-10-24 17:03:24 -04:00
Logan 5eb95a95d0 [Rules] Added rule to extend max race id (#1630)
* Added rule to extend max race id

* Cleaned fmt of MaxRaceID

* Added format command

* Updated MaxRaceID default to be 732
2021-10-24 15:53:49 -05:00
JJ da01156673 Update 2021_03_03_instance_safereturns.sql (#1636) 2021-10-24 11:53:23 -04:00
KayenEQ 6a244f16e1 Update spells.cpp (#1635) 2021-10-24 11:08:21 -04:00
KayenEQ 36d10462f7 [Combat] Updates to IMMUNE_MELEE_NONMAGICAL mechanics (#1616)
* pre remove debug

* Update attack.cpp

* Update attack.cpp

* Update attack.cpp

* Update attack.cpp

* apply to temp pets

* format fix

* changed to just use one rule

Merged into NPC's and Pet's into one rule.
2021-10-22 22:39:37 -04:00
Natedog2012 c30dbf6628 [Bug Fix] Do not check tics remaining on non-buff spells (#1633) 2021-10-22 17:16:56 -04:00
splose 657cbbcabe define caster to fix a crash from #1618 (#1632) 2021-10-22 13:48:15 -04:00
Kinglykrab 81e7cf5a32 [Quest API] Convert Spell Events to similar formats and exports. (#1618)
* [Quest API] Convert Spell Events to similar formats and exports.
Export spell ID, caster ID, caster level, tics remaining, and buff slot to Perl/Lua spell events.
- Export e.buff_slot, e.caster_id, e.caster_level, e.spell_id, and e.tics_remaining to `event_spell_buff_tic`, `event_spell_effect`, and `event_spell_fade` in Lua.
- Export $buff_slot, $caster_id, $caster_level, $spell_id, $tics_remaining to `EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT`, `EVENT_SPELL_EFFECT_BUFF_TIC_NPC`, `EVENT_SPELL_EFFECT_CLIENT`, `EVENT_SPELL_EFFECT_NPC`, and `EVENT_SPELL_FADE` in Perl.

* Formatting.

* Remove debug variable.
2021-10-20 16:02:12 -04:00
Kinglykrab edf298685e [Quest API] Convert all char arrays to strings. (#1612)
* [Quest API] Convert all char arrays to strings.
Also change multiple loops for zone controller to one loop.

* Remove 'this' keyword'
2021-10-20 15:59:28 -04:00
Kinglykrab efab0c4b6b [Quest API] Add remove LDoN Win/Loss to Perl and Lua. (#1611)
* [Quest API] Add remove LDoN Win/Loss to Perl and Lua.
- Add $client->RemoveLDoNLoss(theme_id) to Perl.
- Add $client->RemoveLDoNWin(theme_id) to Perl.
- Add quest::removeldonloss(theme_id) to Perl.
- Add quest::removeldonwin(theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbycharid(character_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbygroupid(group_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbyraidid(raid_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbyguildid(guild_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbyexpeditionid(expedition_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonlossbyclientname(client_name, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbycharid(character_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbygroupid(group_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbyraidid(raid_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbyguildid(guild_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbyexpeditionid(expedition_id, theme_id) to Perl.
- Add quest::crosszoneremoveldonwinbyclientname(client_name, theme_id) to Perl.
- Add quest::worldwideaddldonloss(theme_id, min_status, max_status) to Perl.
- Add quest::worldwideaddldonwin(theme_id, min_status, max_status) to Perl.
- Add client:RemoveLDoNLoss(theme_id) to Lua.
- Add client:RemoveLDoNWin(theme_id) to Lua.
- Add eq.remove_ldon_loss(theme_id) to Lua.
- Add eq.remove_ldon_win(theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_char_id(character_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_group_id(group_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_raid_id(raid_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_guild_id(guild_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_expedition_id(expedition_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_loss_by_client_name(client_name, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_char_id(character_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_group_id(group_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_raid_id(raid_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_guild_id(guild_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_expedition_id(expedition_id, theme_id) to Lua.
- Add eq.cross_zone_remove_ldon_win_by_client_name(client_name, theme_id) to Lua.
- Add eq.world_wide_add_ldon_loss(theme_id, min_status, max_status) to Lua.
- Add eq.world_wide_add_ldon_win(theme_id, min_status, max_status) to Lua.
Adds enum for LDoN Themes and Theme Bitmasks so we're not using magic numbers.
Adds item links to item messages and augment messages on rejection/restriction/Lore.

* Update client_packet.cpp

* Update client_packet.cpp

* Update servertalk.h

Alphabetical.
2021-10-20 15:11:14 -04:00
Kinglykrab c838564023 [Bug Fix] Fix OpenSSL Support for Windows (#1625) 2021-10-19 22:28:10 -05:00
Chris Miles d197ee631e [Saylinks] Fix auto saylink injection edge cases (#1620)
* Fix auto saylink injection edge cases

* Add even more resiliency to edge cases

* Move to split based injection

* Add some constants
2021-10-19 22:25:13 -05:00
Kinglykrab 3dcddcba04 [Quest API] Add GetHateRandomBot(), GetHateRandomClient(), and GetHateRandomNPC() to Perl/Lua. (#1613)
- Add $mob->GetHateRandomBot() to Perl.
- Add $mob->GetHateRandomClient() to Perl.
- Add $mob->GetHateRandomNPC() to Perl.
- Add mob:GetHateRandomBot() to Lua.
- Add mob:GetHateRandomClient() to Lua.
- Add mob:GetHateRandomNPC() to Lua.
2021-10-17 23:41:10 -04:00
Natedog2012 7823ff5336 [Quest API] Add EVENT_LOOT_ZONE to zone_controller (#1608)
* Add EVENT_LOOT_ZONE to zone_controller

* Fix porting event_loot_zone to lua API

* Remove extra spacing and remove forced message to allow for scripted responses.

* Allow all script parsing to fire before sending a failed lootitem, add corpse_id

* Only search for zone_controller once
2021-10-16 23:19:19 -05:00
Chris Miles 11c335a015 [DiaWind] Tag Adjustments for title, button_one, button_two (#1610)
* Add a consistent way to handle a few different tags

* Simplify logic further
2021-10-16 21:35:03 -05:00
Kinglykrab 07d96ad921 [Bug Fix] Fix Character Recast Type -1 saving to database. (#1598) 2021-10-16 15:10:42 -04:00
Kinglykrab 5d522b149b [Bug Fix] Allow invisible to be cast on Raid Group members. (#1607)
When `Spells:InvisRequiresGroup` was true, you could only cast on Group members, intended functionality is to cast on Group members and/or people in your Raid Group.
2021-10-16 08:56:38 -04:00
splose 234bd89ed5 Merge pull request #1609 from KayenEQ/npcMagicAttack2
hotfix for PR #1606 (IMMUNE MELEE NONMAGICAL)
2021-10-16 00:38:06 -04:00
KayenEQ af5cfb9bed [Spells] Fix to prevent Charmed Pets from continuing fight target if owner is dead. (#1600)
* Fix for charm break if pet owner dead

* fix, can't check hatelist it is already wiped.

* Update spell_effects.cpp
2021-10-16 00:22:07 -04:00
KayenEQ 426f9c337b hotfix 2021-10-16 00:10:54 -04:00
KayenEQ 5235dcee95 Fix Immune Melee Nonmagical logic (#1606) 2021-10-15 20:46:57 -04:00
Paul Coene 203ba2d340 [Bug Fix] Urgent - Previous fix for TimeSync on static zones broke dynamic zones. (#1605)
* [BugFix] Urgent - Previous fix for TimeSync on static zones broke dynamic zones

* Use local variable instead of inline accessor for consistancy.

Co-authored-by: Noudess <noudess@gmail.com>
2021-10-15 13:17:51 -04:00
KayenEQ 9c67421ccc quick fix for persistent effects fading (#1604) 2021-10-15 09:04:14 -04:00
KayenEQ 6669fc8214 [Bug Fix] Healing pets not correctly dropping out of combat status (#1603)
* Fix for pets not correctly triggering in combat timers

* Update spells.cpp
2021-10-14 23:30:34 -04:00
Paul Coene cef873f793 [BugFix] Remove detection of client pets from Sense[Summoned|Undead|Animal] spells (#1601)
* Remove detection of client pets from Sense[Summoned|Undead|Animal]

* Use IsPetOwnerClient() function instead of individual checks

* Add option to exclude client pets from GetClosestMobByBodyType

* Add parameter

Co-authored-by: Noudess <noudess@gmail.com>
2021-10-14 10:52:29 -04:00
KayenEQ 6a962f2591 [Spells] Update SPA158 Reflect (#1590)
* update

* updates

* updates

* update

* update

* Update ruletypes.h

* Apply extra spell dmg

Mob with the reflect effect apply its Extra Spell Damage from item stat to the reflected spell.
Updated portion of formula for extra damage based on live parsing.

* correct formula
2021-10-12 15:30:36 -04:00
Kinglykrab 91adf9c0eb [Quest API] Add cross zone and world wide dialogue windows to Perl/Lua. (#1599)
* [Quest API] Add cross zone and world wide dialogue windows to Perl/Lua.
- Add quest::crosszonedialoguewindowbycharid(character_id, message) to Perl.
- Add quest::crosszonedialoguewindowbygroupid(group_id, message) to Perl.
- Add quest::crosszonedialoguewindowbyraidid(raid_id, message) to Perl.
- Add quest::crosszonedialoguewindowbyguildid(guild_id, message) to Perl.
- Add quest::crosszonedialoguewindowbyexpeditionid(expedition_id, message) to Perl.
- Add quest::crosszonedialoguewindowbyclientname(client_name, message) to Perl.
- Add quest::worldwidedialoguewindow(message, min_status, max_status) to Perl.
- Add eq.cross_zone_dialogue_window_by_char_id(character_id, message) to Lua.
- Add eq.cross_zone_dialogue_window_by_group_id(group_id, message) to Lua.
- Add eq.cross_zone_dialogue_window_by_raid_id(raid_id, message) to Lua.
- Add eq.cross_zone_dialogue_window_by_guild_id(guild_id, message) to Lua.
- Add eq.cross_zone_dialogue_window_by_expedition_id(expedition_id, message) to Lua.
- Add eq.cross_zone_dialogue_window_by_client_name(client_name, message) to Lua.
- Add eq.world_wide_dialogue_window(message, min_status, max_status) to Lua.

* Use string instead.
2021-10-11 16:33:18 -04:00
hg 9887580f9a Make columns in doors table not nullable (#1597)
This makes the float and integer fields in the doors table not nullable.
The only column this should affect is the buffer column which wasn't
being loaded in the old doors loading query. The other columns weren't
validated but they should still be made not nullable to avoid issues.

This will fix a crash in potimeb which is the only zone that had NULL
values in the buffer column with the current peq database. This column
can be removed in a future followup since it isn't being used anyway.
2021-10-09 13:45:38 -04:00
KayenEQ b7c62b5242 Merge pull request #1592 from KayenEQ/updateSPA157SpellDS
[Spells] Update to SPA 157 Spell Damage Shield
2021-10-09 12:05:15 -04:00
KayenEQ 89a40272c6 Merge pull request #1588 from KayenEQ/spa154and209updates
[Spells] Update to SPA 154 and SPA 209 Dispel Bene/Detrimental
2021-10-09 00:57:42 -04:00
Chris Miles db369c98c8 [HP Updates] Fix for Titanium clients not being updated properly by removing client version check (#1596) 2021-10-08 21:04:19 -07:00
KayenEQ 10ba5d6046 Merge pull request #1595 from KayenEQ/instanthealbug1
[Spells] Hotfix for healing code error from recent commit.
2021-10-08 18:54:54 -04:00
KayenEQ dd1a869531 hotfix 2021-10-08 18:43:47 -04:00
KayenEQ a9e23cf83a Merge pull request #1594 from KayenEQ/fixExtraSpellAmt
[Spells] Minor fix to Item Extra Spell Damage Amt formula
2021-10-08 18:00:46 -04:00
KayenEQ 783c12590e minor fix
was not correct, was comparing negative to a positive
2021-10-08 13:14:39 -04:00
Kinglykrab 6689b57a52 [Commands] Convert item ID search to use saylinks similar to name search. (#1589) 2021-10-08 05:41:37 -04:00
Paul Coene 7029c699a0 Merge pull request #1591 from noudess/aggro
[Bug Fix] always_aggro flag needed to be checked on assist
2021-10-07 16:25:51 -04:00
KayenEQ 3b9574af14 fix 2021-10-05 16:59:07 -04:00
Noudess 740f84dc22 always_aggro flag needed to be checked on assist 2021-10-05 15:51:22 -04:00
KayenEQ 55d45f9a98 updates 2021-10-05 15:50:26 -04:00
Cole-SoD 61d1eeab6f Minor corrections (#1582) 2021-10-04 17:26:02 -04:00
Kinglykrab 133c1e866c [Bug Fix] Send appearance wasn't setting size properly when changing races. (#1586) 2021-10-04 17:25:51 -04:00
Kinglykrab b730461894 [Bug Fix] Trim output in hidden dialogue response. (#1587) 2021-10-04 17:25:21 -04:00
KayenEQ 8b08e22dbc removed unused function 2021-10-04 14:33:20 -04:00
KayenEQ fc7c99fb0a Update spdat.h 2021-10-04 11:14:56 -04:00
KayenEQ f1d267bb2d Update spell_effects.cpp 2021-10-04 08:31:52 -04:00
Kinglykrab 07664eedc0 [Bug Fix] Trim trailing whitespace off output in Popup. (#1584) 2021-10-03 14:51:12 -04:00
hg 64b8d7c874 Remove unnecessary includes (#1585)
The include order here was causing a compile error when building with
perl 5.12 due to a bad interaction with the older fmt submodule version
being used
2021-10-03 13:25:49 -04:00
Kinglykrab ccab07bd66 [Dialogue] Add hidden response support. (#1583)
Allows operators to hide responses.
Syntax is `hiddenresponse`.
2021-10-03 13:06:39 -04:00
Kinglykrab 3a76d9a28e [Bug Fix] Dialogue Window Name replace. (#1581)
{name} was being replaced with the string "$name" because Perl would parse it properly, but when used here it doesn't return the client's name.
2021-10-03 13:06:31 -04:00
Kinglykrab 5720a5020d [Quest API] Add attuned/augment support to client->SummonBaggedItems() in Perl/Lua. (#1580)
Perl Example:
```pl
my @bag_items = (
      { item_id => 33649, charges => 1, attuned => 1, augment_one => 32940 }
    );
```

Lua Example:
```lua
local bag_items = {
	{ item_id = 33649, charges = 1, attuned = 1, augment_one = 32940 }
}
2021-10-02 19:35:35 -04:00
Kinglykrab b3e9e2099a [Quest API] Add GetIPExemption(), GetIPString(), and SetIPExemption(exemption_amount) to Perl/Lua.
- Add $client->GetIPExemption() to Perl.
- Add $client->GetIPString() to Perl.
- Add $client->SetIPExemption(exemption_amount) to Perl.
- Add client:GetIPExemption() to Lua.
- Add client:GetIPString() to Lua.
- Add client:SetIPExemption(exemption_amount) to Lua.

Will make plugin::IP unnecessary and allow people to get readable IP string easier, as well as set/get IP exemptions from Perl and Lua.
2021-10-02 13:39:32 -04:00
Kinglykrab 93acf50bb4 [Quest API] Add client->ReadBookByName(book_name, book_type) to Perl/Lua.
- Add $client->ReadBookByName(booK_name, book_type) to Perl.
- Add client:ReadBookByName(booK_name, book_type) to Lua.
- Allows server operators to put books in to their database and read from their database instead of storing the values in a script, also allows them to read pre-existing books using a script.
2021-10-02 13:09:30 -04:00
Kinglykrab ff46a854f9 [Quest API] Add LavaDamage and MinLavaDamage to UpdateZoneHeader in Perl/Lua. (#1578)
Allows operators to modify lava damage dynamically.
2021-10-02 12:01:54 -04:00
Kinglykrab 8c5f26ca5e [Quest API] Add IsNPCSpawned(npc_ids) and CountSpawnedNPCs(npc_ids) to Perl/Lua. (#1570)
- Add quest::isnpcspawned(npc_ids) to Perl.
- Add quest::countspawnednpcs(npc_ids) to Perl.
- Add eq.is_npc_spawned(npc_ids) to Lua.
- Add eq.count_spawned_npcs(npc_ids) to Lua.
2021-10-02 12:01:39 -04:00
hg 5560b198ca [Quest API] Add client->SummonBaggedItems(bag_item_id, bag_items_ref) to Perl/Lua.
Alternative apis using arrays of hash items for EQEmu/Server#1575

Perl usage:
```pl
    # create as an array, pass as reference
    my @bag_items = (
      { item_id => 1001, charges => 1 },
      { item_id => 1002, charges => 1 },
      { item_id => 10037, charges => 10 },
    );
    $client->SummonBaggedItems(17403, \@bag_items);

    # create directly as an array reference
    my $bag_items = [
      { item_id => 1001, charges => 1 },
      { item_id => 1002, charges => 1 },
      { item_id => 10037, charges => 10 },
    ];
    $client->SummonBaggedItems(17403, $bag_items); ```

Lua Usage:
```lua
    local bag_items = {
      { item_id = 1001, charges = 1 },
      { item_id = 1002, charges = 1 },
      { item_id = 10037, charges = 10 }
    }
    e.other:SummonBaggedItems(17403, bag_items);
2021-10-02 12:00:00 -04:00
Chris Miles 9a413cf553 [Shared Tasks] Task Kill Update Fix (#1573)
* Revert "Revert "Shared task kill update fix""

This reverts commit 859751f74d.

* Swap return for continue in this context

* Slight tweak

* Slight tweak

* Remove no longer needed task methods

* Update scope for IncrementDoneCount

* Create helper method Client::GetPartyMembers() and add client->HasTaskState()

* Move HandleUpdateTasksOnKill responsibility to TaskManager

* Remove unnecessary pointer
2021-10-01 20:57:00 -07:00
Kinglykrab bb5c491794 [Dialogue] Add support for Dialogue Window titles. (#1563)
* [Dialogue] Add support for Dialogue Window titles.
- Custom title allows defaults to be overridden where necessary, like a leaderboard or something.
- Default target to client in case people want to send Dialogue Windows from current client.

* Fix possible issue with markdown.
- Example: Using the word "title" or using any identifier and forgetting the colon.
2021-10-01 22:20:15 -05:00
Kinglykrab 2f5d360e53 [Quest API] Add UntrainDiscBySpellID(spell_id, update_client) to Perl/Lua. (#1565)
- Add $client->UntrainDiscBySpellID(spell_id, update_client) to Perl.
- Add client:UntrainDiscBySpellID(spell_id, update_client) to Lua.
2021-10-01 22:14:56 -05:00
hg 92e03dccb9 [Quest API] Add perl hash apis for dz creation (#1571)
Add hash overload to perl CreateExpedition api

  This adds an api to perl similar to the Lua api that accepts a reference
  to a hash table with expedition creation info

  Usage example:
    my $expedition_info = {
      expedition => { name => "Perl expedition", min_players => 2, max_players => 6 },
      instance   => { zone => "crushbone", version => 0, duration => 3600 },
      compass    => { zone => "gfaydark", x => 238, y => 987, z => -24.90 },
      safereturn => { zone => "gfaydark", x => 245.84, y => 987.93, z => -27.6, h => 484.0 },
      zonein     => { x => 479.44, y => -500.18, z => 5.75, h => 421.8 }
    };

    $client->CreateExpedition($expedition_info);

  Syntax for passing directly from a hash:
    my %expedition_info = (...);
    $client->CreateExpedition(\%expedition_info);

Add CreateTaskDynamicZone api to perl

  Usage example:
    sub EVENT_TASKACCEPTED {
      if ($task_id == 4795) {
        my %dz_hash = (
          "instance",   { zone=>"thundercrest", version => 11 },
          "compass",    { zone=>"broodlands", x=>1241.88, y=>511.147, z=>23.4192 },
          "safereturn", { zone=>"broodlands", x=>1242.0, y=>526.0, z=>27.0, h=>0.0 }
        );
        $client->CreateTaskDynamicZone($task_id, \%dz_hash)
      }
    }
2021-10-01 22:12:45 -05:00
hg 5ffe6284ca [Shared Tasks] Start solo task replay timers from completion time (#1568)
Shared tasks start replay timers based on accept time but solo tasks
should start from completion time. Solo tasks on live that have a
non-unlimited duration may require further investigation
2021-10-01 22:11:57 -05:00
hg fb98349bbd [Quest API] Add mob SetPet and RemovePet quest apis (#1569)
Will be required for tutoriala script and other similar events
2021-10-01 22:11:16 -05:00
hg 00a22ca12e [Repositories] Use repositories to load doors (#1572)
Remove Door struct that was being used to map db columns
2021-10-01 22:09:40 -05:00
Chris Miles 3883adcefc [Dialogue Window / Saylinks] Missing Changes (#1574)
* Implement auto saylink injection

* Cover Lua say since it takes a different code path

* [Dialogue] Dialogue Window Middleware (#1526)

* Dialogue window quest dialogue work

* Add rest of DialogueWindow hooks

* Remove spacing
2021-10-01 22:09:21 -05:00
Kinglykrab 0762ffa3dc [Quest API] Typo in Perl $entity_lsit->IsMobSpawnedByNpcTypeID(). (#1576)
This causes the wrong name to show up on Spire.
2021-10-01 21:19:26 -04:00
KayenEQ b70dc64d96 Update spell_effects.cpp 2021-10-01 20:36:54 -04:00
Akkadius 859751f74d Revert "Shared task kill update fix"
This reverts commit 91c451b6c5.
2021-10-01 18:42:36 -05:00
Akkadius 91c451b6c5 Shared task kill update fix 2021-10-01 18:42:02 -05:00
KayenEQ 30c7ed7e45 Merge pull request #1557 from KayenEQ/spa395fix2
[Spells] Healing focuses effects update and Fix for SPA 395
2021-10-01 15:52:25 -04:00
KayenEQ 509b6f2056 Merge pull request #1558 from KayenEQ/spa382update2
[Spells] More updates for SPA 382 SE_NegateSpellEffect
2021-10-01 15:51:32 -04:00
KayenEQ 558bebe710 updates 2021-10-01 15:50:26 -04:00
KayenEQ 08a85c5dae Merge remote-tracking branch 'upstream/master' into spa395fix2 2021-10-01 14:28:45 -04:00
KayenEQ d22f9ee294 Merge remote-tracking branch 'upstream/master' into spa382update2 2021-10-01 14:26:43 -04:00
Cole-SoD 0aeaf7c3b7 [Zone] Add LavaDamage and MinLavaDamage support to ZoneHeader (#1540)
* Add LavaDamage and MinLavaDamage support to ZoneHeader

* Add lava_damage and min_lava_damage to base_zone_repository.h

* Update version.h and utils/sql/git/required/ file

* Correct SQL Query, adjust utils/sql/db_update_manifest.txt to check one column

* Correct manifest
https://github.com/EQEmu/Server/pull/1540#discussion_r714330945
2021-09-30 11:44:22 -05:00
KayenEQ c04bcef273 Update spells.cpp (#1554) 2021-09-30 11:43:36 -05:00
KayenEQ 7fcea371c2 [Spells] Updated Memory Blur SPA 63 - Implemented Live Mechanics (#1559)
* memory blur updated

* Update spdat.h
2021-09-30 11:43:05 -05:00
Natedog2012 dd765238f7 Merge pull request #1553 from Natedog2012/tradeskill_fix
[Tradeskill] Fix logic in taught tradeskill recipes
2021-09-29 19:05:41 -05:00
Paul Coene 2c98a11696 Merge pull request #1561 from noudess/timesync
[Bug Fix] Zones no longer syncing time to world
2021-09-29 16:57:30 -04:00
Noudess d4f14efaa0 Fix TimeSync to work with new Servertalk connection order 2021-09-27 10:04:02 -04:00
KayenEQ 27787c247b Update spell_effects.cpp 2021-09-23 18:01:08 -04:00
KayenEQ ea9c07aa98 393 NegateEffect updates 2021-09-23 16:43:07 -04:00
KayenEQ 5cd9bfeb70 reminder
bot code needs to updated, then old function can be removed
2021-09-23 14:23:17 -04:00
KayenEQ b699196299 Update effects.cpp 2021-09-23 14:14:46 -04:00
KayenEQ e89c2aec4a Update bot.cpp 2021-09-23 13:48:15 -04:00
KayenEQ 456fb56e82 revert for bots 2021-09-23 13:42:36 -04:00
KayenEQ 03ac828134 Update bot.cpp 2021-09-23 12:28:34 -04:00
KayenEQ 34b2264d5d bots... 2021-09-23 12:21:53 -04:00
KayenEQ 933ede40f9 Update bot.cpp 2021-09-23 12:13:29 -04:00
KayenEQ 881dc33c9b update 2021-09-23 12:00:16 -04:00
KayenEQ 3faa0d2603 update 2021-09-23 11:41:36 -04:00
KayenEQ 1ce5087e2a Update effects.cpp 2021-09-23 09:35:09 -04:00
Natedog2012 bf8d94eb35 Fix SendTradeskillSearchResults row count was incorrect format. Remove extra database hits from last commit. 2021-09-22 21:43:49 -05:00
Natedog2012 9aac12f517 Hide tradeskill recipes that require being learned before crafting them, as well as fix how learned recipes are checked. 2021-09-22 18:21:57 -05:00
Kinglykrab 7b969173f4 [Door Manipulation] Resolve some typos and add a GM check. (#1550)
* [Door Manipulation] Resolve some typos and add a status check.

* Remove Status check and use GetGM() inside devtools check instead.
2021-09-22 16:43:01 -05:00
Kinglykrab ca77d22035 [Bug Fix] GetSpellStat() Identifiers were comparing improperly. (#1552)
- GetSpellStat() converts identifiers to lowercase and they were being checked against mixed case strings, causing certain identifiers to always fail.
2021-09-21 21:08:16 -04:00
Paul Coene ad3bf35397 Merge pull request #1548 from noudess/master
[Bug Fix] Fix bug where IVU could not be cast on char with Invis
2021-09-21 12:21:18 -04:00
Noudess 9b06221be0 [Bug Fix] Fix bug where IVU could not be cast on char with Invis 2021-09-20 11:30:33 -04:00
Kinglykrab c0de178173 [Commands] Overhauled #npcedit. (#1538)
* [Commands] Overhauled #npcedit.
- Added missing columns like untargetable, show_name, exp_mod, etc.
- Put stats in order of column appearance in table within help message and within code.
- Converted StringFormat to fmt::format.
- Added a GetGenderName() helper method.
- Prettified response messages of nearly every #npcedit option.

All tested and ready to go.

Would like input about possibly changing some of the command arguments to match the table column names more closely, example being "spell" should be "npc_spells_id".

* Cleanup.

* Fix indentation.
2021-09-19 16:32:21 -07:00
Kinglykrab 6a5face0aa [Dialogue] Add support for Dialogue Window buttons. (#1546)
* [Dialogue] Add support for Dialogue Window buttons.
- Also changes "mysterious" identifier to "{mysterious}".
- Both button names are required for anything to show up, otherwise it defaults to Yes/No similar to Client::SendFullPopup.

* Move SetEntityVariable so responses can override default button response.

* Add negativeid support so you can override button two popup ID.

* Fix log.

* Update dialogue_window.cpp

Convert button names to strings and negativeid to secondrespondid.
2021-09-19 16:24:04 -07:00
Kinglykrab c15c54e920 [Quest API] Cross zone and world wide method overhaul. (#1520)
* [Quest API] Cross zone and world wide method overhaul.
- Adds support for Character ID, Character Name, and Expedition ID to all cross zone methods that did not have a method.
- Adds worldwide LDoN Updates.
- Shrinks the number of packets and structs from 83 to 17.

No quest functionality will be affected by this, as the only changes are the underlying method used to send the cross zone and world wide data.

* Formatting, organization, and fixing of improper exports.

* Finalize comb through of variable types, update types, etc.

* Merge fixes.
2021-09-19 16:15:14 -07:00
Chris Miles 24c079dca4 [Hotfix] Fix freeze formatting for Quest API parsing (Spire) (#1547) 2021-09-19 15:25:52 -05:00
Kinglykrab 8eef7bb283 [Quest API] Add EVENT_COMBINE to Perl and Lua. (#1536)
- Exports $container_slot in Perl.
- Exports e.container_slot in Lua.

Allows you to perform events when clicking combine in a tradeskill container.
2021-09-19 15:22:51 -05:00
Michael Cook (mackal) 80493719f2 [Summoning] Make Summon a bit more live like (#1539)
Pretty sure the distance should probably be melee range / 2 but ahh
yeah. Can't do that. Hopefully 5 units isn't too far.
2021-09-19 15:19:29 -05:00
KayenEQ df9d6bc506 [Spells] Corrected implementation of SE_Purify 291 (#1541)
* Correct implementation of spa291

* debug removal
2021-09-19 15:17:10 -05:00
KayenEQ 71870cbd1c [Spells] Update to SPA 442 and 443 (SE_TriggerOnReqTarget, SE_TriggerOnReqCaster) (#1543)
* Update to SPA 442 and 443

Use SpellRestriction Id's and updated PassCastRestriction code

* Update mob.cpp
2021-09-19 15:16:56 -05:00
Kinglykrab f715ccd368 [Bug Fix] Fixes EVENT_DISCONNECT for /quit and /exit. (#1542)
/quit and /exit will now properly parse to EVENT_DISCONNECT so operators can do things on disconnect to these players, previously it only functioned for /camp.
2021-09-19 15:16:38 -05:00
KayenEQ 46edd56acc [Spells] Update SPA 101 SE_CompleteHeal (#1544)
Fixed buff stacking issue
2021-09-19 15:16:21 -05:00
KayenEQ 442850aebb [Spells] Update to SPA305 (#1545)
minor fix to allow for effects with negative values.
2021-09-19 15:16:02 -05:00
KayenEQ d4e752987e fixes 2021-09-17 23:21:03 -04:00
KayenEQ 9c6a85ff16 heal code updates 2021-09-17 22:27:45 -04:00
Kinglykrab fa8d8eccc2 [Quest API] Add corpse->RemoveItemByID(item_id, quantity) to Perl and Lua. (#1535)
* [Quest API] Add corpse->RemoveItemByID(item_id, quantity) to Perl and Lua.
- Add $corpse->RemoveItemByID(item_id, quantity) to Perl.
- Add corpse:RemoveItemByID(item_id, quantity) to Lua.

* Formatting.
2021-09-13 15:42:04 -04:00
Kinglykrab 6e76f89ca2 [Quest API] Add EVENT_CONSIDER to Perl and Lua. (#1531)
* [Quest API] Add EVENT_CONSIDER to Perl and Lua.
- Exports $entity_id in Perl.
- Exports e.entity_id in Lua.

Allows you to perform events on consider for server operators.

* Missing comma.

* Formatting.

* Add return capability to EVENT_CONSIDER and EVENT_CONSIDER_CORPSE so operators can break out of consider functions.
2021-09-13 15:30:17 -04:00
Akkadius 9589bf6bf8 [Hotfix] Crash fix that apparently didn't make it in another PR 2021-09-13 14:15:08 -05:00
Kinglykrab ce5fa9502f [Commands] Adds #dye command. (#1537)
* [Commands] Adds #dye command.

* Fix use tint.
2021-09-12 22:40:43 -05:00
Kinglykrab 7b1b05a35c [Bug Fix] Resolves issues with improper genders and textures on spells. (#1533)
* [Bug Fix] Resolves issues with improper genders and textures on spells.
Spells will now properly understand their expected gender and texture.
Logic is based on what I saw in a stock PEQ database, can be adjusted if need be.
Any feedback is helpful.

* Made use of GetRaceGenderDefaultHeight() and added all races to their proper conditions.

* Formatting.
2021-09-12 22:40:07 -05:00
Kinglykrab 38a86edc70 [Quest API] Add EVENT_CONSIDER_CORPSE to Perl and Lua. (#1530)
- Exports $corpse_entity_id in Perl.
- Exports e.corpse_entity_id in Lua.

Allows you to perform events on corpse consider for server operators.
2021-09-12 22:39:09 -05:00
Kinglykrab 56b9b6f2c4 [Quest API] Add corpse->GetLootList() and npc->GetLootList() to Perl and Lua. (#1529)
* [Quest API] Add corpse->GetLootList() and npc->GetLootList() to Perl and Lua.
- Add $corpse->GetLootList() to Perl.
- Add $npc->GetLootList() to Perl.
- Add corpse:GetLootList() to Lua.
- Add npc:GetLootList() to Lua.

Returns an array of item IDs for use with corpse and NPC methods such as HasItem(item_id), CountItem(item_id), and GetFirstSlotByItemID(item_id).

* Categories.

* Modify Lua to use classes.
2021-09-12 22:38:38 -05:00
Chris 97dcba70cf [Bots] Fix for Bot Pets Taunting (#1519)
Currently bot pets will taunt and there's no way to turn it off. This makes it so pets follow their owner's taunt settings.
2021-09-12 22:37:39 -05:00
Chris Miles 6b93130c13 [Saylinks] Implement Auto Saylink Injection (#1525)
* Implement auto saylink injection

* Cover Lua say since it takes a different code path
2021-09-12 22:08:30 -05:00
Chris Miles 94c1a50cc8 [GM Command] Door Manipulation Command Port (#1524)
* Initial commit

* Push latest

* Update door_manipulation.cpp

* More door work

* More doors work

* Upload notes

* Finalize changes

* Remove comment

* Add missing chat line

* Swapped URI parser with something not using deprecated C++ functions
2021-09-12 22:08:04 -05:00
Kinglykrab 31ab1d4287 Merge pull request #1522 from EQEmu/feature/temp_merchant_list_bug_fix
[Bug Fix] Resolves issue where loading temporary merchant list "fails" because there aren't any to load.
2021-09-11 10:35:39 -04:00
Paul Coene b6ba493450 Merge pull request #1528 from noudess/merchant
Fix bug where stacks of non-stackable items are removed when you buy 1.
2021-09-11 10:19:37 -04:00
Paul Coene c613db2338 Merge pull request #1527 from noudess/toofar
Hack to fix melee chasing fleeing mobs "too far" issues.
2021-09-10 18:21:38 -04:00
Noudess 05ac8499df Fix bug where stacks of non-stackable items are removed when you buy 1. 2021-09-10 10:18:59 -04:00
Chris Miles 69244a094d Update changelog.txt 2021-09-09 09:52:26 -05:00
Noudess 1155673642 Hack to fix melee chasing fleeing mobs "too far" issues. 2021-09-09 08:42:14 -04:00
Kinglykrab 930079959c [Bug Fix] Resolves issue where loading temporary merchant list "fails" because there aren't any to load. 2021-09-06 20:37:34 -04:00
217 changed files with 31951 additions and 19526 deletions
+2 -1
View File
@@ -9,7 +9,8 @@
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17"
"cppStandard": "c++17",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
+8
View File
@@ -0,0 +1,8 @@
{
"files.associations": {
"chrono": "cpp",
"xutility": "cpp",
"iterator": "cpp",
"*.ipp": "cpp"
}
}
+14 -14
View File
@@ -6,7 +6,7 @@
{
"label": "make",
"type": "shell",
"command": "cd bin && make",
"command": "mkdir -p build && cd build && make",
"group": {
"kind": "build",
"isDefault": true
@@ -18,7 +18,7 @@
{
"label": "make clean",
"type": "shell",
"command": "cd bin && make clean",
"command": "mkdir -p build && cd build && make clean",
"group": {
"kind": "build",
"isDefault": true
@@ -30,7 +30,7 @@
{
"label": "cmake",
"type": "shell",
"command": "mkdir -p bin && cd bin && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..",
"command": "mkdir -p build && cd build && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..",
"group": {
"kind": "build",
"isDefault": true
@@ -52,7 +52,7 @@
{
"label": "download maps",
"type": "shell",
"command": "mkdir -p bin && cd bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip",
"command": "mkdir -p build && cd build/bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip",
"group": {
"kind": "build",
"isDefault": true
@@ -64,7 +64,7 @@
{
"label": "download quests",
"type": "shell",
"command": "mkdir -p bin && cd bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests",
"command": "mkdir -p build && cd build/bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests",
"group": {
"kind": "build",
"isDefault": true
@@ -76,7 +76,7 @@
{
"label": "download eqemu_config",
"type": "shell",
"command": "mkdir -p bin && cd bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json",
"command": "mkdir -p build && cd build/bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json",
"group": {
"kind": "build",
"isDefault": true
@@ -88,7 +88,7 @@
{
"label": "rebuild database (mariadb must be started)",
"type": "shell",
"command": "mkdir -p bin && cd bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl linux_login_server_setup'",
"command": "mkdir -p build && cd build/bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl assets && ./eqemu_server.pl lua_modules && ./eqemu_server.pl opcodes && ./eqemu_server.pl linux_login_server_setup'",
"group": {
"kind": "build",
"isDefault": true
@@ -100,7 +100,7 @@
{
"label": "zone 7000",
"type": "shell",
"command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000",
"command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000",
"group": {
"kind": "test",
"isDefault": true
@@ -109,7 +109,7 @@
{
"label": "zone 7001",
"type": "shell",
"command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001",
"command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001",
"group": {
"kind": "test",
"isDefault": true
@@ -118,7 +118,7 @@
{
"label": "loginserver",
"type": "shell",
"command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver",
"command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver",
"group": {
"kind": "test",
"isDefault": true
@@ -127,7 +127,7 @@
{
"label": "shared_memory, world",
"type": "shell",
"command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world",
"command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world",
"group": {
"kind": "test",
"isDefault": true
@@ -136,7 +136,7 @@
{
"label": "queryserv",
"type": "shell",
"command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv",
"command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv",
"group": {
"kind": "test",
"isDefault": true
@@ -145,7 +145,7 @@
{
"label": "mariadb",
"type": "shell",
"command": "docker stop mariadb | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest",
"command": "docker stop mariadb | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest",
"group": {
"kind": "test",
"isDefault": true
@@ -154,7 +154,7 @@
{
"label": "ucs",
"type": "shell",
"command": "docker stop ucs | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src -p 7778:7778 --name ucs --network=eqemu eqemu/server:0.0.3 gdb -ex run ./ucs",
"command": "docker stop ucs | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src -p 7778:7778 --name ucs --network=eqemu eqemu/server:0.0.3 gdb -ex run ./ucs",
"group": {
"kind": "test",
"isDefault": true
+3
View File
@@ -224,6 +224,9 @@ IF(OpenSSL_FOUND AND MBEDTLS_FOUND)
SET(TLS_LIBRARY_LIBS ${OPENSSL_LIBRARIES})
SET(TLS_LIBRARY_INCLUDE ${OPENSSL_INCLUDE_DIR})
ADD_DEFINITIONS(-DEQEMU_USE_OPENSSL)
IF(${OPENSSL_VERSION} VERSION_GREATER_EQUAL "1.1.1")
ADD_DEFINITIONS(-DCPPHTTPLIB_OPENSSL_SUPPORT)
ENDIF()
ELSEIF(TLS_LIBRARY_SELECTION STREQUAL "mbedTLS")
SET(TLS_LIBRARY_TYPE " mbedTLS")
SET(TLS_LIBRARY_ENABLED ON)
+1 -1
View File
@@ -3,7 +3,7 @@
############################################
#
# New changelog can be found here
# https://eqemu.gitbook.io/changelog/
# https://docs.eqemu.io/server/changelog/server
#
############################################
# Deprecated
+3 -1
View File
@@ -526,6 +526,7 @@ SET(common_headers
guild_base.h
guilds.h
http/httplib.h
http/uri.h
inventory_profile.h
inventory_slot.h
ipc_mutex.h
@@ -638,7 +639,8 @@ SET(common_headers
StackWalker/StackWalker.h
util/memory_stream.h
util/directory.h
util/uuid.h)
util/uuid.h
)
SOURCE_GROUP(Event FILES
event/event_loop.h
+125 -27
View File
@@ -39,6 +39,7 @@
#include "unix.h"
#include <netinet/in.h>
#include <sys/time.h>
#endif
#include "database.h"
@@ -46,6 +47,8 @@
#include "extprofile.h"
#include "string_util.h"
#include "database_schema.h"
#include "http/httplib.h"
#include "http/uri.h"
extern Client client;
@@ -2039,62 +2042,64 @@ void Database::ClearRaidLeader(uint32 gid, uint32 rid)
QueryDatabase(query);
}
void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win)
void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win, bool remove)
{
std::string field;
switch(theme)
{
case 1:
{
switch(theme) {
case LDoNThemes::GUK: {
field = "guk_";
break;
}
case 2:
{
case LDoNThemes::MIR: {
field = "mir_";
break;
}
case 3:
{
case LDoNThemes::MMC: {
field = "mmc_";
break;
}
case 4:
{
case LDoNThemes::RUJ: {
field = "ruj_";
break;
}
case 5:
{
case LDoNThemes::TAK: {
field = "tak_";
break;
}
default:
{
default: {
return;
}
}
if (win)
field += "wins";
else
field += "losses";
field += win ? "wins" : "losses";
std::string field_operation = remove ? "-" : "+";
std::string query = StringFormat("UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u",field.c_str(), field.c_str(), char_id);
std::string query = fmt::format(
"UPDATE `adventure_stats` SET {} = {} {} 1 WHERE player_id = {}",
field,
field,
field_operation,
char_id
);
auto results = QueryDatabase(query);
if (results.RowsAffected() != 0)
if (results.RowsAffected() != 0) {
return;
}
query = StringFormat("INSERT INTO `adventure_stats` SET %s=1, player_id=%u", field.c_str(), char_id);
QueryDatabase(query);
if (!remove) {
query = fmt::format(
"INSERT INTO `adventure_stats` SET {} = 1, player_id = {}",
field,
char_id
);
QueryDatabase(query);
}
}
bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as)
{
std::string query = StringFormat(
std::string query = fmt::format(
"SELECT "
"`guk_wins`, "
"`mir_wins`, "
@@ -2109,7 +2114,7 @@ bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as)
"FROM "
"`adventure_stats` "
"WHERE "
"player_id = %u ",
"player_id = {}",
char_id
);
auto results = QueryDatabase(query);
@@ -2268,6 +2273,35 @@ int Database::GetIPExemption(std::string account_ip) {
return RuleI(World, MaxClientsPerIP);
}
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
std::string query = fmt::format(
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
account_ip
);
auto results = QueryDatabase(query);
uint32 exemption_id = 0;
if (results.Success() && results.RowCount() > 0) {
auto row = results.begin();
exemption_id = atoi(row[0]);
}
query = fmt::format(
"INSERT INTO `ip_exemptions` (`exemption_ip`, `exemption_amount`) VALUES ('{}', {})",
account_ip,
exemption_amount
);
if (exemption_id != 0) {
query = fmt::format(
"UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'",
exemption_amount,
account_ip
);
}
QueryDatabase(query);
}
int Database::GetInstanceID(uint32 char_id, uint32 zone_id) {
std::string query = StringFormat("SELECT instance_list.id FROM instance_list INNER JOIN instance_list_player ON instance_list.id = instance_list_player.id WHERE instance_list.zone = '%i' AND instance_list_player.charid = '%i'", zone_id, char_id);
auto results = QueryDatabase(query);
@@ -2418,3 +2452,67 @@ bool Database::CopyCharacter(
return true;
}
void Database::SourceDatabaseTableFromUrl(std::string table_name, std::string url)
{
try {
uri request_uri(url);
LogHTTP(
"[SourceDatabaseTableFromUrl] parsing url [{}] path [{}] host [{}] query_string [{}] protocol [{}] port [{}]",
url,
request_uri.get_path(),
request_uri.get_host(),
request_uri.get_query(),
request_uri.get_scheme(),
request_uri.get_port()
);
if (!DoesTableExist(table_name)) {
LogMySQLQuery("Table [{}] does not exist. Downloading from Github and installing...", table_name);
// http get request
httplib::Client cli(
fmt::format(
"{}://{}",
request_uri.get_scheme(),
request_uri.get_host()
).c_str()
);
cli.set_connection_timeout(0, 60000000); // 60 sec
cli.set_read_timeout(60, 0); // 60 seconds
cli.set_write_timeout(60, 0); // 60 seconds
int sourced_queries = 0;
if (auto res = cli.Get(request_uri.get_path().c_str())) {
if (res->status == 200) {
for (auto &s: SplitString(res->body, ';')) {
if (!trim(s).empty()) {
auto results = QueryDatabase(s);
if (!results.ErrorMessage().empty()) {
LogError("Error sourcing SQL [{}]", results.ErrorMessage());
return;
}
sourced_queries++;
}
}
}
}
else {
LogError("Error retrieving URL [{}]", url);
}
LogMySQLQuery(
"Table [{}] installed. Sourced [{}] queries",
table_name,
sourced_queries
);
}
}
catch (std::invalid_argument iae) {
LogError("[SourceDatabaseTableFromUrl] URI parser error [{}]", iae.what());
}
}
+5 -2
View File
@@ -176,7 +176,7 @@ public:
/* Adventure related. */
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win);
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false);
bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as);
/* Account Related */
@@ -198,7 +198,8 @@ public:
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
void SetAgreementFlag(uint32 acctid);
int GetIPExemption(std::string account_ip);
int GetIPExemption(std::string account_ip);
void SetIPExemption(std::string account_ip, int exemption_amount);
int GetInstanceID(uint32 char_id, uint32 zone_id);
@@ -269,6 +270,8 @@ public:
int CountInvSnapshots();
void ClearInvSnapshots(bool from_now = false);
void SourceDatabaseTableFromUrl(std::string table_name, std::string url);
private:
+45
View File
@@ -18,6 +18,7 @@
*/
#include "emu_constants.h"
#include "languages.h"
int16 EQ::invtype::GetInvTypeSize(int16 inv_type) {
@@ -152,3 +153,47 @@ int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
return 0;
}
const std::map<int, std::string>& EQ::constants::GetLanguageMap()
{
static const std::map<int, std::string> language_map = {
{ LANG_COMMON_TONGUE, "Common Tongue" },
{ LANG_BARBARIAN, "Barbarian" },
{ LANG_ERUDIAN, "Erudian" },
{ LANG_ELVISH, "Elvish" },
{ LANG_DARK_ELVISH, "Dark Elvish" },
{ LANG_DWARVISH, "Dwarvish" },
{ LANG_TROLL, "Troll" },
{ LANG_OGRE, "Ogre" },
{ LANG_GNOMISH, "Gnomish" },
{ LANG_HALFLING, "Halfling" },
{ LANG_THIEVES_CANT, "Thieves Cant" },
{ LANG_OLD_ERUDIAN, "Old Erudian" },
{ LANG_ELDER_ELVISH, "Elder Elvish" },
{ LANG_FROGLOK, "Froglok" },
{ LANG_GOBLIN, "Goblin" },
{ LANG_GNOLL, "Gnoll" },
{ LANG_COMBINE_TONGUE, "Combine Tongue" },
{ LANG_ELDER_TEIRDAL, "Elder Teirdal" },
{ LANG_LIZARDMAN, "Lizardman" },
{ LANG_ORCISH, "Orcish" },
{ LANG_FAERIE, "Faerie" },
{ LANG_DRAGON, "Dragon" },
{ LANG_ELDER_DRAGON, "Elder Dragon" },
{ LANG_DARK_SPEECH, "Dark Speech" },
{ LANG_VAH_SHIR, "Vah Shir" },
{ LANG_ALARAN, "Alaran" },
{ LANG_HADAL, "Hadal" },
{ LANG_UNKNOWN, "Unknown" }
};
return language_map;
}
std::string EQ::constants::GetLanguageName(int language_id)
{
if (language_id >= LANG_COMMON_TONGUE && language_id <= LANG_UNKNOWN) {
auto languages = EQ::constants::GetLanguageMap();
return languages[language_id];
}
return std::string();
}
+3
View File
@@ -223,6 +223,9 @@ namespace EQ
const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type);
extern const std::map<int, std::string>& GetLanguageMap();
std::string GetLanguageName(int language_id);
const int STANCE_TYPE_FIRST = stancePassive;
const int STANCE_TYPE_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE;
+35
View File
@@ -974,4 +974,39 @@ enum class DynamicZoneMemberStatus : uint8_t
LinkDead
};
enum LDoNThemes {
Unused = 0,
GUK,
MIR,
MMC,
RUJ,
TAK
};
enum LDoNThemeBits {
UnusedBit = 0,
GUKBit = 1,
MIRBit = 2,
MMCBit = 4,
RUJBit = 8,
TAKBit = 16
};
enum StartZoneIndex {
Odus = 0,
Qeynos,
Halas,
Rivervale,
Freeport,
Neriak,
Grobb,
Oggok,
Kaladim,
GreaterFaydark,
Felwithe,
Akanon,
Cabilis,
SharVahl
};
#endif /*COMMON_EQ_CONSTANTS_H*/
+7 -5
View File
@@ -384,7 +384,9 @@ struct NewZone_Struct {
/*0716*/ uint32 FastRegenEndurance;
/*0720*/ uint32 NPCAggroMaxDist;
/*0724*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, if this value is 0, it prevents you from running off edges that would end up underworld
/*0728*/
/*0728*/ uint32 LavaDamage; // Seen 50
/*0732*/ uint32 MinLavaDamage; // Seen 10
/*0736*/
};
/*
@@ -4340,8 +4342,8 @@ struct AARankPrereq_Struct
struct AARankEffect_Struct
{
int32 effect_id;
int32 base1;
int32 base2;
int32 base_value;
int32 limit_value;
int32 slot;
};
@@ -4349,8 +4351,8 @@ struct AARankEffect_Struct
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
};
+14 -1
View File
@@ -44,6 +44,12 @@ void EQEmuConfig::parse_config()
if (_root["server"]["world"]["loginserver"].get("legacy", "0").asString() == "1") { LoginLegacy = true; }
LoginAccount = _root["server"]["world"]["loginserver"].get("account", "").asString();
LoginPassword = _root["server"]["world"]["loginserver"].get("password", "").asString();
// at least today, this is wrong a majority of the time
// remove this if eqemulator ever upgrades its loginserver
if (LoginHost.find("login.eqemulator.net") != std::string::npos) {
LoginLegacy = true;
}
}
else {
char str[32];
@@ -62,12 +68,19 @@ void EQEmuConfig::parse_config()
loginconfig->LoginLegacy = false;
if (_root["server"]["world"][str].get("legacy", "0").asString() == "1") { loginconfig->LoginLegacy = true; }
// at least today, this is wrong a majority of the time
// remove this if eqemulator ever upgrades its loginserver
if (loginconfig->LoginHost.find("login.eqemulator.net") != std::string::npos) {
loginconfig->LoginLegacy = true;
}
loginlist.Insert(loginconfig);
} while (LoginCount < 100);
}
//<locked> from xml converts to json as locked: "", so i default to "false".
//<locked> from xml converts to json as locked: "", so i default to "false".
//The only way to enable locked is by switching to true, meaning this value is always false until manually set true
Locked = false;
if (_root["server"]["world"].get("locked", "false").asString() == "true") { Locked = true; }
+2
View File
@@ -130,6 +130,8 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::Scheduler].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Cheat].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
/**
* RFC 5424
+4
View File
@@ -125,6 +125,8 @@ namespace Logs {
Cheat,
ClientList,
DiaWind,
HTTP,
Saylink,
MaxCategoryID /* Don't Remove this */
};
@@ -208,6 +210,8 @@ namespace Logs {
"Cheat",
"ClientList",
"DialogueWindow",
"HTTP",
"Saylink",
};
}
+26
View File
@@ -676,6 +676,26 @@
OutF(LogSys, Logs::Detail, Logs::DiaWind, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogHTTP(message, ...) do {\
if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogHTTPDetail(message, ...) do {\
if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogSaylink(message, ...) do {\
if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogSaylinkDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@@ -1066,6 +1086,12 @@
#define LogDiaWindDetail(message, ...) do {\
} while (0)
#define LogHTTP(message, ...) do {\
} while (0)
#define LogHTTPDetail(message, ...) do {\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
} while (0)
+633
View File
@@ -0,0 +1,633 @@
// Copyright (C) 2015 Ben Lewis <benjf5+github@gmail.com>
// Licensed under the MIT license.
// https://github.com/ben-zen/uri-library
#pragma once
#include <cctype>
#include <map>
#include <string>
#include <stdexcept>
#include <utility>
class uri {
/* URIs are broadly divided into two categories: hierarchical and
* non-hierarchical. Both hierarchical URIs and non-hierarchical URIs have a
* few elements in common; all URIs have a scheme of one or more alphanumeric
* characters followed by a colon, and they all may optionally have a query
* component preceded by a question mark, and a fragment component preceded by
* an octothorpe (hash mark: '#'). The query consists of stanzas separated by
* either ampersands ('&') or semicolons (';') (but only one or the other),
* and each stanza consists of a key and an optional value; if the value
* exists, the key and value must be divided by an equals sign.
*
* The following is an example from Wikipedia of a hierarchical URI:
* scheme:[//[user:password@]domain[:port]][/]path[?query][#fragment]
*/
public:
enum class scheme_category {
Hierarchical,
NonHierarchical
};
enum class component {
Scheme,
Content,
Username,
Password,
Host,
Port,
Path,
Query,
Fragment
};
enum class query_argument_separator {
ampersand,
semicolon
};
uri(
char const *uri_text, scheme_category category = scheme_category::Hierarchical,
query_argument_separator separator = query_argument_separator::ampersand
) :
m_category(category),
m_path_is_rooted(false),
m_port(0),
m_separator(separator)
{
setup(std::string(uri_text), category);
};
uri(
std::string const &uri_text, scheme_category category = scheme_category::Hierarchical,
query_argument_separator separator = query_argument_separator::ampersand
) :
m_category(category),
m_path_is_rooted(false),
m_port(0),
m_separator(separator)
{
setup(uri_text, category);
};
uri(
std::map<component, std::string> const &components,
scheme_category category,
bool rooted_path,
query_argument_separator separator = query_argument_separator::ampersand
) :
m_category(category),
m_path_is_rooted(rooted_path),
m_separator(separator)
{
if (components.count(component::Scheme)) {
if (components.at(component::Scheme).length() == 0) {
throw std::invalid_argument("Scheme cannot be empty.");
}
m_scheme = components.at(component::Scheme);
}
else {
throw std::invalid_argument("A URI must have a scheme.");
}
if (category == scheme_category::Hierarchical) {
if (components.count(component::Content)) {
throw std::invalid_argument("The content component is only for use in non-hierarchical URIs.");
}
bool has_username = components.count(component::Username);
bool has_password = components.count(component::Password);
if (has_username && has_password) {
m_username = components.at(component::Username);
m_password = components.at(component::Password);
}
else if ((has_username && !has_password) || (!has_username && has_password)) {
throw std::invalid_argument("If a username or password is supplied, both must be provided.");
}
if (components.count(component::Host)) {
m_host = components.at(component::Host);
}
if (components.count(component::Port)) {
m_port = std::stoul(components.at(component::Port));
}
if (components.count(component::Path)) {
m_path = components.at(component::Path);
}
else {
throw std::invalid_argument("A path is required on a hierarchical URI, even an empty path.");
}
}
else {
if (components.count(component::Username)
|| components.count(component::Password)
|| components.count(component::Host)
|| components.count(component::Port)
|| components.count(component::Path)) {
throw std::invalid_argument("None of the hierarchical components are allowed in a non-hierarchical URI.");
}
if (components.count(component::Content)) {
m_content = components.at(component::Content);
}
else {
throw std::invalid_argument(
"Content is a required component for a non-hierarchical URI, even an empty string."
);
}
}
if (components.count(component::Query)) {
m_query = components.at(component::Query);
}
if (components.count(component::Fragment)) {
m_fragment = components.at(component::Fragment);
}
}
uri(uri const &other, std::map<component, std::string> const &replacements) :
m_category(other.m_category),
m_path_is_rooted(other.m_path_is_rooted),
m_separator(other.m_separator)
{
m_scheme = (replacements.count(component::Scheme))
? replacements.at(component::Scheme) : other.m_scheme;
if (m_category == scheme_category::Hierarchical) {
m_username = (replacements.count(component::Username))
? replacements.at(component::Username) : other.m_username;
m_password = (replacements.count(component::Password))
? replacements.at(component::Password) : other.m_password;
m_host = (replacements.count(component::Host))
? replacements.at(component::Host) : other.m_host;
m_port = (replacements.count(component::Port))
? std::stoul(replacements.at(component::Port)) : other.m_port;
m_path = (replacements.count(component::Path))
? replacements.at(component::Path) : other.m_path;
}
else {
m_content = (replacements.count(component::Content))
? replacements.at(component::Content) : other.m_content;
}
m_query = (replacements.count(component::Query))
? replacements.at(component::Query) : other.m_query;
m_fragment = (replacements.count(component::Fragment))
? replacements.at(component::Fragment) : other.m_fragment;
}
// Copy constructor; just use the copy assignment operator internally.
uri(uri const &other)
{
*this = other;
};
// Copy assignment operator
uri &operator=(uri const &other)
{
if (this != &other) {
m_scheme = other.m_scheme;
m_content = other.m_content;
m_username = other.m_username;
m_password = other.m_password;
m_host = other.m_host;
m_path = other.m_path;
m_query = other.m_query;
m_fragment = other.m_fragment;
m_query_dict = other.m_query_dict;
m_category = other.m_category;
m_port = other.m_port;
m_path_is_rooted = other.m_path_is_rooted;
m_separator = other.m_separator;
}
return *this;
}
~uri() {};
std::string const &get_scheme() const
{
return m_scheme;
};
scheme_category get_scheme_category() const
{
return m_category;
};
std::string const &get_content() const
{
if (m_category != scheme_category::NonHierarchical) {
throw std::domain_error("The content component is only valid for non-hierarchical URIs.");
}
return m_content;
};
std::string const &get_username() const
{
if (m_category != scheme_category::Hierarchical) {
throw std::domain_error("The username component is only valid for hierarchical URIs.");
}
return m_username;
};
std::string const &get_password() const
{
if (m_category != scheme_category::Hierarchical) {
throw std::domain_error("The password component is only valid for hierarchical URIs.");
}
return m_password;
};
std::string const &get_host() const
{
if (m_category != scheme_category::Hierarchical) {
throw std::domain_error("The host component is only valid for hierarchical URIs.");
}
return m_host;
};
unsigned long get_port() const
{
if (m_category != scheme_category::Hierarchical) {
throw std::domain_error("The port component is only valid for hierarchical URIs.");
}
return m_port;
};
std::string const &get_path() const
{
if (m_category != scheme_category::Hierarchical) {
throw std::domain_error("The path component is only valid for hierarchical URIs.");
}
return m_path;
};
std::string const &get_query() const
{
return m_query;
};
std::map<std::string, std::string> const &get_query_dictionary() const
{
return m_query_dict;
};
std::string const &get_fragment() const
{
return m_fragment;
};
std::string to_string() const
{
std::string full_uri;
full_uri.append(m_scheme);
full_uri.append(":");
if (m_content.length() > m_path.length()) {
full_uri.append("//");
if (!(m_username.empty() || m_password.empty())) {
full_uri.append(m_username);
full_uri.append(":");
full_uri.append(m_password);
full_uri.append("@");
}
full_uri.append(m_host);
if (m_port != 0) {
full_uri.append(":");
full_uri.append(std::to_string(m_port));
}
}
if (m_path_is_rooted) {
full_uri.append("/");
}
full_uri.append(m_path);
if (!m_query.empty()) {
full_uri.append("?");
full_uri.append(m_query);
}
if (!m_fragment.empty()) {
full_uri.append("#");
full_uri.append(m_fragment);
}
return full_uri;
};
private:
void setup(std::string const &uri_text, scheme_category category)
{
size_t const uri_length = uri_text.length();
if (uri_length == 0) {
throw std::invalid_argument("URIs cannot be of zero length.");
}
std::string::const_iterator cursor = parse_scheme(
uri_text,
uri_text.begin());
// After calling parse_scheme, *cursor == ':'; none of the following parsers
// expect a separator character, so we advance the cursor upon calling them.
cursor = parse_content(uri_text, (cursor + 1));
if ((cursor != uri_text.end()) && (*cursor == '?')) {
cursor = parse_query(uri_text, (cursor + 1));
}
if ((cursor != uri_text.end()) && (*cursor == '#')) {
cursor = parse_fragment(uri_text, (cursor + 1));
}
init_query_dictionary(); // If the query string is empty, this will be empty too.
};
std::string::const_iterator parse_scheme(
std::string const &uri_text,
std::string::const_iterator scheme_start
)
{
std::string::const_iterator scheme_end = scheme_start;
while ((scheme_end != uri_text.end()) && (*scheme_end != ':')) {
if (!(std::isalnum(*scheme_end) || (*scheme_end == '-')
|| (*scheme_end == '+') || (*scheme_end == '.'))) {
throw std::invalid_argument(
"Invalid character found in the scheme component. Supplied URI was: \""
+ uri_text + "\"."
);
}
++scheme_end;
}
if (scheme_end == uri_text.end()) {
throw std::invalid_argument(
"End of URI found while parsing the scheme. Supplied URI was: \""
+ uri_text + "\"."
);
}
if (scheme_start == scheme_end) {
throw std::invalid_argument(
"Scheme component cannot be zero-length. Supplied URI was: \""
+ uri_text + "\"."
);
}
m_scheme = std::move(std::string(scheme_start, scheme_end));
return scheme_end;
};
std::string::const_iterator parse_content(
std::string const &uri_text,
std::string::const_iterator content_start
)
{
std::string::const_iterator content_end = content_start;
while ((content_end != uri_text.end()) && (*content_end != '?') && (*content_end != '#')) {
++content_end;
}
m_content = std::move(std::string(content_start, content_end));
if ((m_category == scheme_category::Hierarchical) && (m_content.length() > 0)) {
// If it's a hierarchical URI, the content should be parsed for the hierarchical components.
std::string::const_iterator path_start = m_content.begin();
std::string::const_iterator path_end = m_content.end();
if (!m_content.compare(0, 2, "//")) {
// In this case an authority component is present.
std::string::const_iterator authority_cursor = (m_content.begin() + 2);
if (m_content.find_first_of('@') != std::string::npos) {
std::string::const_iterator userpass_divider = parse_username(
uri_text,
m_content,
authority_cursor
);
authority_cursor = parse_password(uri_text, m_content, (userpass_divider + 1));
// After this call, *authority_cursor == '@', so we skip over it.
++authority_cursor;
}
authority_cursor = parse_host(uri_text, m_content, authority_cursor);
if ((authority_cursor != m_content.end()) && (*authority_cursor == ':')) {
authority_cursor = parse_port(uri_text, m_content, (authority_cursor + 1));
}
if ((authority_cursor != m_content.end()) && (*authority_cursor == '/')) {
// Then the path is rooted, and we should note this.
m_path_is_rooted = true;
path_start = authority_cursor + 1;
}
// If we've reached the end and no path is present then set path_start
// to the end.
if (authority_cursor == m_content.end()) {
path_start = m_content.end();
}
}
else if (!m_content.compare(0, 1, "/")) {
m_path_is_rooted = true;
++path_start;
}
// We can now build the path based on what remains in the content string,
// since that's all that exists after the host and optional port component.
m_path = std::move(std::string(path_start, path_end));
}
return content_end;
};
std::string::const_iterator parse_username(
std::string const &uri_text,
std::string const &content,
std::string::const_iterator username_start
)
{
std::string::const_iterator username_end = username_start;
// Since this is only reachable when '@' was in the content string, we can
// ignore the end-of-string case.
while (*username_end != ':') {
if (*username_end == '@') {
throw std::invalid_argument(
"Username must be followed by a password. Supplied URI was: \""
+ uri_text + "\"."
);
}
++username_end;
}
m_username = std::move(std::string(username_start, username_end));
return username_end;
};
std::string::const_iterator parse_password(
std::string const &uri_text,
std::string const &content,
std::string::const_iterator password_start
)
{
std::string::const_iterator password_end = password_start;
while (*password_end != '@') {
++password_end;
}
m_password = std::move(std::string(password_start, password_end));
return password_end;
};
std::string::const_iterator parse_host(
std::string const &uri_text,
std::string const &content,
std::string::const_iterator host_start
)
{
std::string::const_iterator host_end = host_start;
// So, the host can contain a few things. It can be a domain, it can be an
// IPv4 address, it can be an IPv6 address, or an IPvFuture literal. In the
// case of those last two, it's of the form [...] where what's between the
// brackets is a matter of which IPv?? version it is.
while (host_end != content.end()) {
if (*host_end == '[') {
// We're parsing an IPv6 or IPvFuture address, so we should handle that
// instead of the normal procedure.
while ((host_end != content.end()) && (*host_end != ']')) {
++host_end;
}
if (host_end == content.end()) {
throw std::invalid_argument(
"End of content component encountered "
"while parsing the host component. "
"Supplied URI was: \""
+ uri_text + "\"."
);
}
++host_end;
break;
// We can stop looping, we found the end of the IP literal, which is the
// whole of the host component when one's in use.
}
else if ((*host_end == ':') || (*host_end == '/')) {
break;
}
else {
++host_end;
}
}
m_host = std::move(std::string(host_start, host_end));
return host_end;
};
std::string::const_iterator parse_port(
std::string const &uri_text,
std::string const &content,
std::string::const_iterator port_start
)
{
std::string::const_iterator port_end = port_start;
while ((port_end != content.end()) && (*port_end != '/')) {
if (!std::isdigit(*port_end)) {
throw std::invalid_argument(
"Invalid character while parsing the port. "
"Supplied URI was: \"" + uri_text + "\"."
);
}
++port_end;
}
m_port = std::stoul(std::string(port_start, port_end));
return port_end;
};
std::string::const_iterator parse_query(
std::string const &uri_text,
std::string::const_iterator query_start
)
{
std::string::const_iterator query_end = query_start;
while ((query_end != uri_text.end()) && (*query_end != '#')) {
// Queries can contain almost any character except hash, which is reserved
// for the start of the fragment.
++query_end;
}
m_query = std::move(std::string(query_start, query_end));
return query_end;
};
std::string::const_iterator parse_fragment(
std::string const &uri_text,
std::string::const_iterator fragment_start
)
{
m_fragment = std::move(std::string(fragment_start, uri_text.end()));
return uri_text.end();
};
void init_query_dictionary()
{
if (!m_query.empty()) {
// Loop over the query string looking for '&'s, then check each one for
// an '=' to find keys and values; if there's not an '=' then the key
// will have an empty value in the map.
char separator = (m_separator == query_argument_separator::ampersand) ? '&' : ';';
size_t carat = 0;
size_t stanza_end = m_query.find_first_of(separator);
do {
std::string stanza = m_query.substr(
carat,
((stanza_end != std::string::npos) ? (stanza_end - carat) : std::string::npos));
size_t key_value_divider = stanza.find_first_of('=');
std::string key = stanza.substr(0, key_value_divider);
std::string value;
if (key_value_divider != std::string::npos) {
value = stanza.substr((key_value_divider + 1));
}
if (m_query_dict.count(key) != 0) {
throw std::invalid_argument("Bad key in the query string!");
}
m_query_dict.emplace(key, value);
carat = ((stanza_end != std::string::npos) ? (stanza_end + 1)
: std::string::npos);
stanza_end = m_query.find_first_of(separator, carat);
} while ((stanza_end != std::string::npos)
|| (carat != std::string::npos));
}
}
std::string m_scheme;
std::string m_content;
std::string m_username;
std::string m_password;
std::string m_host;
std::string m_path;
std::string m_query;
std::string m_fragment;
std::map<std::string, std::string> m_query_dict;
scheme_category m_category;
unsigned long m_port;
bool m_path_is_rooted;
query_argument_separator m_separator;
};
+1 -1
View File
@@ -224,7 +224,7 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id, uint8 bagidx) con
return GetItem(InventoryProfile::CalcSlotId(slot_id, bagidx));
}
// Put an item snto specified slot
// Put an item into specified slot
int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst)
{
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) {
+1 -1
View File
@@ -482,7 +482,7 @@ namespace EQ
uint32 Haste;
uint32 DamageShield;
uint32 RecastDelay;
uint32 RecastType;
int RecastType;
uint32 AugDistiller;
bool Attuneable;
bool NoPet;
+161 -12
View File
@@ -11,6 +11,7 @@ EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr<
m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1));
m_connection->Start();
m_legacy_mode = false;
}
EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
@@ -19,17 +20,73 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p)
{
// pad zero size packets
if (p.Length() == 0) {
p.PutUInt8(0, 0);
if (m_legacy_mode) {
if (!m_connection)
return;
if (opcode == ServerOP_UsertoWorldReq) {
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
req.PutUInt32(i, req_in->lsaccountid); i += 4;
req.PutUInt32(i, req_in->worldid); i += 4;
req.PutUInt32(i, req_in->FromID); i += 4;
req.PutUInt32(i, req_in->ToID); i += 4;
req.PutData(i, req_in->IPAddr, 64); i += 64;
EQ::Net::DynamicPacket out;
out.PutUInt16(0, ServerOP_UsertoWorldReqLeg);
out.PutUInt16(2, req.Length() + 4);
out.PutPacket(4, req);
m_connection->Write((const char*)out.Data(), out.Length());
return;
}
if (opcode == ServerOP_LSClientAuth) {
auto req_in = (ClientAuth_Struct*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
req.PutUInt32(i, req_in->loginserver_account_id); i += 4;
req.PutData(i, req_in->account_name, 30); i += 30;
req.PutData(i, req_in->key, 30); i += 30;
req.PutUInt8(i, req_in->lsadmin); i += 1;
req.PutUInt16(i, req_in->is_world_admin); i += 2;
req.PutUInt32(i, req_in->ip); i += 4;
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
EQ::Net::DynamicPacket out;
out.PutUInt16(0, ServerOP_LSClientAuthLeg);
out.PutUInt16(2, req.Length() + 4);
out.PutPacket(4, req);
m_connection->Write((const char*)out.Data(), out.Length());
return;
}
EQ::Net::DynamicPacket out;
out.PutUInt16(0, opcode);
out.PutUInt16(2, p.Length() + 4);
out.PutPacket(4, p);
m_connection->Write((const char*)out.Data(), out.Length());
} else {
// pad zero size packets
// pad packets that would cause a collision with legacy identification code
// It's unlikely we'd send a 4MB msg for any reason but just incase.
if (p.Length() == 0 || p.Length() == 43061256) {
p.PutUInt8(0, 0);
}
EQ::Net::DynamicPacket out;
out.PutUInt32(0, p.Length());
out.PutUInt16(4, opcode);
out.PutPacket(6, p);
InternalSend(ServertalkMessage, out);
}
EQ::Net::DynamicPacket out;
out.PutUInt32(0, p.Length());
out.PutUInt16(4, opcode);
out.PutPacket(6, p);
InternalSend(ServertalkMessage, out);
}
void EQ::Net::ServertalkServerConnection::SendPacket(ServerPacket *p)
@@ -54,17 +111,41 @@ void EQ::Net::ServertalkServerConnection::OnMessage(std::function<void(uint16_t,
void EQ::Net::ServertalkServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
{
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + sz);
ProcessReadBuffer();
if (m_legacy_mode) {
ProcessOldReadBuffer();
} else {
ProcessReadBuffer();
}
}
void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
constexpr size_t ls_info_size = sizeof(ServerNewLSInfo_Struct);
while (current < total) {
auto left = total - current;
if (left < 4) {
break;
}
auto leg_opcode = *(uint16_t*)&m_buffer[current];
auto leg_size = *(uint16_t*)&m_buffer[current + 2] - 4;
//this creates a small edge case where the exact size of a
//packet from the modern protocol can't be "43061256"
//so in send we pad it one byte if that's the case
if (leg_opcode == ServerOP_NewLSInfo && leg_size == sizeof(ServerNewLSInfo_Struct)) {
m_legacy_mode = true;
m_identifier = "World";
m_parent->ConnectionIdentified(this);
ProcessOldReadBuffer();
return;
}
/*
//header:
//uint32 length;
@@ -129,6 +210,57 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
}
}
void EQ::Net::ServertalkServerConnection::ProcessOldReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
while (current < total) {
auto left = total - current;
/*
//header:
//uint32 length;
//uint8 type;
*/
uint16_t length = 0;
uint16_t opcode = 0;
if (left < 4) {
break;
}
opcode = *(uint16_t*)&m_buffer[current];
length = *(uint16_t*)&m_buffer[current + 2];
if (length < 4) {
break;
}
length -= 4;
if (current + 4 + length > total) {
break;
}
if (length == 0) {
EQ::Net::DynamicPacket p;
ProcessMessageOld(opcode, p);
}
else {
EQ::Net::StaticPacket p(&m_buffer[current + 4], length);
ProcessMessageOld(opcode, p);
}
current += length + 4;
}
if (current == total) {
m_buffer.clear();
}
else {
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
}
}
void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c)
{
m_parent->ConnectionDisconnected(this);
@@ -144,7 +276,7 @@ void EQ::Net::ServertalkServerConnection::SendHello()
void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
{
if (!m_connection)
if (!m_connection || m_legacy_mode)
return;
EQ::Net::DynamicPacket out;
@@ -201,3 +333,20 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
LogError("Error parsing message from client: {0}", ex.what());
}
}
void EQ::Net::ServertalkServerConnection::ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p)
{
try {
auto cb = m_message_callbacks.find(opcode);
if (cb != m_message_callbacks.end()) {
cb->second(opcode, p);
}
if (m_message_callback) {
m_message_callback(opcode, p);
}
}
catch (std::exception &ex) {
LogError("Error parsing legacy message from client: {0}", ex.what());
}
}
@@ -27,11 +27,13 @@ namespace EQ
private:
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
void ProcessReadBuffer();
void ProcessOldReadBuffer();
void OnDisconnect(TCPConnection* c);
void SendHello();
void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p);
void ProcessHandshake(EQ::Net::Packet &p);
void ProcessMessage(EQ::Net::Packet &p);
void ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p);
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
ServertalkServer *m_parent;
@@ -41,6 +43,7 @@ namespace EQ
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
std::string m_identifier;
std::string m_uuid;
bool m_legacy_mode;
};
}
}
+2 -2
View File
@@ -1863,8 +1863,8 @@ namespace RoF
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown800 = -1;
eq->unknown844 = 600;
eq->unknown880 = 50;
eq->unknown884 = 10;
OUT(LavaDamage);
OUT(MinLavaDamage);
eq->unknown888 = 1;
eq->unknown889 = 0;
eq->unknown890 = 1;
+2 -2
View File
@@ -1919,8 +1919,8 @@ namespace RoF2
eq->SkyRelated2 = -1;
eq->NPCAggroMaxDist = 600;
eq->FilterID = 2008; // Guild Lobby observed value
eq->LavaDamage = 50;
eq->MinLavaDamage = 10;
OUT(LavaDamage);
OUT(MinLavaDamage);
eq->bDisallowManaStone = 1;
eq->bNoBind = 0;
eq->bNoAttack = 0;
+2 -2
View File
@@ -4361,8 +4361,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
/*16*/
};
+4 -4
View File
@@ -581,8 +581,8 @@ struct NewZone_Struct {
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
/*0872*/ uint32 scriptIDSomething3;
/*0876*/ uint32 SuspendBuffs;
/*0880*/ uint32 unknown880; // Seen 50
/*0884*/ uint32 unknown884; // Seen 10
/*0880*/ uint32 LavaDamage; // Seen 50
/*0884*/ uint32 MinLavaDamage; // Seen 10
/*0888*/ uint8 unknown888; // Seen 1
/*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj)
/*0890*/ uint8 unknown890; // Seen 1
@@ -4305,8 +4305,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
/*16*/
};
+4 -4
View File
@@ -1388,8 +1388,8 @@ namespace SoD
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown800 = -1;
eq->unknown844 = 600;
eq->unknown880 = 50;
eq->unknown884 = 10;
OUT(LavaDamage);
OUT(MinLavaDamage);
eq->unknown888 = 1;
eq->unknown889 = 0;
eq->unknown890 = 1;
@@ -1874,8 +1874,8 @@ namespace SoD
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].base_value = inapp->ReadUInt32();
eq->abilities[i].limit_value = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
+4 -4
View File
@@ -450,8 +450,8 @@ struct NewZone_Struct {
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
/*0872*/ uint32 scriptIDSomething3;
/*0876*/ uint32 SuspendBuffs;
/*0880*/ uint32 unknown880; //seen 50
/*0884*/ uint32 unknown884; //seen 10
/*0880*/ uint32 LavaDamage; //seen 50
/*0884*/ uint32 MinLavaDamage; //seen 10
/*0888*/ uint8 unknown888; //seen 1
/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj)
/*0890*/ uint8 unknown890; //seen 1
@@ -3748,8 +3748,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
/*16*/
};
+4 -4
View File
@@ -1066,8 +1066,8 @@ namespace SoF
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown796 = -1;
eq->unknown840 = 600;
eq->unknown876 = 50;
eq->unknown880 = 10;
OUT(LavaDamage);
OUT(MinLavaDamage);
eq->unknown884 = 1;
eq->unknown885 = 0;
eq->unknown886 = 1;
@@ -1545,8 +1545,8 @@ namespace SoF
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].base_value = inapp->ReadUInt32();
eq->abilities[i].limit_value = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
+4 -4
View File
@@ -454,8 +454,8 @@ struct NewZone_Struct {
/*0864*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
/*0868*/ uint32 scriptIDSomething3;
/*0872*/ uint32 SuspendBuffs;
/*0876*/ uint32 unknown876; //seen 50
/*0880*/ uint32 unknown880; //seen 10
/*0876*/ uint32 LavaDamage; //seen 50
/*0880*/ uint32 MinLavaDamage; //seen 10
/*0884*/ uint8 unknown884; //seen 1
/*0885*/ uint8 unknown885; //seen 0 (POK) or 1 (rujj)
/*0886*/ uint8 unknown886; //seen 1
@@ -3673,8 +3673,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
/*16*/
};
+2 -2
View File
@@ -1338,8 +1338,8 @@ namespace Titanium
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].base_value = inapp->ReadUInt32();
eq->abilities[i].limit_value = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
+2 -2
View File
@@ -3180,8 +3180,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
};
+4 -4
View File
@@ -1614,8 +1614,8 @@ namespace UF
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown800 = -1;
eq->unknown844 = 600;
eq->unknown880 = 50;
eq->unknown884 = 10;
OUT(LavaDamage);
OUT(MinLavaDamage);
eq->unknown888 = 1;
eq->unknown889 = 0;
eq->unknown890 = 1;
@@ -2139,8 +2139,8 @@ namespace UF
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].base_value = inapp->ReadUInt32();
eq->abilities[i].limit_value = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
+4 -4
View File
@@ -450,8 +450,8 @@ struct NewZone_Struct {
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
/*0872*/ uint32 scriptIDSomething3;
/*0876*/ uint32 SuspendBuffs;
/*0880*/ uint32 unknown880; //seen 50
/*0884*/ uint32 unknown884; //seen 10
/*0880*/ uint32 LavaDamage; //seen 50
/*0884*/ uint32 MinLavaDamage; //seen 10
/*0888*/ uint8 unknown888; //seen 1
/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj)
/*0890*/ uint8 unknown890; //seen 1
@@ -3803,8 +3803,8 @@ struct UseAA_Struct {
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
/*08*/ uint32 base2;
/*04*/ uint32 base_value;
/*08*/ uint32 limit_value;
/*12*/ uint32 slot;
/*16*/
};
+12
View File
@@ -2232,3 +2232,15 @@ bool PlayerAppearance::IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_v
}
return false;
}
const char* GetGenderName(uint32 gender_id) {
const char* gender_name = "Unknown";
if (gender_id == MALE) {
gender_name = "Male";
} else if (gender_id == FEMALE) {
gender_name = "Female";
} else if (gender_id == NEUTER) {
gender_name = "Neuter";
}
return gender_name;
}
+9
View File
@@ -851,6 +851,7 @@
const char* GetRaceIDName(uint16 race_id);
const char* GetPlayerRaceName(uint32 player_race_value);
const char* GetGenderName(uint32 gender_id);
uint32 GetPlayerRaceValue(uint16 race_id);
uint32 GetPlayerRaceBit(uint16 race_id);
@@ -1602,6 +1603,14 @@ namespace PlayerAppearance
#define RACE_FALLEN_KNIGHT_722 722
#define RACE_SERVANT_OF_SHADOW_723 723
#define RACE_LUCLIN_724 724
#define RACE_XARIC_725 725
#define RACE_DERVISH_726 726
#define RACE_DERVISH_727 727
#define RACE_LUCLIN_728 728
#define RACE_LUCLIN_729 729
#define RACE_ORB_730 730
#define RACE_LUCLIN_731 731
#define RACE_PEGASUS_732 732
#define RACE_INTERACTIVE_OBJECT_2250 2250
#endif
@@ -148,7 +148,7 @@ public:
entry.zone_in_time = 1800;
entry.win_points = 0;
entry.lose_points = 0;
entry.theme = 1;
entry.theme = LDoNThemes::GUK;
entry.zone_in_zone_id = 0;
entry.zone_in_x = 0;
entry.zone_in_y = 0;
@@ -717,7 +717,7 @@ public:
entry.itemclass = 0;
entry.itemtype = 0;
entry.ldonprice = 0;
entry.ldontheme = 0;
entry.ldontheme = LDoNThemes::Unused;
entry.ldonsold = 0;
entry.light = 0;
entry.lore = "";
@@ -0,0 +1,346 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
*/
#ifndef EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
#define EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
#include "../../database.h"
#include "../../string_util.h"
#include <ctime>
class BaseToolGameObjectsRepository {
public:
struct ToolGameObjects {
int id;
int zoneid;
std::string zonesn;
std::string object_name;
std::string file_from;
int is_global;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"zoneid",
"zonesn",
"object_name",
"file_from",
"is_global",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"zoneid",
"zonesn",
"object_name",
"file_from",
"is_global",
};
}
static std::string ColumnsRaw()
{
return std::string(implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("tool_game_objects");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static ToolGameObjects NewEntity()
{
ToolGameObjects entry{};
entry.id = 0;
entry.zoneid = 0;
entry.zonesn = "";
entry.object_name = "";
entry.file_from = "";
entry.is_global = 0;
return entry;
}
static ToolGameObjects GetToolGameObjectsEntry(
const std::vector<ToolGameObjects> &tool_game_objectss,
int tool_game_objects_id
)
{
for (auto &tool_game_objects : tool_game_objectss) {
if (tool_game_objects.id == tool_game_objects_id) {
return tool_game_objects;
}
}
return NewEntity();
}
static ToolGameObjects FindOne(
Database& db,
int tool_game_objects_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
BaseSelect(),
tool_game_objects_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
ToolGameObjects entry{};
entry.id = atoi(row[0]);
entry.zoneid = atoi(row[1]);
entry.zonesn = row[2] ? row[2] : "";
entry.object_name = row[3] ? row[3] : "";
entry.file_from = row[4] ? row[4] : "";
entry.is_global = atoi(row[5]);
return entry;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int tool_game_objects_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
tool_game_objects_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
ToolGameObjects tool_game_objects_entry
)
{
std::vector<std::string> update_values;
auto columns = Columns();
update_values.push_back(columns[1] + " = " + std::to_string(tool_game_objects_entry.zoneid));
update_values.push_back(columns[2] + " = '" + EscapeString(tool_game_objects_entry.zonesn) + "'");
update_values.push_back(columns[3] + " = '" + EscapeString(tool_game_objects_entry.object_name) + "'");
update_values.push_back(columns[4] + " = '" + EscapeString(tool_game_objects_entry.file_from) + "'");
update_values.push_back(columns[5] + " = " + std::to_string(tool_game_objects_entry.is_global));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
implode(", ", update_values),
PrimaryKey(),
tool_game_objects_entry.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static ToolGameObjects InsertOne(
Database& db,
ToolGameObjects tool_game_objects_entry
)
{
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(tool_game_objects_entry.id));
insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid));
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'");
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'");
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'");
insert_values.push_back(std::to_string(tool_game_objects_entry.is_global));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
implode(",", insert_values)
)
);
if (results.Success()) {
tool_game_objects_entry.id = results.LastInsertedID();
return tool_game_objects_entry;
}
tool_game_objects_entry = NewEntity();
return tool_game_objects_entry;
}
static int InsertMany(
Database& db,
std::vector<ToolGameObjects> tool_game_objects_entries
)
{
std::vector<std::string> insert_chunks;
for (auto &tool_game_objects_entry: tool_game_objects_entries) {
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(tool_game_objects_entry.id));
insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid));
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'");
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'");
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'");
insert_values.push_back(std::to_string(tool_game_objects_entry.is_global));
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
}
std::vector<std::string> insert_values;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<ToolGameObjects> All(Database& db)
{
std::vector<ToolGameObjects> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
ToolGameObjects entry{};
entry.id = atoi(row[0]);
entry.zoneid = atoi(row[1]);
entry.zonesn = row[2] ? row[2] : "";
entry.object_name = row[3] ? row[3] : "";
entry.file_from = row[4] ? row[4] : "";
entry.is_global = atoi(row[5]);
all_entries.push_back(entry);
}
return all_entries;
}
static std::vector<ToolGameObjects> GetWhere(Database& db, std::string where_filter)
{
std::vector<ToolGameObjects> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
ToolGameObjects entry{};
entry.id = atoi(row[0]);
entry.zoneid = atoi(row[1]);
entry.zonesn = row[2] ? row[2] : "";
entry.object_name = row[3] ? row[3] : "";
entry.file_from = row[4] ? row[4] : "";
entry.is_global = atoi(row[5]);
all_entries.push_back(entry);
}
return all_entries;
}
static int DeleteWhere(Database& db, std::string where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
@@ -110,6 +110,8 @@ public:
std::string content_flags;
std::string content_flags_disabled;
int underworld_teleport_index;
int lava_damage;
int min_lava_damage;
};
static std::string PrimaryKey()
@@ -212,6 +214,8 @@ public:
"content_flags",
"content_flags_disabled",
"underworld_teleport_index",
"lava_damage",
"min_lava_damage",
};
}
@@ -339,6 +343,8 @@ public:
entry.content_flags = "";
entry.content_flags_disabled = "";
entry.underworld_teleport_index = 0;
entry.lava_damage = 50;
entry.min_lava_damage = 10;
return entry;
}
@@ -466,6 +472,8 @@ public:
entry.content_flags = row[89] ? row[89] : "";
entry.content_flags_disabled = row[90] ? row[90] : "";
entry.underworld_teleport_index = atoi(row[91]);
entry.lava_damage = atoi(row[92]);
entry.min_lava_damage = atoi(row[93]);
return entry;
}
@@ -590,6 +598,8 @@ public:
update_values.push_back(columns[89] + " = '" + EscapeString(zone_entry.content_flags) + "'");
update_values.push_back(columns[90] + " = '" + EscapeString(zone_entry.content_flags_disabled) + "'");
update_values.push_back(columns[91] + " = " + std::to_string(zone_entry.underworld_teleport_index));
update_values.push_back(columns[92] + " = " + std::to_string(zone_entry.lava_damage));
update_values.push_back(columns[93] + " = " + std::to_string(zone_entry.min_lava_damage));
auto results = db.QueryDatabase(
fmt::format(
@@ -703,6 +713,8 @@ public:
insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'");
insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'");
insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index));
insert_values.push_back(std::to_string(zone_entry.lava_damage));
insert_values.push_back(std::to_string(zone_entry.min_lava_damage));
auto results = db.QueryDatabase(
fmt::format(
@@ -824,6 +836,8 @@ public:
insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'");
insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'");
insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index));
insert_values.push_back(std::to_string(zone_entry.lava_damage));
insert_values.push_back(std::to_string(zone_entry.min_lava_damage));
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
}
@@ -949,6 +963,8 @@ public:
entry.content_flags = row[89] ? row[89] : "";
entry.content_flags_disabled = row[90] ? row[90] : "";
entry.underworld_teleport_index = atoi(row[91]);
entry.lava_damage = atoi(row[92]);
entry.min_lava_damage = atoi(row[93]);
all_entries.push_back(entry);
}
@@ -1065,6 +1081,8 @@ public:
entry.content_flags = row[89] ? row[89] : "";
entry.content_flags_disabled = row[90] ? row[90] : "";
entry.underworld_teleport_index = atoi(row[91]);
entry.lava_damage = atoi(row[92]);
entry.min_lava_damage = atoi(row[93]);
all_entries.push_back(entry);
}
@@ -0,0 +1,70 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
#define EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
#include "../database.h"
#include "../string_util.h"
#include "base/base_tool_game_objects_repository.h"
class ToolGameObjectsRepository: public BaseToolGameObjectsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* ToolGameObjectsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* ToolGameObjectsRepository::GetWhereNeverExpires()
* ToolGameObjectsRepository::GetWhereXAndY()
* ToolGameObjectsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
+35 -2
View File
@@ -139,6 +139,7 @@ RULE_INT(Character, TradeskillUpMakePoison, 2, "Make Poison skillup rate adjustm
RULE_INT(Character, TradeskillUpPottery, 4, "Pottery skillup rate adjustment. Lower is faster")
RULE_INT(Character, TradeskillUpResearch, 1, "Research skillup rate adjustment. Lower is faster")
RULE_INT(Character, TradeskillUpTinkering, 2, "Tinkering skillup rate adjustment. Lower is faster")
RULE_INT(Character, TradeskillUpTailoring, 2, "Tailoring skillup rate adjustment. Lower is faster")
RULE_BOOL(Character, MarqueeHPUpdates, false, "Will show health percentage in center of screen if health lesser than 100%")
RULE_INT(Character, IksarCommonTongue, 95, "Starting value for Common Tongue for Iksars")
RULE_INT(Character, OgreCommonTongue, 95, "Starting value for Common Tongue for Ogres")
@@ -167,6 +168,12 @@ RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-base
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
RULE_BOOL(Character, SkillUpFromItems, true, "Allow Skill ups from clickable items")
RULE_BOOL(Character, EnableTestBuff, false, "Allow the use of /testbuff")
RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")
RULE_INT(Character, OldResurrectionSicknessSpellID, 757, "757 is Default Old Resurrection Sickness Spell ID")
RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrection Sickness Spell ID")
RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.")
RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.")
RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.")
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
@@ -181,6 +188,9 @@ RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a mer
RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse")
RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor")
RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
RULE_BOOL(Mercs, IsMercsElixirEnabled, false, "Override AI with elixir logic")
RULE_INT(Mercs, MercsElixirHealPercent, 90, "Heal allies at this percent health")
RULE_INT(Mercs, MercsElixirAEMinimum, 3, "AE Minimum to trigger AE spells (heals and nukes)")
RULE_CATEGORY_END()
RULE_CATEGORY(Guild)
@@ -319,7 +329,7 @@ RULE_INT(Spells, MaxDiscSlotsNPC, 0, "Maximum number of NPC disc slots. NPC don'
RULE_INT(Spells, MaxTotalSlotsNPC, 60, "Maximum total of NPC slots. The default value is the limit of the Titanium client")
RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default value is the limit of the Titanium client")
RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells")
RULE_INT(Spells, ReflectType, 3, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells")
RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells")
RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting")
RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim")
RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized")
@@ -380,6 +390,10 @@ RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts
RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible")
RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls")
RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.")
RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus")
RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells")
RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells")
RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.")
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)
@@ -391,7 +405,8 @@ RULE_INT(Combat, ArcheryCritDifficulty, 3400, "Value against which is rolled to
RULE_INT(Combat, ThrowingCritDifficulty, 1100, "Value against which is rolled to check if a throwing crit is triggered. Lower is easier")
RULE_BOOL(Combat, NPCCanCrit, false, "Setting whether an NPC can land critical hits")
RULE_BOOL(Combat, UseIntervalAC, true, "Switch whether bonuses, armour class, multipliers, classes and caps should be considered in the calculation of damage values")
RULE_INT(Combat, PetAttackMagicLevel, 30, "Level at which pets can cause magic damage")
RULE_INT(Combat, PetAttackMagicLevel, 10, "Level at which pets can cause magic damage, no longer used")
RULE_INT(Combat, NPCAttackMagicLevel, 10, "Level at which NPC and pets can cause magic damage")
RULE_BOOL(Combat, EnableFearPathing, true, "Setting whether to use pathing during fear")
RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used")
RULE_INT(Combat, FleeGrayHPRatio, 50, "HP percentage when a Gray NPC begins to flee")
@@ -477,6 +492,8 @@ RULE_BOOL(Combat, UseExtendedPoisonProcs, false, "Allow old school poisons to la
RULE_BOOL(Combat, EnableSneakPull, false, "Enable implementation of Sneak Pull")
RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak pull")
RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation")
RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@@ -508,6 +525,7 @@ RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate")
RULE_BOOL(NPC, UseMeditateBasedManaRegen, false, "Based NPC ooc regen on Meditate skill")
RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if enabled")
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
RULE_CATEGORY_END()
RULE_CATEGORY(Aggro)
@@ -580,6 +598,13 @@ RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel f
RULE_BOOL(Bots, AllowApplyPoisonCommand, true, "Allows the use of the bot command 'applypoison'")
RULE_BOOL(Bots, AllowApplyPotionCommand, true, "Allows the use of the bot command 'applypotion'")
RULE_BOOL(Bots, RestrictApplyPotionToRogue, true, "Restricts the bot command 'applypotion' to rogue-usable potions (i.e., poisons)")
RULE_BOOL(Bots, OldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore")
RULE_BOOL(Bots, ResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")
RULE_INT(Bots, OldResurrectionSicknessSpell, 757, "757 is Default Old Resurrection Sickness Spell")
RULE_INT(Bots, ResurrectionSicknessSpell, 756, "756 is Default Resurrection Sickness Spell")
RULE_BOOL(Bots, IsBotsElixirEnabled, false, "Override AI with elixir logic")
RULE_INT(Bots, BotsElixirHealPercent, 90, "Heal allies at this percent health")
RULE_INT(Bots, BotsElixirAEMinimum, 3, "AE Minimum to trigger AE spells (heals and nukes)")
RULE_CATEGORY_END()
#endif
@@ -598,6 +623,10 @@ RULE_INT(Chat, IntervalDurationMS, 60000, "Interval length in milliseconds")
RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000, "Karma update interval in milliseconds")
RULE_INT(Chat, KarmaGlobalChatLimit, 72, "Amount of karma you need to be able to talk in ooc/auction/chat below the level limit")
RULE_INT(Chat, GlobalChatLevelLimit, 8, "Level limit you need to of reached to talk in ooc/auction/chat if your karma is too low")
RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window")
RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute")
RULE_CATEGORY_END()
RULE_CATEGORY(Merchant)
@@ -779,6 +808,10 @@ RULE_BOOL(Cheat, EnableMQFastMemDetector, true, "Enable the MQFastMem Detector.
RULE_BOOL(Cheat, MarkMQWarpLT, false, "Mark clients makeing smaller warps")
RULE_CATEGORY_END()
RULE_CATEGORY(Command)
RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.")
RULE_CATEGORY_END()
#undef RULE_CATEGORY
#undef RULE_INT
#undef RULE_REAL
+205 -37
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
@@ -11,7 +11,7 @@
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -24,7 +24,10 @@
#include "item_instance.h"
#include "item_data.h"
#include "../zone/zonedb.h"
#include <algorithm>
// static bucket global
std::vector<SaylinkRepository::Saylink> g_cached_saylinks = {};
bool EQ::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body)
{
@@ -102,13 +105,13 @@ const std::string &EQ::SayLinkEngine::GenerateLink()
m_Link = "<LINKER ERROR>";
LogError("SayLinkEngine::GenerateLink() failed to generate a useable say link");
LogError(">> LinkType: {}, Lengths: [link: {}({}), body: {}({}), text: {}({})]",
m_LinkType,
m_Link.length(),
EQ::constants::SAY_LINK_MAXIMUM_SIZE,
m_LinkBody.length(),
EQ::constants::SAY_LINK_BODY_SIZE,
m_LinkText.length(),
EQ::constants::SAY_LINK_TEXT_SIZE
m_LinkType,
m_Link.length(),
EQ::constants::SAY_LINK_MAXIMUM_SIZE,
m_LinkBody.length(),
EQ::constants::SAY_LINK_BODY_SIZE,
m_LinkText.length(),
EQ::constants::SAY_LINK_TEXT_SIZE
);
LogError(">> LinkBody: {}", m_LinkBody.c_str());
LogError(">> LinkText: {}", m_LinkText.c_str());
@@ -295,33 +298,9 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo
{
uint32 saylink_id = 0;
/**
* Query for an existing phrase and id in the saylink table
*/
std::string query = StringFormat(
"SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1",
EscapeString(saylink_text).c_str());
auto results = database.QueryDatabase(query);
if (results.Success()) {
if (results.RowCount() >= 1) {
for (auto row = results.begin(); row != results.end(); ++row)
saylink_id = static_cast<uint32>(atoi(row[0]));
}
else {
std::string insert_query = StringFormat(
"INSERT INTO `saylink` (`phrase`) VALUES ('%s')",
EscapeString(saylink_text).c_str());
results = database.QueryDatabase(insert_query);
if (!results.Success()) {
LogError("Error in saylink phrase queries {}", results.ErrorMessage().c_str());
}
else {
saylink_id = results.LastInsertedID();
}
}
SaylinkRepository::Saylink saylink = GetOrSaveSaylink(saylink_text);
if (saylink.id > 0) {
saylink_id = saylink.id;
}
/**
@@ -339,4 +318,193 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo
linker.SetProxyText(link_name.c_str());
return linker.GenerateLink();
}
}
std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
{
std::string new_message = message;
int link_index = 0;
int saylink_index = 0;
std::vector<std::string> links = {};
std::vector<std::string> saylinks = {};
int saylink_length = 50;
std::string saylink_separator = "\u0012";
std::string saylink_partial = "00000";
LogSaylinkDetail("new_message pre pass 1 [{}]", new_message);
// first pass - strip existing saylinks by putting placeholder anchors on them
for (auto &saylink: split_string(new_message, saylink_separator)) {
if (!saylink.empty() && saylink.length() > saylink_length &&
saylink.find(saylink_partial) != std::string::npos) {
saylinks.emplace_back(saylink);
LogSaylinkDetail("Found saylink [{}]", saylink);
// replace with anchor
find_replace(
new_message,
fmt::format("{}", saylink),
fmt::format("<saylink:{}>", saylink_index)
);
saylink_index++;
}
}
LogSaylinkDetail("new_message post pass 1 [{}]", new_message);
LogSaylinkDetail("saylink separator count [{}]", std::count(new_message.begin(), new_message.end(), '\u0012'));
// loop through brackets until none exist
if (new_message.find('[') != std::string::npos) {
for (auto &b: split_string(new_message, "[")) {
if (!b.empty() && b.find(']') != std::string::npos) {
std::vector<std::string> right_split = split_string(b, "]");
if (!right_split.empty()) {
std::string bracket_message = trim(right_split[0]);
// we shouldn't see a saylink fragment here, ignore this bracket
if (bracket_message.find(saylink_partial) != std::string::npos) {
continue;
}
// skip where multiple saylinks are within brackets
if (bracket_message.find(saylink_separator) != std::string::npos &&
std::count(bracket_message.begin(), bracket_message.end(), '\u0012') > 1) {
continue;
}
// if non empty bracket contents
if (!bracket_message.empty()) {
LogSaylinkDetail("Found bracket_message [{}]", bracket_message);
// already a saylink
// todo: improve this later
if (!bracket_message.empty() &&
(bracket_message.length() > saylink_length ||
bracket_message.find(saylink_separator) != std::string::npos)) {
links.emplace_back(bracket_message);
}
else {
links.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
bracket_message,
false,
bracket_message
)
);
}
// replace with anchor
find_replace(
new_message,
fmt::format("[{}]", bracket_message),
fmt::format("<prelink:{}>", link_index)
);
link_index++;
}
}
}
}
}
LogSaylinkDetail("new_message post pass 2 (post brackets) [{}]", new_message);
// strip any current delimiters of saylinks
find_replace(new_message, saylink_separator, "");
// pop links onto anchors
link_index = 0;
for (auto &link: links) {
// strip any current delimiters of saylinks
find_replace(link, saylink_separator, "");
find_replace(
new_message,
fmt::format("<prelink:{}>", link_index),
fmt::format("[\u0012{}\u0012]", link)
);
link_index++;
}
LogSaylinkDetail("new_message post pass 3 (post prelink anchor pop) [{}]", new_message);
// pop links onto anchors
saylink_index = 0;
for (auto &link: saylinks) {
// strip any current delimiters of saylinks
find_replace(link, saylink_separator, "");
// check to see if we did a double anchor pass (existing saylink that was also inside brackets)
// this means we found a saylink and we're checking to see if we're already encoded before double encoding
if (new_message.find(fmt::format("\u0012<saylink:{}>\u0012", saylink_index)) != std::string::npos) {
LogSaylinkDetail("Found encoded saylink at index [{}]", saylink_index);
find_replace(
new_message,
fmt::format("\u0012<saylink:{}>\u0012", saylink_index),
fmt::format("\u0012{}\u0012", link)
);
saylink_index++;
continue;
}
find_replace(
new_message,
fmt::format("<saylink:{}>", saylink_index),
fmt::format("\u0012{}\u0012", link)
);
saylink_index++;
}
LogSaylinkDetail("new_message post pass 4 (post saylink anchor pop) [{}]", new_message);
return new_message;
}
void EQ::SayLinkEngine::LoadCachedSaylinks()
{
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not like '%#%'");
LogSaylink("Loaded [{}] saylinks into cache", saylinks.size());
g_cached_saylinks = saylinks;
}
SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string saylink_text)
{
// return cached saylink if exist
if (!g_cached_saylinks.empty()) {
for (auto &s: g_cached_saylinks) {
if (s.phrase == saylink_text) {
return s;
}
}
}
auto saylinks = SaylinkRepository::GetWhere(
database,
fmt::format("phrase = '{}'", EscapeString(saylink_text))
);
// return if found from the database
if (!saylinks.empty()) {
return saylinks[0];
}
// if not found in database - save
if (saylinks.empty()) {
auto new_saylink = SaylinkRepository::NewEntity();
new_saylink.phrase = saylink_text;
// persist to database
auto link = SaylinkRepository::InsertOne(database, new_saylink);
if (link.id > 0) {
g_cached_saylinks.emplace_back(link);
return link;
}
}
return {};
}
+7 -4
View File
@@ -1,17 +1,17 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -23,7 +23,7 @@
#include "types.h"
#include <string>
#include "repositories/saylink_repository.h"
struct ServerLootItem_Struct;
@@ -105,6 +105,8 @@ namespace EQ
void Reset();
static std::string InjectSaylinksIfNotExist(const char *message);
static void LoadCachedSaylinks();
private:
void generate_body();
void generate_text();
@@ -120,6 +122,7 @@ namespace EQ
std::string m_LinkBody;
std::string m_LinkText;
bool m_Error;
static SaylinkRepository::Saylink GetOrSaveSaylink(std::string saylink_text);
};
} /*EQEmu*/
+188 -594
View File
@@ -227,90 +227,26 @@
#define ServerOP_HotReloadQuests 0x4011
#define ServerOP_UpdateSchedulerEvents 0x4012
#define ServerOP_CZCastSpellPlayer 0x4500
#define ServerOP_CZCastSpellGroup 0x4501
#define ServerOP_CZCastSpellRaid 0x4502
#define ServerOP_CZCastSpellGuild 0x4503
#define ServerOP_CZMarqueePlayer 0x4504
#define ServerOP_CZMarqueeGroup 0x4505
#define ServerOP_CZMarqueeRaid 0x4506
#define ServerOP_CZMarqueeGuild 0x4507
#define ServerOP_CZMessagePlayer 0x4508
#define ServerOP_CZMessageGroup 0x4509
#define ServerOP_CZMessageRaid 0x4510
#define ServerOP_CZMessageGuild 0x4511
#define ServerOP_CZMovePlayer 0x4512
#define ServerOP_CZMoveGroup 0x4513
#define ServerOP_CZMoveRaid 0x4514
#define ServerOP_CZMoveGuild 0x4515
#define ServerOP_CZMoveInstancePlayer 0x4516
#define ServerOP_CZMoveInstanceGroup 0x4517
#define ServerOP_CZMoveInstanceRaid 0x4518
#define ServerOP_CZMoveInstanceGuild 0x4519
#define ServerOP_CZRemoveSpellPlayer 0x4520
#define ServerOP_CZRemoveSpellGroup 0x4521
#define ServerOP_CZRemoveSpellRaid 0x4522
#define ServerOP_CZRemoveSpellGuild 0x4523
#define ServerOP_CZSetEntityVariableByClientName 0x4524
#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4525
#define ServerOP_CZSetEntityVariableByGroupID 0x4526
#define ServerOP_CZSetEntityVariableByRaidID 0x4527
#define ServerOP_CZSetEntityVariableByGuildID 0x4528
#define ServerOP_CZSignalClient 0x4529
#define ServerOP_CZSignalClientByName 0x4530
#define ServerOP_CZSignalNPC 0x4531
#define ServerOP_CZSignalGroup 0x4532
#define ServerOP_CZSignalRaid 0x4533
#define ServerOP_CZSignalGuild 0x4534
#define ServerOP_CZTaskActivityResetPlayer 0x4535
#define ServerOP_CZTaskActivityResetGroup 0x4536
#define ServerOP_CZTaskActivityResetRaid 0x4537
#define ServerOP_CZTaskActivityResetGuild 0x4538
#define ServerOP_CZTaskActivityUpdatePlayer 0x4539
#define ServerOP_CZTaskActivityUpdateGroup 0x4540
#define ServerOP_CZTaskActivityUpdateRaid 0x4541
#define ServerOP_CZTaskActivityUpdateGuild 0x4542
#define ServerOP_CZTaskAssignPlayer 0x4543
#define ServerOP_CZTaskAssignGroup 0x4544
#define ServerOP_CZTaskAssignRaid 0x4545
#define ServerOP_CZTaskAssignGuild 0x4546
#define ServerOP_CZTaskDisablePlayer 0x4547
#define ServerOP_CZTaskDisableGroup 0x4548
#define ServerOP_CZTaskDisableRaid 0x4549
#define ServerOP_CZTaskDisableGuild 0x4550
#define ServerOP_CZTaskEnablePlayer 0x4551
#define ServerOP_CZTaskEnableGroup 0x4552
#define ServerOP_CZTaskEnableRaid 0x4553
#define ServerOP_CZTaskEnableGuild 0x4554
#define ServerOP_CZTaskFailPlayer 0x4555
#define ServerOP_CZTaskFailGroup 0x4556
#define ServerOP_CZTaskFailRaid 0x4557
#define ServerOP_CZTaskFailGuild 0x4558
#define ServerOP_CZTaskRemovePlayer 0x4559
#define ServerOP_CZTaskRemoveGroup 0x4560
#define ServerOP_CZTaskRemoveRaid 0x4561
#define ServerOP_CZTaskRemoveGuild 0x4562
#define ServerOP_CZClientMessageString 0x4563
#define ServerOP_CZLDoNUpdate 0x4564
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501
#define ServerOP_CZMarquee 0x4502
#define ServerOP_CZMessage 0x4503
#define ServerOP_CZMove 0x4504
#define ServerOP_CZSetEntityVariable 0x4505
#define ServerOP_CZSignal 0x4506
#define ServerOP_CZSpell 0x4507
#define ServerOP_CZTaskUpdate 0x4508
#define ServerOP_CZClientMessageString 0x4509
#define ServerOP_WWAssignTask 0x4750
#define ServerOP_WWCastSpell 0x4751
#define ServerOP_WWCompleteActivity 0x4752
#define ServerOP_WWDisableTask 0x4753
#define ServerOP_WWEnableTask 0x4754
#define ServerOP_WWFailTask 0x4755
#define ServerOP_WWMarquee 0x4756
#define ServerOP_WWMessage 0x4757
#define ServerOP_WWMove 0x4758
#define ServerOP_WWMoveInstance 0x4759
#define ServerOP_WWRemoveSpell 0x4760
#define ServerOP_WWRemoveTask 0x4761
#define ServerOP_WWResetActivity 0x4762
#define ServerOP_WWSetEntityVariableClient 0x4763
#define ServerOP_WWSetEntityVariableNPC 0x4764
#define ServerOP_WWSignalClient 0x4765
#define ServerOP_WWSignalNPC 0x4766
#define ServerOP_WWUpdateActivity 0x4767
#define ServerOP_WWDialogueWindow 0x4750
#define ServerOP_WWLDoNUpdate 0x4751
#define ServerOP_WWMarquee 0x4752
#define ServerOP_WWMessage 0x4753
#define ServerOP_WWMove 0x4754
#define ServerOP_WWSetEntityVariable 0x4755
#define ServerOP_WWSignal 0x4756
#define ServerOP_WWSpell 0x4757
#define ServerOP_WWTaskUpdate 0x4758
/**
* QueryServer
@@ -325,17 +261,79 @@
#define ServerOP_QSPlayerDropItem 0x5007
enum {
CZLDoNUpdateType_Character = 0,
CZLDoNUpdateType_Group,
CZLDoNUpdateType_Raid,
CZLDoNUpdateType_Guild,
CZLDoNUpdateType_Expedition
CZUpdateType_Character,
CZUpdateType_Group,
CZUpdateType_Raid,
CZUpdateType_Guild,
CZUpdateType_Expedition,
CZUpdateType_ClientName,
CZUpdateType_NPC
};
enum {
CZLDoNUpdateSubtype_Win = 0,
CZLDoNUpdateSubtype_Loss,
CZLDoNUpdateSubtype_Points
CZLDoNUpdateSubtype_AddLoss,
CZLDoNUpdateSubtype_AddPoints,
CZLDoNUpdateSubtype_AddWin,
CZLDoNUpdateSubtype_RemoveLoss,
CZLDoNUpdateSubtype_RemoveWin,
};
enum {
CZMoveUpdateSubtype_MoveZone,
CZMoveUpdateSubtype_MoveZoneInstance
};
enum {
CZSpellUpdateSubtype_Cast,
CZSpellUpdateSubtype_Remove
};
enum {
CZTaskUpdateSubtype_ActivityReset,
CZTaskUpdateSubtype_ActivityUpdate,
CZTaskUpdateSubtype_AssignTask,
CZTaskUpdateSubtype_DisableTask,
CZTaskUpdateSubtype_EnableTask,
CZTaskUpdateSubtype_FailTask,
CZTaskUpdateSubtype_RemoveTask
};
enum {
WWLDoNUpdateType_AddLoss,
WWLDoNUpdateType_AddPoints,
WWLDoNUpdateType_AddWin,
WWLDoNUpdateType_RemoveLoss,
WWLDoNUpdateType_RemoveWin
};
enum {
WWMoveUpdateType_MoveZone,
WWMoveUpdateType_MoveZoneInstance
};
enum {
WWSetEntityVariableUpdateType_Character,
WWSetEntityVariableUpdateType_NPC
};
enum {
WWSignalUpdateType_Character,
WWSignalUpdateType_NPC
};
enum {
WWSpellUpdateType_Cast,
WWSpellUpdateType_Remove
};
enum {
WWTaskUpdateType_ActivityReset,
WWTaskUpdateType_ActivityUpdate,
WWTaskUpdateType_AssignTask,
WWTaskUpdateType_DisableTask,
WWTaskUpdateType_EnableTask,
WWTaskUpdateType_FailTask,
WWTaskUpdateType_RemoveTask
};
/* Query Serv Generic Packet Flag/Type Enumeration */
@@ -1440,489 +1438,107 @@ struct QSGeneralQuery_Struct {
char QueryString[0];
};
struct CZCastSpellPlayer_Struct {
int character_id;
uint32 spell_id;
};
struct CZCastSpellGroup_Struct {
int group_id;
uint32 spell_id;
};
struct CZCastSpellRaid_Struct {
int raid_id;
uint32 spell_id;
};
struct CZCastSpellGuild_Struct {
int guild_id;
uint32 spell_id;
};
struct CZClientSignal_Struct {
int character_id;
uint32 signal;
};
struct CZGroupSignal_Struct {
int group_id;
uint32 signal;
};
struct CZRaidSignal_Struct {
int raid_id;
uint32 signal;
};
struct CZGuildSignal_Struct {
int guild_id;
uint32 signal;
};
struct CZNPCSignal_Struct {
uint32 npctype_id;
uint32 signal;
};
struct CZClientMessageString_Struct {
uint32 string_id;
uint16 chat_type;
char character_name[64];
char client_name[64];
uint32 args_size;
char args[1]; // null delimited
};
struct CZClientSignalByName_Struct {
char character_name[64];
uint32 signal;
};
struct CZCompleteActivityPlayer_Struct {
int character_id;
uint32 task_id;
int activity_id;
};
struct CZCompleteActivityGroup_Struct {
int group_id;
uint32 task_id;
int activity_id;
};
struct CZCompleteActivityRaid_Struct {
int raid_id;
uint32 task_id;
int activity_id;
};
struct CZCompleteActivityGuild_Struct {
int guild_id;
uint32 task_id;
int activity_id;
};
struct CZMovePlayer_Struct {
int character_id;
char zone_short_name[32];
};
struct CZMarqueePlayer_Struct {
int character_id;
uint32 type;
uint32 priority;
uint32 fade_in;
uint32 fade_out;
uint32 duration;
char message[512];
};
struct CZMarqueeGroup_Struct {
int group_id;
uint32 type;
uint32 priority;
uint32 fade_in;
uint32 fade_out;
uint32 duration;
char message[512];
};
struct CZMarqueeRaid_Struct {
int raid_id;
uint32 type;
uint32 priority;
uint32 fade_in;
uint32 fade_out;
uint32 duration;
char message[512];
};
struct CZMarqueeGuild_Struct {
int guild_id;
uint32 type;
uint32 priority;
uint32 fade_in;
uint32 fade_out;
uint32 duration;
char message[512];
};
struct CZMessagePlayer_Struct {
uint32 type;
char character_name[64];
char message[512];
};
struct CZMessageGroup_Struct {
uint32 type;
int group_id;
char message[512];
};
struct CZMessageRaid_Struct {
uint32 type;
int raid_id;
char message[512];
};
struct CZMessageGuild_Struct {
uint32 type;
int guild_id;
char message[512];
};
struct CZMoveGroup_Struct {
int group_id;
char zone_short_name[32];
};
struct CZMoveRaid_Struct {
int raid_id;
char zone_short_name[32];
};
struct CZMoveGuild_Struct {
int guild_id;
char zone_short_name[32];
};
struct CZMoveInstancePlayer_Struct {
int character_id;
uint16 instance_id;
};
struct CZMoveInstanceGroup_Struct {
int group_id;
uint16 instance_id;
};
struct CZMoveInstanceRaid_Struct {
int raid_id;
uint16 instance_id;
};
struct CZMoveInstanceGuild_Struct {
int guild_id;
uint16 instance_id;
};
struct CZRemoveSpellPlayer_Struct {
int character_id;
uint32 spell_id;
};
struct CZRemoveSpellGroup_Struct {
int group_id;
uint32 spell_id;
};
struct CZRemoveSpellRaid_Struct {
int raid_id;
uint32 spell_id;
};
struct CZRemoveSpellGuild_Struct {
int guild_id;
uint32 spell_id;
};
struct CZRemoveTaskPlayer_Struct {
int character_id;
uint32 task_id;
};
struct CZRemoveTaskGroup_Struct {
int group_id;
uint32 task_id;
};
struct CZRemoveTaskRaid_Struct {
int raid_id;
uint32 task_id;
};
struct CZRemoveTaskGuild_Struct {
int guild_id;
uint32 task_id;
};
struct CZResetActivityPlayer_Struct {
int character_id;
uint32 task_id;
int activity_id;
};
struct CZResetActivityGroup_Struct {
int group_id;
uint32 task_id;
int activity_id;
};
struct CZResetActivityRaid_Struct {
int raid_id;
uint32 task_id;
int activity_id;
};
struct CZResetActivityGuild_Struct {
int guild_id;
uint32 task_id;
int activity_id;
};
struct CZSetEntVarByNPCTypeID_Struct {
uint32 npctype_id;
char variable_name[256];
char variable_value[256];
};
struct CZSetEntVarByClientName_Struct {
char character_name[64];
char variable_name[256];
char variable_value[256];
};
struct CZSetEntVarByGroupID_Struct {
int group_id;
char variable_name[256];
char variable_value[256];
};
struct CZSetEntVarByRaidID_Struct {
int raid_id;
char variable_name[256];
char variable_value[256];
};
struct CZSetEntVarByGuildID_Struct {
int guild_id;
char variable_name[256];
char variable_value[256];
};
struct CZTaskActivityResetPlayer_Struct {
int character_id;
uint32 task_id;
int activity_id;
};
struct CZTaskActivityResetGroup_Struct {
int group_id;
uint32 task_id;
int activity_id;
};
struct CZTaskActivityResetRaid_Struct {
int raid_id;
uint32 task_id;
int activity_id;
};
struct CZTaskActivityResetGuild_Struct {
int guild_id;
uint32 task_id;
int activity_id;
};
struct CZTaskActivityUpdatePlayer_Struct {
int character_id;
uint32 task_id;
int activity_id;
int activity_count;
};
struct CZTaskActivityUpdateGroup_Struct {
int group_id;
uint32 task_id;
int activity_id;
int activity_count;
};
struct CZTaskActivityUpdateRaid_Struct {
int raid_id;
uint32 task_id;
int activity_id;
int activity_count;
};
struct CZTaskActivityUpdateGuild_Struct {
int guild_id;
uint32 task_id;
int activity_id;
int activity_count;
};
struct CZTaskAssignPlayer_Struct {
uint16 npc_entity_id;
int character_id;
uint32 task_id;
bool enforce_level_requirement;
};
struct CZTaskAssignGroup_Struct {
uint16 npc_entity_id;
int group_id;
uint32 task_id;
bool enforce_level_requirement;
};
struct CZTaskAssignRaid_Struct {
uint16 npc_entity_id;
int raid_id;
uint32 task_id;
bool enforce_level_requirement;
};
struct CZTaskAssignGuild_Struct {
uint16 npc_entity_id;
int guild_id;
uint32 task_id;
bool enforce_level_requirement;
};
struct CZTaskDisablePlayer_Struct {
int character_id;
uint32 task_id;
};
struct CZTaskDisableGroup_Struct {
int group_id;
uint32 task_id;
};
struct CZTaskDisableRaid_Struct {
int raid_id;
uint32 task_id;
};
struct CZTaskDisableGuild_Struct {
int guild_id;
uint32 task_id;
};
struct CZTaskEnablePlayer_Struct {
int character_id;
uint32 task_id;
};
struct CZTaskEnableGroup_Struct {
int group_id;
uint32 task_id;
};
struct CZTaskEnableRaid_Struct {
int raid_id;
uint32 task_id;
};
struct CZTaskEnableGuild_Struct {
int guild_id;
uint32 task_id;
};
struct CZTaskFailPlayer_Struct {
int character_id;
uint32 task_id;
};
struct CZTaskFailGroup_Struct {
int group_id;
uint32 task_id;
};
struct CZTaskFailRaid_Struct {
int raid_id;
uint32 task_id;
};
struct CZTaskFailGuild_Struct {
int guild_id;
uint32 task_id;
};
struct CZTaskRemovePlayer_Struct {
uint16 npc_entity_id;
int character_id;
uint32 task_id;
};
struct CZTaskRemoveGroup_Struct {
uint16 npc_entity_id;
int group_id;
uint32 task_id;
};
struct CZTaskRemoveRaid_Struct {
uint16 npc_entity_id;
int raid_id;
uint32 task_id;
};
struct CZTaskRemoveGuild_Struct {
uint16 npc_entity_id;
int guild_id;
uint32 task_id;
struct CZDialogueWindow_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
char message[4096];
char client_name[64]; // Only used by Character Name Type, else empty
};
struct CZLDoNUpdate_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition
uint8 update_subtype; // 0 - Win, 1 - Loss, 2 - Points
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
uint8 update_subtype; // 0 - Loss, 1 - Points, 2 - Win
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint32 theme_id;
int points; // Always 1, except for when Points are used
int points; // Only used in Points Subtype, else 1
char client_name[64]; // Only used by Character Name Type, else empty
};
struct WWAssignTask_Struct {
uint16 npc_entity_id;
uint32 task_id;
bool enforce_level_requirement;
uint8 min_status;
uint8 max_status;
struct CZMarquee_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint32 type;
uint32 priority;
uint32 fade_in;
uint32 fade_out;
uint32 duration;
char message[512];
char client_name[64]; // Only used by Character Name Type, else empty
};
struct WWCastSpell_Struct {
struct CZMessage_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
int update_identifier; // Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint32 type;
char message[512];
char client_name[64]; // Only used by Character Name Type, else empty
};
struct CZMove_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
uint8 update_subtype; // 0 - Move Zone, 1 - Move Zone Instance
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint16 instance_id; // Only used by Move Zone Instance, else 0
char zone_short_name[32]; // Only by with Move Zone, else empty
char client_name[64]; // Only used by Character Name Type, else empty
};
struct CZSetEntityVariable_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC
int update_identifier; // Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name
char variable_name[256];
char variable_value[256];
char client_name[64]; // Only used by Character Type, else empty
};
struct CZSignal_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name
uint32 signal;
char client_name[64]; // Only used by Character Name Type, else empty
};
struct CZSpell_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
uint8 update_subtype; // 0 - Cast Spell, 1 - Remove Spell
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint32 spell_id;
char client_name[64]; // Only used by Character Name Type, else empty
};
struct CZTaskUpdate_Struct {
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
uint8 update_subtype; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
uint32 task_identifier;
int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1
int update_count; // Only used by Activity Update, else 1
bool enforce_level_requirement; // Only used by Assign Task
char client_name[64]; // Only used by Character Name Type, else empty
};
struct WWDialogueWindow_Struct {
char message[4096];
uint8 min_status;
uint8 max_status;
};
struct WWDisableTask_Struct {
uint32 task_id;
struct WWLDoNUpdate_Struct {
uint8 update_type; // 0 - Loss, 1 - Points, 2 - Win
uint32 theme_id;
int points; // Only used in Points Subtype, else 1
uint8 min_status;
uint8 max_status;
};
struct WWEnableTask_Struct {
uint32 task_id;
uint8 min_status;
uint8 max_status;
};
struct WWFailTask_Struct {
uint32 task_id;
uint8 min_status;
uint8 max_status;
};
struct WWMarquee_Struct {
uint32 type;
uint32 priority;
@@ -1942,63 +1558,41 @@ struct WWMessage_Struct {
};
struct WWMove_Struct {
char zone_short_name[32];
uint8 update_type; // 0 - Move Zone, 1 - Move Zone Instance
char zone_short_name[32]; // Used with Move Zone
uint16 instance_id; // Used with Move Zone Instance
uint8 min_status;
uint8 max_status;
};
struct WWMoveInstance_Struct {
uint16 instance_id;
struct WWSetEntityVariable_Struct {
uint8 update_type; // 0 - Character, 1 - NPC
char variable_name[256];
char variable_value[256];
uint8 min_status;
uint8 max_status;
};
struct WWRemoveSpell_Struct {
struct WWSignal_Struct {
uint8 update_type; // 0 - Character, 1 - NPC
uint32 signal;
uint8 min_status;
uint8 max_status;
};
struct WWSpell_Struct {
uint8 update_type; // 0 - Cast Spell, 1 - Remove Spell
uint32 spell_id;
uint8 min_status;
uint8 max_status;
};
struct WWRemoveTask_Struct {
uint32 task_id;
uint8 min_status;
uint8 max_status;
};
struct WWResetActivity_Struct {
uint32 task_id;
int activity_id;
uint8 min_status;
uint8 max_status;
};
struct WWSetEntVarClient_Struct {
char variable_name[256];
char variable_value[256];
uint8 min_status;
uint8 max_status;
};
struct WWSetEntVarNPC_Struct {
char variable_name[256];
char variable_value[256];
};
struct WWSignalClient_Struct {
uint32 signal;
uint8 min_status;
uint8 max_status;
};
struct WWSignalNPC_Struct {
uint32 signal;
};
struct WWUpdateActivity_Struct {
uint32 task_id;
int activity_id;
int activity_count;
struct WWTaskUpdate_Struct {
uint8 update_type; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task
uint32 task_identifier;
int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1
int update_count; // Update Count for Activity Update, else 1
bool enforce_level_requirement; // Only used by Assign Task, else false
uint8 min_status;
uint8 max_status;
};
+59 -58
View File
@@ -1118,7 +1118,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
item.Haste = (uint32)atoul(row[ItemField::haste]);
item.DamageShield = (uint32)atoul(row[ItemField::damageshield]);
item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]);
item.RecastType = (uint32)atoul(row[ItemField::recasttype]);
item.RecastType = (int)atoi(row[ItemField::recasttype]);
item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]);
item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]);
item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true;
@@ -1682,7 +1682,7 @@ void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpe
for(auto row = results.begin(); row != results.end(); ++row) {
int spellID = atoi(row[0]);
if((spellID > 0) && (spellID <= iMaxSpellID))
sp[spellID].DamageShieldType = atoi(row[1]);
sp[spellID].damage_shield_type = atoi(row[1]);
}
}
@@ -1762,48 +1762,48 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades));
sp[tempid].range=static_cast<float>(atof(row[9]));
sp[tempid].aoerange=static_cast<float>(atof(row[10]));
sp[tempid].pushback=static_cast<float>(atof(row[11]));
sp[tempid].pushup=static_cast<float>(atof(row[12]));
sp[tempid].aoe_range=static_cast<float>(atof(row[10]));
sp[tempid].push_back=static_cast<float>(atof(row[11]));
sp[tempid].push_up=static_cast<float>(atof(row[12]));
sp[tempid].cast_time=atoi(row[13]);
sp[tempid].recovery_time=atoi(row[14]);
sp[tempid].recast_time=atoi(row[15]);
sp[tempid].buffdurationformula=atoi(row[16]);
sp[tempid].buffduration=atoi(row[17]);
sp[tempid].AEDuration=atoi(row[18]);
sp[tempid].buff_duration_formula=atoi(row[16]);
sp[tempid].buff_duration=atoi(row[17]);
sp[tempid].aoe_duration=atoi(row[18]);
sp[tempid].mana=atoi(row[19]);
int y=0;
for(y=0; y< EFFECT_COUNT;y++)
sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value
sp[tempid].base_value[y]=atoi(row[20+y]); // effect_base_value
for(y=0; y < EFFECT_COUNT; y++)
sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value
sp[tempid].limit_value[y]=atoi(row[32+y]); // effect_limit_value
for(y=0; y< EFFECT_COUNT;y++)
sp[tempid].max[y]=atoi(row[44+y]);
sp[tempid].max_value[y]=atoi(row[44+y]);
for(y=0; y< 4;y++)
sp[tempid].components[y]=atoi(row[58+y]);
sp[tempid].component[y]=atoi(row[58+y]);
for(y=0; y< 4;y++)
sp[tempid].component_counts[y]=atoi(row[62+y]);
sp[tempid].component_count[y]=atoi(row[62+y]);
for(y=0; y< 4;y++)
sp[tempid].NoexpendReagent[y]=atoi(row[66+y]);
sp[tempid].no_expend_reagent[y]=atoi(row[66+y]);
for(y=0; y< EFFECT_COUNT;y++)
sp[tempid].formula[y]=atoi(row[70+y]);
sp[tempid].goodEffect=atoi(row[83]);
sp[tempid].Activated=atoi(row[84]);
sp[tempid].resisttype=atoi(row[85]);
sp[tempid].good_effect=atoi(row[83]);
sp[tempid].activated=atoi(row[84]);
sp[tempid].resist_type=atoi(row[85]);
for(y=0; y< EFFECT_COUNT;y++)
sp[tempid].effectid[y]=atoi(row[86+y]);
sp[tempid].effect_id[y]=atoi(row[86+y]);
sp[tempid].targettype = (SpellTargetType) atoi(row[98]);
sp[tempid].basediff=atoi(row[99]);
sp[tempid].target_type = (SpellTargetType) atoi(row[98]);
sp[tempid].base_difficulty=atoi(row[99]);
int tmp_skill = atoi(row[100]);;
@@ -1812,15 +1812,15 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
else
sp[tempid].skill = (EQ::skills::SkillType) tmp_skill;
sp[tempid].zonetype=atoi(row[101]);
sp[tempid].EnvironmentType=atoi(row[102]);
sp[tempid].TimeOfDay=atoi(row[103]);
sp[tempid].zone_type=atoi(row[101]);
sp[tempid].environment_type=atoi(row[102]);
sp[tempid].time_of_day=atoi(row[103]);
for(y=0; y < PLAYER_CLASS_COUNT;y++)
sp[tempid].classes[y]=atoi(row[104+y]);
sp[tempid].CastingAnim=atoi(row[120]);
sp[tempid].SpellAffectIndex=atoi(row[123]);
sp[tempid].casting_animation=atoi(row[120]);
sp[tempid].spell_affect_index=atoi(row[123]);
sp[tempid].disallow_sit=atoi(row[124]);
sp[tempid].deity_agnostic=atoi(row[125]);
@@ -1829,31 +1829,32 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].new_icon=atoi(row[144]);
sp[tempid].uninterruptable=atoi(row[146]) != 0;
sp[tempid].ResistDiff=atoi(row[147]);
sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0;
sp[tempid].RecourseLink = atoi(row[150]);
sp[tempid].resist_difficulty=atoi(row[147]);
sp[tempid].unstackable_dot = atoi(row[148]) != 0;
sp[tempid].recourse_link = atoi(row[150]);
sp[tempid].no_partial_resist = atoi(row[151]) != 0;
sp[tempid].short_buff_box = atoi(row[154]);
sp[tempid].descnum = atoi(row[155]);
sp[tempid].typedescnum = atoi(row[156]);
sp[tempid].effectdescnum = atoi(row[157]);
sp[tempid].description_id = atoi(row[155]);
sp[tempid].type_description_id = atoi(row[156]);
sp[tempid].effect_description_id = atoi(row[157]);
sp[tempid].npc_no_los = atoi(row[159]) != 0;
sp[tempid].feedbackable = atoi(row[160]) != 0;
sp[tempid].reflectable = atoi(row[161]) != 0;
sp[tempid].bonushate=atoi(row[162]);
sp[tempid].bonus_hate=atoi(row[162]);
sp[tempid].ldon_trap = atoi(row[165]) != 0;
sp[tempid].EndurCost=atoi(row[166]);
sp[tempid].EndurTimerIndex=atoi(row[167]);
sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0;
sp[tempid].HateAdded=atoi(row[173]);
sp[tempid].EndurUpkeep=atoi(row[174]);
sp[tempid].numhitstype = atoi(row[175]);
sp[tempid].numhits = atoi(row[176]);
sp[tempid].pvpresistbase=atoi(row[177]);
sp[tempid].pvpresistcalc=atoi(row[178]);
sp[tempid].pvpresistcap=atoi(row[179]);
sp[tempid].endurance_cost=atoi(row[166]);
sp[tempid].timer_id=atoi(row[167]);
sp[tempid].is_discipline = atoi(row[168]) != 0;
sp[tempid].hate_added=atoi(row[173]);
sp[tempid].endurance_upkeep=atoi(row[174]);
sp[tempid].hit_number_type = atoi(row[175]);
sp[tempid].hit_number = atoi(row[176]);
sp[tempid].pvp_resist_base=atoi(row[177]);
sp[tempid].pvp_resist_per_level=atoi(row[178]);
sp[tempid].pvp_resist_cap=atoi(row[179]);
sp[tempid].spell_category=atoi(row[180]);
sp[tempid].pvp_duration = atoi(row[181]);
sp[tempid].pvp_duration_cap = atoi(row[182]);
@@ -1861,11 +1862,11 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].cast_not_standing = atoi(row[184]) != 0;
sp[tempid].can_mgb=atoi(row[185]);
sp[tempid].dispel_flag = atoi(row[186]);
sp[tempid].MinResist = atoi(row[189]);
sp[tempid].MaxResist = atoi(row[190]);
sp[tempid].min_resist = atoi(row[189]);
sp[tempid].max_resist = atoi(row[190]);
sp[tempid].viral_targets = atoi(row[191]);
sp[tempid].viral_timer = atoi(row[192]);
sp[tempid].NimbusEffect = atoi(row[193]);
sp[tempid].nimbus_effect = atoi(row[193]);
sp[tempid].directional_start = static_cast<float>(atoi(row[194]));
sp[tempid].directional_end = static_cast<float>(atoi(row[195]));
sp[tempid].sneak = atoi(row[196]) != 0;
@@ -1873,29 +1874,29 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].no_detrimental_spell_aggro = atoi(row[198]) != 0;
sp[tempid].suspendable = atoi(row[200]) != 0;
sp[tempid].viral_range = atoi(row[201]);
sp[tempid].songcap = atoi(row[202]);
sp[tempid].song_cap = atoi(row[202]);
sp[tempid].no_block = atoi(row[205]);
sp[tempid].spellgroup=atoi(row[207]);
sp[tempid].spell_group=atoi(row[207]);
sp[tempid].rank = atoi(row[208]);
sp[tempid].no_resist=atoi(row[209]);
sp[tempid].CastRestriction = atoi(row[211]);
sp[tempid].AllowRest = atoi(row[212]) != 0;
sp[tempid].InCombat = atoi(row[213]) != 0;
sp[tempid].OutofCombat = atoi(row[214]) != 0;
sp[tempid].cast_restriction = atoi(row[211]);
sp[tempid].allow_rest = atoi(row[212]) != 0;
sp[tempid].can_cast_in_combat = atoi(row[213]) != 0;
sp[tempid].can_cast_out_of_combat = atoi(row[214]) != 0;
sp[tempid].override_crit_chance = atoi(row[217]);
sp[tempid].aemaxtargets = atoi(row[218]);
sp[tempid].aoe_max_targets = atoi(row[218]);
sp[tempid].no_heal_damage_item_mod = atoi(row[219]);
sp[tempid].caster_requirement_id = atoi(row[220]);
sp[tempid].spell_class = atoi(row[221]);
sp[tempid].spell_subclass = atoi(row[222]);
sp[tempid].persistdeath = atoi(row[224]) != 0;
sp[tempid].min_dist = atof(row[227]);
sp[tempid].min_dist_mod = atof(row[228]);
sp[tempid].max_dist = atof(row[229]);
sp[tempid].max_dist_mod = atof(row[230]);
sp[tempid].persist_death = atoi(row[224]) != 0;
sp[tempid].min_distance = atof(row[227]);
sp[tempid].min_distance_mod = atof(row[228]);
sp[tempid].max_distance = atof(row[229]);
sp[tempid].max_distance_mod = atof(row[230]);
sp[tempid].min_range = static_cast<float>(atoi(row[231]));
sp[tempid].no_remove = atoi(row[232]) != 0;
sp[tempid].DamageShieldType = 0;
sp[tempid].damage_shield_type = 0;
}
LoadDamageShieldTypes(sp, max_spells);
+10 -96
View File
@@ -177,8 +177,7 @@ bool EQ::skills::IsMeleeDmg(SkillType skill)
const std::map<EQ::skills::SkillType, std::string>& EQ::skills::GetSkillTypeMap()
{
/* VS2013 code
static const std::map<SkillUseTypes, std::string> skill_use_types_map = {
static const std::map<SkillType, std::string> skill_type_map = {
{ Skill1HBlunt, "1H Blunt" },
{ Skill1HSlashing, "1H Slashing" },
{ Skill2HBlunt, "2H Blunt" },
@@ -258,103 +257,18 @@ const std::map<EQ::skills::SkillType, std::string>& EQ::skills::GetSkillTypeMap(
{ SkillTripleAttack, "Triple Attack" },
{ Skill2HPiercing, "2H Piercing" }
};
*/
/* VS2012 code - begin */
static const char* skill_use_names[SkillCount] = {
"1H Blunt",
"1H Slashing",
"2H Blunt",
"2H Slashing",
"Abjuration",
"Alteration",
"Apply Poison",
"Archery",
"Backstab",
"Bind Wound",
"Bash",
"Block",
"Brass Instruments",
"Channeling",
"Conjuration",
"Defense",
"Disarm",
"Disarm Traps",
"Divination",
"Dodge",
"Double Attack",
"Dragon Punch",
"Dual Wield",
"Eagle Strike",
"Evocation",
"Feign Death",
"Flying Kick",
"Forage",
"Hand to Hand",
"Hide",
"Kick",
"Meditate",
"Mend",
"Offense",
"Parry",
"Pick Lock",
"1H Piercing",
"Riposte",
"Round Kick",
"Safe Fall",
"Sense Heading",
"Singing",
"Sneak",
"Specialize Abjuration",
"Specialize Alteration",
"Specialize Conjuration",
"Specialize Divination",
"Specialize Evocation",
"Pick Pockets",
"Stringed Instruments",
"Swimming",
"Throwing",
"Tiger Claw",
"Tracking",
"Wind Instruments",
"Fishing",
"Make Poison",
"Tinkering",
"Research",
"Alchemy",
"Baking",
"Tailoring",
"Sense Traps",
"Blacksmithing",
"Fletching",
"Brewing",
"Alcohol Tolerance",
"Begging",
"Jewelry Making",
"Pottery",
"Percussion Instruments",
"Intimidation",
"Berserking",
"Taunt",
"Frenzy",
"Remove Traps",
"Triple Attack",
"2H Piercing"
};
static std::map<SkillType, std::string> skill_type_map;
skill_type_map.clear();
for (int i = Skill1HBlunt; i < SkillCount; ++i)
skill_type_map[(SkillType)i] = skill_use_names[i];
/* VS2012 code - end */
return skill_type_map;
}
std::string EQ::skills::GetSkillName(SkillType skill)
{
if (skill >= Skill1HBlunt && skill <= Skill2HPiercing) {
auto skills = GetSkillTypeMap();
return skills[skill];
}
return std::string();
}
EQ::SkillProfile::SkillProfile()
{
memset(&Skill, 0, (sizeof(uint32) * PACKET_SKILL_ARRAY_SIZE));
+1
View File
@@ -171,6 +171,7 @@ namespace EQ
extern const std::map<SkillType, std::string>& GetSkillTypeMap();
std::string GetSkillName(SkillType skill);
} /*skills*/
struct SkillProfile { // prototype - not implemented
+326 -163
View File
@@ -87,7 +87,7 @@
bool IsTargetableAESpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) {
if (IsValidSpell(spell_id) && spells[spell_id].target_type == ST_AETarget) {
return true;
}
@@ -103,8 +103,8 @@ bool IsLifetapSpell(uint16 spell_id)
{
// Ancient Lifebane: 2115
if (IsValidSpell(spell_id) &&
(spells[spell_id].targettype == ST_Tap ||
spells[spell_id].targettype == ST_TargetAETap ||
(spells[spell_id].target_type == ST_Tap ||
spells[spell_id].target_type == ST_TargetAETap ||
spell_id == 2115))
return true;
@@ -124,7 +124,7 @@ bool IsStunSpell(uint16 spell_id)
bool IsSummonSpell(uint16 spellid)
{
for (int o = 0; o < EFFECT_COUNT; o++) {
uint32 tid = spells[spellid].effectid[o];
uint32 tid = spells[spellid].effect_id[o];
if (tid == SE_SummonPet || tid == SE_SummonItem || tid == SE_SummonPC)
return true;
}
@@ -140,10 +140,10 @@ bool IsEvacSpell(uint16 spellid)
bool IsDamageSpell(uint16 spellid)
{
for (int o = 0; o < EFFECT_COUNT; o++) {
uint32 tid = spells[spellid].effectid[o];
uint32 tid = spells[spellid].effect_id[o];
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) &&
spells[spellid].targettype != ST_Tap && spells[spellid].buffduration < 1 &&
spells[spellid].base[o] < 0)
spells[spellid].target_type != ST_Tap && spells[spellid].buff_duration < 1 &&
spells[spellid].base_value[o] < 0)
return true;
}
@@ -163,8 +163,8 @@ bool IsCureSpell(uint16 spell_id)
bool CureEffect = false;
for(int i = 0; i < EFFECT_COUNT; i++){
if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter
|| sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter)
if (sp.effect_id[i] == SE_DiseaseCounter || sp.effect_id[i] == SE_PoisonCounter
|| sp.effect_id[i] == SE_CurseCounter || sp.effect_id[i] == SE_CorruptionCounter)
CureEffect = true;
}
@@ -179,8 +179,8 @@ bool IsSlowSpell(uint16 spell_id)
const SPDat_Spell_Struct &sp = spells[spell_id];
for(int i = 0; i < EFFECT_COUNT; i++)
if ((sp.effectid[i] == SE_AttackSpeed && sp.base[i] < 100) ||
(sp.effectid[i] == SE_AttackSpeed4))
if ((sp.effect_id[i] == SE_AttackSpeed && sp.base_value[i] < 100) ||
(sp.effect_id[i] == SE_AttackSpeed4))
return true;
return false;
@@ -191,8 +191,8 @@ bool IsHasteSpell(uint16 spell_id)
const SPDat_Spell_Struct &sp = spells[spell_id];
for(int i = 0; i < EFFECT_COUNT; i++)
if(sp.effectid[i] == SE_AttackSpeed)
return (sp.base[i] < 100);
if(sp.effect_id[i] == SE_AttackSpeed)
return (sp.base_value[i] < 100);
return false;
}
@@ -210,7 +210,7 @@ bool IsPercentalHealSpell(uint16 spell_id)
bool IsGroupOnlySpell(uint16 spell_id)
{
return IsValidSpell(spell_id) && spells[spell_id].goodEffect == 2;
return IsValidSpell(spell_id) && spells[spell_id].good_effect == 2;
}
bool IsBeneficialSpell(uint16 spell_id)
@@ -219,10 +219,10 @@ bool IsBeneficialSpell(uint16 spell_id)
return false;
// You'd think just checking goodEffect flag would be enough?
if (spells[spell_id].goodEffect == 1) {
if (spells[spell_id].good_effect == 1) {
// If the target type is ST_Self or ST_Pet and is a SE_CancleMagic spell
// it is not Beneficial
SpellTargetType tt = spells[spell_id].targettype;
SpellTargetType tt = spells[spell_id].target_type;
if (tt != ST_Self && tt != ST_Pet &&
IsEffectInSpell(spell_id, SE_CancelMagic))
return false;
@@ -231,14 +231,14 @@ bool IsBeneficialSpell(uint16 spell_id)
// We need to check more things!
if (tt == ST_Target || tt == ST_AETarget || tt == ST_Animal ||
tt == ST_Undead || tt == ST_Pet) {
uint16 sai = spells[spell_id].SpellAffectIndex;
uint16 sai = spells[spell_id].spell_affect_index;
// If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight
// it's not beneficial
if (spells[spell_id].resisttype == RESIST_MAGIC) {
if (spells[spell_id].resist_type == RESIST_MAGIC) {
// checking these SAI cause issues with the rng defensive proc line
// So I guess instead of fixing it for real, just a quick hack :P
if (spells[spell_id].effectid[0] != SE_DefensiveProc &&
if (spells[spell_id].effect_id[0] != SE_DefensiveProc &&
(sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur ||
sai == SAI_Calm_Song))
return false;
@@ -252,7 +252,7 @@ bool IsBeneficialSpell(uint16 spell_id)
}
// And finally, if goodEffect is not 0 or if it's a group spell it's beneficial
return spells[spell_id].goodEffect != 0 || IsGroupSpell(spell_id);
return spells[spell_id].good_effect != 0 || IsGroupSpell(spell_id);
}
bool IsDetrimentalSpell(uint16 spell_id)
@@ -364,8 +364,8 @@ bool IsImprovedDamageSpell(uint16 spell_id)
bool IsAEDurationSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) &&
(spells[spell_id].targettype == ST_AETarget || spells[spell_id].targettype == ST_UndeadAE) &&
spells[spell_id].AEDuration != 0)
(spells[spell_id].target_type == ST_AETarget || spells[spell_id].target_type == ST_UndeadAE) &&
spells[spell_id].aoe_duration != 0)
return true;
return false;
@@ -383,7 +383,7 @@ bool IsPureNukeSpell(uint16 spell_id)
effect_count++;
if (effect_count == 1 && IsEffectInSpell(spell_id, SE_CurrentHP) &&
spells[spell_id].buffduration == 0 && IsDamageSpell(spell_id))
spells[spell_id].buff_duration == 0 && IsDamageSpell(spell_id))
return true;
return false;
@@ -392,7 +392,7 @@ bool IsPureNukeSpell(uint16 spell_id)
bool IsAENukeSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
spells[spell_id].aoerange > 0)
spells[spell_id].aoe_range > 0)
return true;
return false;
@@ -401,7 +401,7 @@ bool IsAENukeSpell(uint16 spell_id)
bool IsPBAENukeSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
spells[spell_id].aoerange > 0 && spells[spell_id].targettype == ST_AECaster)
spells[spell_id].aoe_range > 0 && spells[spell_id].target_type == ST_AECaster)
return true;
return false;
@@ -410,7 +410,7 @@ bool IsPBAENukeSpell(uint16 spell_id)
bool IsAERainNukeSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
spells[spell_id].aoerange > 0 && spells[spell_id].AEDuration > 1000)
spells[spell_id].aoe_range > 0 && spells[spell_id].aoe_duration > 1000)
return true;
return false;
@@ -424,12 +424,12 @@ bool IsPartialCapableSpell(uint16 spell_id)
// spell uses 600 (partial) scale if first effect is damage, else it uses 200 scale.
// this includes DoTs. no_partial_resist excludes spells like necro snares
for (int o = 0; o < EFFECT_COUNT; o++) {
auto tid = spells[spell_id].effectid[o];
auto tid = spells[spell_id].effect_id[o];
if (IsBlankSpellEffect(spell_id, o))
continue;
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base[o] < 0)
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base_value[o] < 0)
return true;
return false;
@@ -452,9 +452,9 @@ bool IsResistableSpell(uint16 spell_id)
bool IsGroupSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) &&
(spells[spell_id].targettype == ST_AEBard ||
spells[spell_id].targettype == ST_Group ||
spells[spell_id].targettype == ST_GroupTeleport))
(spells[spell_id].target_type == ST_AEBard ||
spells[spell_id].target_type == ST_Group ||
spells[spell_id].target_type == ST_GroupTeleport))
return true;
return false;
@@ -464,7 +464,7 @@ bool IsGroupSpell(uint16 spell_id)
bool IsTGBCompatibleSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) &&
(!IsDetrimentalSpell(spell_id) && spells[spell_id].buffduration != 0 &&
(!IsDetrimentalSpell(spell_id) && spells[spell_id].buff_duration != 0 &&
!IsBardSong(spell_id) && !IsEffectInSpell(spell_id, SE_Illusion)))
return true;
@@ -473,7 +473,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id)
bool IsBardSong(uint16 spell_id)
{
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].IsDisciplineBuff)
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].is_discipline)
return true;
return false;
@@ -487,7 +487,7 @@ bool IsEffectInSpell(uint16 spellid, int effect)
return false;
for (j = 0; j < EFFECT_COUNT; j++)
if (spells[spellid].effectid[j] == effect)
if (spells[spellid].effect_id[j] == effect)
return true;
return false;
@@ -498,15 +498,15 @@ bool IsEffectInSpell(uint16 spellid, int effect)
// the blanks
bool IsBlankSpellEffect(uint16 spellid, int effect_index)
{
int effect, base, formula;
int effect, base_value, formula;
effect = spells[spellid].effectid[effect_index];
base = spells[spellid].base[effect_index];
effect = spells[spellid].effect_id[effect_index];
base_value = spells[spellid].base_value[effect_index];
formula = spells[spellid].formula[effect_index];
// SE_CHA is "spacer"
// SE_Stacking* are also considered blank where this is used
if (effect == SE_Blank || (effect == SE_CHA && base == 0 && formula == 100) ||
if (effect == SE_Blank || (effect == SE_CHA && base_value == 0 && formula == 100) ||
effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite)
return true;
@@ -561,7 +561,7 @@ int GetSpellEffectIndex(uint16 spell_id, int effect)
return -1;
for (i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == effect)
if (spells[spell_id].effect_id[i] == effect)
return i;
return -1;
@@ -591,7 +591,7 @@ bool BeneficialSpell(uint16 spell_id)
/*|| spells[spell_id].stacking == 27*/ )
return true;
switch (spells[spell_id].goodEffect) {
switch (spells[spell_id].good_effect) {
case 1:
case 3:
return true;
@@ -602,7 +602,7 @@ bool BeneficialSpell(uint16 spell_id)
bool GroupOnlySpell(uint16 spell_id)
{
switch (spells[spell_id].goodEffect) {
switch (spells[spell_id].good_effect) {
case 2:
case 3:
return true;
@@ -623,9 +623,9 @@ int32 CalculatePoisonCounters(uint16 spell_id)
int32 Counters = 0;
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_PoisonCounter &&
spells[spell_id].base[i] > 0)
Counters += spells[spell_id].base[i];
if (spells[spell_id].effect_id[i] == SE_PoisonCounter &&
spells[spell_id].base_value[i] > 0)
Counters += spells[spell_id].base_value[i];
return Counters;
}
@@ -637,9 +637,9 @@ int32 CalculateDiseaseCounters(uint16 spell_id)
int32 Counters = 0;
for (int i = 0; i < EFFECT_COUNT; i++)
if(spells[spell_id].effectid[i] == SE_DiseaseCounter &&
spells[spell_id].base[i] > 0)
Counters += spells[spell_id].base[i];
if(spells[spell_id].effect_id[i] == SE_DiseaseCounter &&
spells[spell_id].base_value[i] > 0)
Counters += spells[spell_id].base_value[i];
return Counters;
}
@@ -651,9 +651,9 @@ int32 CalculateCurseCounters(uint16 spell_id)
int32 Counters = 0;
for (int i = 0; i < EFFECT_COUNT; i++)
if(spells[spell_id].effectid[i] == SE_CurseCounter &&
spells[spell_id].base[i] > 0)
Counters += spells[spell_id].base[i];
if(spells[spell_id].effect_id[i] == SE_CurseCounter &&
spells[spell_id].base_value[i] > 0)
Counters += spells[spell_id].base_value[i];
return Counters;
}
@@ -665,9 +665,9 @@ int32 CalculateCorruptionCounters(uint16 spell_id)
int32 Counters = 0;
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_CorruptionCounter &&
spells[spell_id].base[i] > 0)
Counters += spells[spell_id].base[i];
if (spells[spell_id].effect_id[i] == SE_CorruptionCounter &&
spells[spell_id].base_value[i] > 0)
Counters += spells[spell_id].base_value[i];
return Counters;
}
@@ -695,7 +695,7 @@ bool IsDisciplineBuff(uint16 spell_id)
if (!IsValidSpell(spell_id))
return false;
if (spells[spell_id].IsDisciplineBuff && spells[spell_id].targettype == ST_Self)
if (spells[spell_id].is_discipline && spells[spell_id].target_type == ST_Self)
return true;
return false;
@@ -707,7 +707,7 @@ bool IsDiscipline(uint16 spell_id)
return false;
if (spells[spell_id].mana == 0 &&
(spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))
(spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep))
return true;
return false;
@@ -719,7 +719,7 @@ bool IsCombatSkill(uint16 spell_id)
return false;
//Check if Discipline
if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)))
if ((spells[spell_id].mana == 0 && (spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep)))
return true;
return false;
@@ -738,7 +738,7 @@ bool IsRuneSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id))
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_Rune)
if (spells[spell_id].effect_id[i] == SE_Rune)
return true;
return false;
@@ -748,7 +748,7 @@ bool IsMagicRuneSpell(uint16 spell_id)
{
if(IsValidSpell(spell_id))
for(int i = 0; i < EFFECT_COUNT; i++)
if(spells[spell_id].effectid[i] == SE_AbsorbMagicAtt)
if(spells[spell_id].effect_id[i] == SE_AbsorbMagicAtt)
return true;
return false;
@@ -758,8 +758,8 @@ bool IsManaTapSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id))
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_CurrentMana &&
spells[spell_id].targettype == ST_Tap)
if (spells[spell_id].effect_id[i] == SE_CurrentMana &&
spells[spell_id].target_type == ST_Tap)
return true;
return false;
@@ -788,8 +788,8 @@ bool IsPartialDeathSaveSpell(uint16 spell_id)
return false;
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_DeathSave &&
spells[spell_id].base[i] == 1)
if (spells[spell_id].effect_id[i] == SE_DeathSave &&
spells[spell_id].base_value[i] == 1)
return true;
return false;
@@ -802,8 +802,8 @@ bool IsFullDeathSaveSpell(uint16 spell_id)
return false;
for (int i = 0; i < EFFECT_COUNT; i++)
if (spells[spell_id].effectid[i] == SE_DeathSave &&
spells[spell_id].base[i] == 2)
if (spells[spell_id].effect_id[i] == SE_DeathSave &&
spells[spell_id].base_value[i] == 2)
return true;
return false;
@@ -833,6 +833,14 @@ bool IsTeleportSpell(uint16 spell_id)
return false;
}
bool IsTranslocateSpell(uint16 spell_id)
{
if (IsEffectInSpell(spell_id, SE_Translocate))
return true;
return false;
}
bool IsGateSpell(uint16 spell_id)
{
if (IsEffectInSpell(spell_id, SE_Gate))
@@ -844,7 +852,7 @@ bool IsGateSpell(uint16 spell_id)
bool IsPlayerIllusionSpell(uint16 spell_id)
{
if (IsEffectInSpell(spell_id, SE_Illusion) &&
spells[spell_id].targettype == ST_Self)
spells[spell_id].target_type == ST_Self)
return true;
return false;
@@ -853,7 +861,7 @@ bool IsPlayerIllusionSpell(uint16 spell_id)
int GetSpellEffectDescNum(uint16 spell_id)
{
if (IsValidSpell(spell_id))
return spells[spell_id].effectdescnum;
return spells[spell_id].effect_description_id;
return -1;
}
@@ -864,12 +872,12 @@ DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType)
// else, make a guess, based on the resist type. Default return value is DS_THORNS
if (IsValidSpell(spell_id)) {
LogSpells("DamageShieldType for spell [{}] ([{}]) is [{}]", spell_id,
spells[spell_id].name, spells[spell_id].DamageShieldType);
spells[spell_id].name, spells[spell_id].damage_shield_type);
if (spells[spell_id].DamageShieldType)
return (DmgShieldType) spells[spell_id].DamageShieldType;
if (spells[spell_id].damage_shield_type)
return (DmgShieldType) spells[spell_id].damage_shield_type;
switch (spells[spell_id].resisttype) {
switch (spells[spell_id].resist_type) {
case RESIST_COLD:
return DS_TORMENT;
case RESIST_FIRE:
@@ -899,12 +907,12 @@ bool IsLDoNObjectSpell(uint16 spell_id)
int32 GetSpellResistType(uint16 spell_id)
{
return spells[spell_id].resisttype;
return spells[spell_id].resist_type;
}
int32 GetSpellTargetType(uint16 spell_id)
{
return (int32)spells[spell_id].targettype;
return (int32)spells[spell_id].target_type;
}
bool IsHealOverTimeSpell(uint16 spell_id)
@@ -929,7 +937,7 @@ bool IsFastHealSpell(uint16 spell_id)
const int MaxFastHealCastingTime = 2000;
if (spells[spell_id].cast_time <= MaxFastHealCastingTime &&
spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
!IsGroupSpell(spell_id))
return true;
@@ -941,7 +949,7 @@ bool IsVeryFastHealSpell(uint16 spell_id)
const int MaxFastHealCastingTime = 1000;
if (spells[spell_id].cast_time <= MaxFastHealCastingTime &&
spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
!IsGroupSpell(spell_id))
return true;
@@ -950,8 +958,8 @@ bool IsVeryFastHealSpell(uint16 spell_id)
bool IsRegularSingleTargetHealSpell(uint16 spell_id)
{
if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 &&
if(spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
spells[spell_id].target_type == ST_Target && spells[spell_id].buff_duration == 0 &&
!IsCompleteHealSpell(spell_id) &&
!IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id))
return true;
@@ -977,7 +985,7 @@ bool IsGroupCompleteHealSpell(uint16 spell_id)
bool IsGroupHealOverTimeSpell(uint16 spell_id)
{
if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buffduration < 10)
if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buff_duration < 10)
return true;
return false;
@@ -1008,8 +1016,8 @@ bool IsResistDebuffSpell(uint16 spell_id)
bool IsSelfConversionSpell(uint16 spell_id)
{
if (GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) &&
IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0)
IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0)
return true;
else
return false;
@@ -1018,7 +1026,7 @@ bool IsSelfConversionSpell(uint16 spell_id)
// returns true for both detrimental and beneficial buffs
bool IsBuffSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && (spells[spell_id].buffduration || spells[spell_id].buffdurationformula))
if (IsValidSpell(spell_id) && (spells[spell_id].buff_duration || spells[spell_id].buff_duration_formula))
return true;
return false;
@@ -1026,7 +1034,7 @@ bool IsBuffSpell(uint16 spell_id)
bool IsPersistDeathSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) && spells[spell_id].persistdeath)
if (IsValidSpell(spell_id) && spells[spell_id].persist_death)
return true;
return false;
@@ -1043,8 +1051,8 @@ bool IsSuspendableSpell(uint16 spell_id)
uint32 GetMorphTrigger(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect)
return spells[spell_id].base[i];
if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect)
return spells[spell_id].base_value[i];
return 0;
}
@@ -1052,9 +1060,9 @@ uint32 GetMorphTrigger(uint32 spell_id)
bool IsCastonFadeDurationSpell(uint16 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i) {
if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect
|| spells[spell_id].effectid[i] == SE_CastOnFadeEffectNPC
|| spells[spell_id].effectid[i] == SE_CastOnFadeEffectAlways){
if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect
|| spells[spell_id].effect_id[i] == SE_CastOnFadeEffectNPC
|| spells[spell_id].effect_id[i] == SE_CastOnFadeEffectAlways){
return true;
}
@@ -1065,7 +1073,7 @@ bool IsCastonFadeDurationSpell(uint16 spell_id)
bool IsPowerDistModSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) &&
(spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist)
(spells[spell_id].max_distance_mod || spells[spell_id].min_distance_mod) && spells[spell_id].max_distance > spells[spell_id].min_distance)
return true;
return false;
@@ -1074,8 +1082,8 @@ bool IsPowerDistModSpell(uint16 spell_id)
uint32 GetPartialMeleeRuneReduction(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage)
return spells[spell_id].base[i];
if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage)
return spells[spell_id].base_value[i];
return 0;
}
@@ -1083,8 +1091,8 @@ uint32 GetPartialMeleeRuneReduction(uint32 spell_id)
uint32 GetPartialMagicRuneReduction(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage)
return spells[spell_id].base[i];
if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage)
return spells[spell_id].base_value[i];
return 0;
}
@@ -1092,8 +1100,8 @@ uint32 GetPartialMagicRuneReduction(uint32 spell_id)
uint32 GetPartialMeleeRuneAmount(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage)
return spells[spell_id].max[i];
if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage)
return spells[spell_id].max_value[i];
return 0;
}
@@ -1101,8 +1109,8 @@ uint32 GetPartialMeleeRuneAmount(uint32 spell_id)
uint32 GetPartialMagicRuneAmount(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage)
return spells[spell_id].max[i];
if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage)
return spells[spell_id].max_value[i];
return 0;
}
@@ -1111,7 +1119,7 @@ uint32 GetPartialMagicRuneAmount(uint32 spell_id)
bool DetrimentalSpellAllowsRest(uint16 spell_id)
{
if (IsValidSpell(spell_id))
return spells[spell_id].AllowRest;
return spells[spell_id].allow_rest;
return false;
}
@@ -1130,7 +1138,7 @@ bool IsStackableDot(uint16 spell_id)
if (!IsValidSpell(spell_id))
return false;
const auto &spell = spells[spell_id];
if (spell.dot_stacking_exempt || spell.goodEffect || !spell.buffdurationformula)
if (spell.unstackable_dot || spell.good_effect || !spell.buff_duration_formula)
return false;
return IsEffectInSpell(spell_id, SE_CurrentHP) || IsEffectInSpell(spell_id, SE_GravityEffect);
}
@@ -1222,7 +1230,7 @@ bool IsEffectIgnoredInStacking(int spa)
case SE_LimitClass:
case SE_LimitRace:
case SE_FcBaseEffects:
case 415:
case SE_FFItemClass:
case SE_SkillDamageAmount2:
case SE_FcLimitUse:
case SE_FcIncreaseNumHits:
@@ -1247,6 +1255,7 @@ bool IsEffectIgnoredInStacking(int spa)
case SE_Ff_ReuseTimeMax:
case SE_Ff_Value_Min:
case SE_Ff_Value_Max:
case SE_Ff_FocusTimerMin:
return true;
default:
return false;
@@ -1288,6 +1297,8 @@ bool IsFocusLimit(int spa)
case SE_Ff_ReuseTimeMax:
case SE_Ff_Value_Min:
case SE_Ff_Value_Max:
case SE_Ff_FocusTimerMin:
case SE_FFItemClass:
return true;
default:
return false;
@@ -1297,7 +1308,7 @@ bool IsFocusLimit(int spa)
uint32 GetNimbusEffect(uint16 spell_id)
{
if (IsValidSpell(spell_id))
return (int32)spells[spell_id].NimbusEffect;
return (int32)spells[spell_id].nimbus_effect;
return 0;
}
@@ -1311,9 +1322,9 @@ int32 GetFuriousBash(uint16 spell_id)
int32 mod = 0;
for (int i = 0; i < EFFECT_COUNT; ++i)
if (spells[spell_id].effectid[i] == SE_SpellHateMod)
mod = spells[spell_id].base[i];
else if (spells[spell_id].effectid[i] == SE_LimitEffect && spells[spell_id].base[i] == 999)
if (spells[spell_id].effect_id[i] == SE_SpellHateMod)
mod = spells[spell_id].base_value[i];
else if (spells[spell_id].effect_id[i] == SE_LimitEffect && spells[spell_id].base_value[i] == 999)
found_effect_limit = true;
if (found_effect_limit)
@@ -1334,8 +1345,8 @@ bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type)
{
//check if spell can be cast in any zone (-1 or 255), then if spell zonetype matches zone's zonetype
// || spells[spell_id].zonetype == 255 comparing signed 8 bit int to 255 is always false
if (IsValidSpell(spell_id) && (spells[spell_id].zonetype == -1 ||
spells[spell_id].zonetype == zone_type))
if (IsValidSpell(spell_id) && (spells[spell_id].zone_type == -1 ||
spells[spell_id].zone_type == zone_type))
return true;
return false;
@@ -1348,16 +1359,109 @@ const char* GetSpellName(uint16 spell_id)
bool SpellRequiresTarget(int spell_id)
{
if (spells[spell_id].targettype == ST_AEClientV1 ||
spells[spell_id].targettype == ST_Self ||
spells[spell_id].targettype == ST_AECaster ||
spells[spell_id].targettype == ST_Ring ||
spells[spell_id].targettype == ST_Beam) {
if (spells[spell_id].target_type == ST_AEClientV1 ||
spells[spell_id].target_type == ST_Self ||
spells[spell_id].target_type == ST_AECaster ||
spells[spell_id].target_type == ST_Ring ||
spells[spell_id].target_type == ST_Beam) {
return false;
}
return true;
}
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect)
{
//Effects that are verified modifiable by bard instrument/singing mods, or highly likely due to similiar type of effect.
switch (effect) {
//Only modify instant endurance or mana effects (Ie. Mana drain, Crescendo line)
case SE_CurrentEndurance:
case SE_CurrentMana: {
if (spells[spell_id].buff_duration == 0) {
return true;
}
//Mana regen is not modified.
return false;
}
case SE_CurrentHP:
case SE_ArmorClass:
case SE_ACv2:
case SE_MovementSpeed:
case SE_ATK:
case SE_STR:
case SE_DEX:
case SE_AGI:
case SE_STA:
case SE_INT:
case SE_WIS:
case SE_CHA:
case SE_AllStats:
case SE_ResistFire:
case SE_ResistCold:
case SE_ResistPoison:
case SE_ResistDisease:
case SE_ResistMagic:
case SE_ResistAll:
case SE_ResistCorruption:
case SE_Rune:
case SE_AbsorbMagicAtt:
case SE_DamageShield:
case SE_MitigateDamageShield:
case SE_Amplification: //On live Amplification is modified by singing mods, including itself.
case SE_TripleAttackChance:
case SE_Flurry:
case SE_DamageModifier:
case SE_DamageModifier2:
case SE_MinDamageModifier:
case SE_ProcChance:
case SE_PetFlurry: // ? Need verified
case SE_DiseaseCounter:
case SE_PoisonCounter:
case SE_CurseCounter:
case SE_CorruptionCounter:
return true;
/*
Following are confirmed NOT modifiable by instrument/singing mods.
Focus Effects, Proc Effects, Spell Triggers are not modified but handled elsewhere, not neccessary to checked here.
*/
case SE_AttackSpeed: //(Haste AND Slow not modifiable)
case SE_AttackSpeed2:
case SE_AttackSpeed3:
case SE_Lull:
case SE_ChangeFrenzyRad:
case SE_Harmony:
case SE_AddFaction:
//case SE_CurrentMana: // duration only
case SE_ManaRegen_v2:
//case SE_CurrentEndurance: // duration only
case SE_PersistentEffect:
case SE_ReduceReuseTimer:
case SE_Stun:
case SE_Mez:
case SE_WipeHateList: //?
case SE_CancelMagic:
case SE_ManaAbsorbPercentDamage:
case SE_ResistSpellChance:
case SE_Reflect:
case SE_MitigateSpellDamage:
case SE_MitigateMeleeDamage:
case SE_AllInstrumentMod:
case SE_AddSingingMod:
case SE_SongModCap:
case SE_BardSongRange:
case SE_TemporaryPets:
case SE_SpellOnDeath:
return false;
default:
return true;
}
//Allowing anything not confirmed to be restricted / allowed to receive modifiers, as to not inhbit anyone making custom bard songs.
}
int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot)
{
if (!IsValidSpell(spell_id))
@@ -1380,93 +1484,152 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot)
}
if (slot < 12) {
if (id == "base") { return spells[spell_id].base[slot]; }
else if (id == "base2") { return spells[spell_id].base2[slot]; }
else if (id == "max") { return spells[spell_id].max[slot]; }
if (id == "base") { return spells[spell_id].base_value[slot]; }
else if (id == "base2") { return spells[spell_id].limit_value[slot]; }
else if (id == "max") { return spells[spell_id].max_value[slot]; }
else if (id == "formula") { return spells[spell_id].formula[slot]; }
else if (id == "effectid") { return spells[spell_id].effectid[slot]; }
else if (id == "effectid") { return spells[spell_id].effect_id[slot]; }
}
if (slot < 4) {
if (id == "components") { return spells[spell_id].components[slot]; }
else if (id == "component_counts") { return spells[spell_id].component_counts[slot]; }
else if (id == "NoexpendReagent") { return spells[spell_id].NoexpendReagent[slot]; }
if (id == "components") { return spells[spell_id].component[slot]; }
else if (id == "component_counts") { return spells[spell_id].component_count[slot]; }
else if (id == "noexpendreagent") { return spells[spell_id].no_expend_reagent[slot]; }
}
if (id == "range") { return static_cast<int32>(spells[spell_id].range); }
else if (id == "aoerange") { return static_cast<int32>(spells[spell_id].aoerange); }
else if (id == "pushback") { return static_cast<int32>(spells[spell_id].pushback); }
else if (id == "pushup") { return static_cast<int32>(spells[spell_id].pushup); }
else if (id == "aoe_range") { return static_cast<int32>(spells[spell_id].aoe_range); }
else if (id == "push_back") { return static_cast<int32>(spells[spell_id].push_back); }
else if (id == "push_up") { return static_cast<int32>(spells[spell_id].push_up); }
else if (id == "cast_time") { return spells[spell_id].cast_time; }
else if (id == "recovery_time") { return spells[spell_id].recovery_time; }
else if (id == "recast_time") { return spells[spell_id].recast_time; }
else if (id == "buffdurationformula") { return spells[spell_id].buffdurationformula; }
else if (id == "buffduration") { return spells[spell_id].buffduration; }
else if (id == "AEDuration") { return spells[spell_id].AEDuration; }
else if (id == "buff_duration_formula") { return spells[spell_id].buff_duration_formula; }
else if (id == "buff_duration") { return spells[spell_id].buff_duration; }
else if (id == "aeduration") { return spells[spell_id].aoe_duration; }
else if (id == "mana") { return spells[spell_id].mana; }
//else if (id == "LightType") {stat = spells[spell_id].LightType; } - Not implemented
else if (id == "goodEffect") { return spells[spell_id].goodEffect; }
else if (id == "Activated") { return spells[spell_id].Activated; }
else if (id == "resisttype") { return spells[spell_id].resisttype; }
else if (id == "targettype") { return spells[spell_id].targettype; }
else if (id == "basediff") { return spells[spell_id].basediff; }
else if (id == "goodeffect") { return spells[spell_id].good_effect; }
else if (id == "activated") { return spells[spell_id].activated; }
else if (id == "resisttype") { return spells[spell_id].resist_type; }
else if (id == "targettype") { return spells[spell_id].target_type; }
else if (id == "basediff") { return spells[spell_id].base_difficulty; }
else if (id == "skill") { return spells[spell_id].skill; }
else if (id == "zonetype") { return spells[spell_id].zonetype; }
else if (id == "EnvironmentType") { return spells[spell_id].EnvironmentType; }
else if (id == "TimeOfDay") { return spells[spell_id].TimeOfDay; }
else if (id == "CastingAnim") { return spells[spell_id].CastingAnim; }
else if (id == "SpellAffectIndex") { return spells[spell_id].SpellAffectIndex; }
else if (id == "zonetype") { return spells[spell_id].zone_type; }
else if (id == "environmenttype") { return spells[spell_id].environment_type; }
else if (id == "timeofday") { return spells[spell_id].time_of_day; }
else if (id == "castinganim") { return spells[spell_id].casting_animation; }
else if (id == "spellaffectindex") { return spells[spell_id].spell_affect_index; }
else if (id == "disallow_sit") { return spells[spell_id].disallow_sit; }
//else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented
else if (id == "uninterruptable") { return spells[spell_id].uninterruptable; }
else if (id == "ResistDiff") { return spells[spell_id].ResistDiff; }
else if (id == "dot_stacking_exempt") { return spells[spell_id].dot_stacking_exempt; }
else if (id == "RecourseLink") { return spells[spell_id].RecourseLink; }
else if (id == "resistdiff") { return spells[spell_id].resist_difficulty; }
else if (id == "dot_stacking_exempt") { return spells[spell_id].unstackable_dot; }
else if (id == "recourselink") { return spells[spell_id].recourse_link; }
else if (id == "no_partial_resist") { return spells[spell_id].no_partial_resist; }
else if (id == "short_buff_box") { return spells[spell_id].short_buff_box; }
else if (id == "descnum") { return spells[spell_id].descnum; }
else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; }
else if (id == "descnum") { return spells[spell_id].description_id; }
else if (id == "effectdescnum") { return spells[spell_id].effect_description_id; }
else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; }
else if (id == "feedbackable") { return spells[spell_id].feedbackable; }
else if (id == "reflectable") { return spells[spell_id].reflectable; }
else if (id == "bonushate") { return spells[spell_id].bonushate; }
else if (id == "EndurCost") { return spells[spell_id].EndurCost; }
else if (id == "EndurTimerIndex") { return spells[spell_id].EndurTimerIndex; }
else if (id == "IsDisciplineBuff") { return spells[spell_id].IsDisciplineBuff; }
else if (id == "HateAdded") { return spells[spell_id].HateAdded; }
else if (id == "EndurUpkeep") { return spells[spell_id].EndurUpkeep; }
else if (id == "numhitstype") { return spells[spell_id].numhitstype; }
else if (id == "numhits") { return spells[spell_id].numhits; }
else if (id == "pvpresistbase") { return spells[spell_id].pvpresistbase; }
else if (id == "pvpresistcalc") { return spells[spell_id].pvpresistcalc; }
else if (id == "pvpresistcap") { return spells[spell_id].pvpresistcap; }
else if (id == "bonushate") { return spells[spell_id].bonus_hate; }
else if (id == "endurcost") { return spells[spell_id].endurance_cost; }
else if (id == "endurtimerindex") { return spells[spell_id].timer_id; }
else if (id == "isdisciplinebuff") { return spells[spell_id].is_discipline; }
else if (id == "hateadded") { return spells[spell_id].hate_added; }
else if (id == "endurupkeep") { return spells[spell_id].endurance_upkeep; }
else if (id == "numhitstype") { return spells[spell_id].hit_number_type; }
else if (id == "numhits") { return spells[spell_id].hit_number; }
else if (id == "pvpresistbase") { return spells[spell_id].pvp_resist_base; }
else if (id == "pvpresistcalc") { return spells[spell_id].pvp_resist_per_level; }
else if (id == "pvpresistcap") { return spells[spell_id].pvp_resist_cap; }
else if (id == "spell_category") { return spells[spell_id].spell_category; }
else if (id == "can_mgb") { return spells[spell_id].can_mgb; }
else if (id == "dispel_flag") { return spells[spell_id].dispel_flag; }
else if (id == "MinResist") { return spells[spell_id].MinResist; }
else if (id == "MaxResist") { return spells[spell_id].MaxResist; }
else if (id == "minresist") { return spells[spell_id].min_resist; }
else if (id == "maxresist") { return spells[spell_id].max_resist; }
else if (id == "viral_targets") { return spells[spell_id].viral_targets; }
else if (id == "viral_timer") { return spells[spell_id].viral_timer; }
else if (id == "NimbusEffect") { return spells[spell_id].NimbusEffect; }
else if (id == "nimbuseffect") { return spells[spell_id].nimbus_effect; }
else if (id == "directional_start") { return static_cast<int32>(spells[spell_id].directional_start); }
else if (id == "directional_end") { return static_cast<int32>(spells[spell_id].directional_end); }
else if (id == "not_focusable") { return spells[spell_id].not_focusable; }
else if (id == "suspendable") { return spells[spell_id].suspendable; }
else if (id == "viral_range") { return spells[spell_id].viral_range; }
else if (id == "spellgroup") { return spells[spell_id].spellgroup; }
else if (id == "spellgroup") { return spells[spell_id].spell_group; }
else if (id == "rank") { return spells[spell_id].rank; }
else if (id == "no_resist") { return spells[spell_id].no_resist; }
else if (id == "CastRestriction") { return spells[spell_id].CastRestriction; }
else if (id == "AllowRest") { return spells[spell_id].AllowRest; }
else if (id == "InCombat") { return spells[spell_id].InCombat; }
else if (id == "OutofCombat") { return spells[spell_id].OutofCombat; }
else if (id == "aemaxtargets") { return spells[spell_id].aemaxtargets; }
else if (id == "castrestriction") { return spells[spell_id].cast_restriction; }
else if (id == "allowrest") { return spells[spell_id].allow_rest; }
else if (id == "incombat") { return spells[spell_id].can_cast_in_combat; }
else if (id == "outofcombat") { return spells[spell_id].can_cast_out_of_combat; }
else if (id == "aemaxtargets") { return spells[spell_id].aoe_max_targets; }
else if (id == "no_heal_damage_item_mod") { return spells[spell_id].no_heal_damage_item_mod; }
else if (id == "persistdeath") { return spells[spell_id].persistdeath; }
else if (id == "min_dist") { return static_cast<int32>(spells[spell_id].min_dist); }
else if (id == "min_dist_mod") { return static_cast<int32>(spells[spell_id].min_dist_mod); }
else if (id == "max_dist") { return static_cast<int32>(spells[spell_id].max_dist); }
else if (id == "persistdeath") { return spells[spell_id].persist_death; }
else if (id == "min_dist") { return static_cast<int32>(spells[spell_id].min_distance); }
else if (id == "min_dist_mod") { return static_cast<int32>(spells[spell_id].min_distance_mod); }
else if (id == "max_dist") { return static_cast<int32>(spells[spell_id].max_distance); }
else if (id == "min_range") { return static_cast<int32>(spells[spell_id].min_range); }
else if (id == "DamageShieldType") { return spells[spell_id].DamageShieldType; }
else if (id == "damageshieldtype") { return spells[spell_id].damage_shield_type; }
return 0;
}
}
bool IsVirusSpell(int32 spell_id)
{
if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){
return true;
}
return false;
}
int32 GetViralMinSpreadTime(int32 spell_id)
{
return spells[spell_id].viral_targets;
}
int32 GetViralMaxSpreadTime(int32 spell_id)
{
return spells[spell_id].viral_timer;
}
int32 GetViralSpreadRange(int32 spell_id)
{
return spells[spell_id].viral_range;
}
uint32 GetProcLimitTimer(int32 spell_id, int proc_type) {
//This allows for support for effects that may have multiple different proc types and timers.
if (!IsValidSpell(spell_id)) {
return 0;
}
bool use_next_timer = false;
for (int i = 0; i < EFFECT_COUNT; ++i) {
if (proc_type == SE_WeaponProc) {
if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) {
use_next_timer = true;
}
}
if (proc_type == SE_RangedProc) {
if (spells[spell_id].effect_id[i] == SE_RangedProc) {
use_next_timer = true;
}
}
if (proc_type == SE_DefensiveProc) {
if (spells[spell_id].effect_id[i] == SE_DefensiveProc) {
use_next_timer = true;
}
}
if (use_next_timer && spells[spell_id].effect_id[i] == SE_Proc_Timer_Modifier) {
return spells[spell_id].limit_value[i];
}
}
return 0;
}
+107 -81
View File
@@ -129,6 +129,8 @@
#define SPELL_SPIRITUAL_ECHO 1248
#define SPELL_BRISTLING_ARMAMENT 1249
#define SPELL_WATON_DESTRUCTION 1250
#define SPELL_TRANSLOCATE_GROUP 1334
#define SPELL_TRANSLOCATE 1422
#define SPELL_ACTING_MAGIC_RESIST_I 1900
#define SPELL_ACTING_FIRE_RESIST_I 1901
#define SPELL_ACTING_COLD_RESIST_I 1902
@@ -153,8 +155,11 @@
#define SPELL_ACTING_SPIRIT_I 1921
#define SPELL_ACTING_SPIRIT_II 1922
#define SPELL_RESURRECTION_SICKNESS 756
#define SPELL_RESURRECTION_SICKNESS4 757
#define SPELL_TELEPORT 3243
#define SPELL_RESURRECTION_SICKNESS2 5249
#define SPELL_REVIVAL_SICKNESS 13087
#define SPELL_RESURRECTION_SICKNESS3 37624
#define SPELL_PACT_OF_HATE_RECOURSE 40375
#define SPELL_INCENDIARY_OOZE_BUFF 32513
@@ -172,9 +177,12 @@
#define EFFECT_COUNT 12
#define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2)
#define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists.
#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects
#define MaxLimitInclude 18 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects
#define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks]
#define MAX_AA_PROCS 16 //(Actual Proc Amount is MAX_AA_PROCS/4) Number of spells to check AA procs from. (This is arbitrary)
#define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary)
#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary)
#define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary)
@@ -198,7 +206,9 @@ enum FocusLimitIncludes {
IncludeExistsSELimitSpellClass = 12,
IncludeFoundSELimitSpellClass = 13,
IncludeExistsSELimitSpellSubclass = 14,
IncludeFoundSELimitSpellSubclass = 15
IncludeFoundSELimitSpellSubclass = 15,
IncludeExistsSEFFItemClass = 16,
IncludeFoundSEFFItemClass = 17
};
/*
The id's correspond to 'type' 39 in live(2021) dbstr_us gives the message for target and caster restricted effects. These are not present in the ROF2 dbstr_us.
@@ -509,6 +519,15 @@ enum NegateSpellEffectType
NEGATE_SPA_SPELLBONUS_AND_AABONUS = 5,
NEGATE_SPA_ITEMBONUS_AND_AABONUS = 6,
};
//Used for rule RuleI(Spells, ReflectType))
enum ReflectSpellType
{
REFLECT_DISABLED = 0,
REFLECT_SINGLE_TARGET_SPELLS_ONLY = 1,
REFLECT_ALL_PLAYER_SPELLS = 2,
RELFECT_ALL_SINGLE_TARGET_SPELLS = 3,
REFLECT_ALL_SPELLS = 4,
};
enum SpellTypes : uint32
{
@@ -547,7 +566,7 @@ const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellTyp
// These should not be used to determine spell category..
// They are a graphical affects (effects?) index only
// TODO: import sai list
enum SpellAffectIndex {
enum spell_affect_index {
SAI_Summon_Mount_Unclass = -1,
SAI_Direct_Damage = 0,
SAI_Heal_Cure = 1,
@@ -746,7 +765,7 @@ typedef enum {
//#define SE_TransferItem 60 // not used
#define SE_Identify 61 // implemented
//#define SE_ItemID 62 // not used
#define SE_WipeHateList 63 // implemented
#define SE_WipeHateList 63 // implemented, @Memblur, chance to wipe hate list of target, base: pct chance, limit: none, max: ? (not implemented), Note: caster level and CHA add to pct chance
#define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly
#define SE_InfraVision 65 // implemented
#define SE_UltraVision 66 // implemented
@@ -801,7 +820,7 @@ typedef enum {
#define SE_Hunger 115 // implemented - Song of Sustenance
#define SE_CurseCounter 116 // implemented
#define SE_MagicWeapon 117 // implemented - makes weapon magical
#define SE_Amplification 118 // implemented - Harmonize/Amplification (stacks with other singing mods)
#define SE_Amplification 118 // implemented, @Song, stackable singing mod, base: mod%, limit: none, max: none, Note: Can focus itself.
#define SE_AttackSpeed3 119 // implemented
#define SE_HealRate 120 // implemented - reduces healing by a %
#define SE_ReverseDS 121 // implemented
@@ -837,11 +856,11 @@ typedef enum {
#define SE_SuspendPet 151 // implemented, @Pet, allow caster to have an extra suspended pet, base: 0=no buffs/items 1=buffs+items, limit: none, max: none
#define SE_TemporaryPets 152 // implemented
#define SE_BalanceHP 153 // implemented
#define SE_DispelDetrimental 154 // implemented
#define SE_DispelDetrimental 154 // implemented, @Dispel, removes only detrimental effects on a target, base: pct chance (950=95%), limit: none, max: none
#define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently
#define SE_IllusionCopy 156 // implemented - Deception
#define SE_SpellDamageShield 157 // implemented - Petrad's Protection
#define SE_Reflect 158 // implemented
#define SE_SpellDamageShield 157 // implemented, @DS, causes non-melee damage on caster of a spell, base: Amt DS (negative), limit: none, max: unknown (same as base but +)
#define SE_Reflect 158 // implemented, @SpellMisc, reflect casted detrimental spell back at caster, base: chance pct, limit: resist modifier (positive value reduces resists), max: pct of base dmg mod (50=50pct of base)
#define SE_AllStats 159 // implemented
//#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance)
#define SE_MitigateSpellDamage 161 // implemented - rune with max value
@@ -862,7 +881,7 @@ typedef enum {
#define SE_DualWieldChance 176 // implemented
#define SE_DoubleAttackChance 177 // implemented
#define SE_MeleeLifetap 178 // implemented
#define SE_AllInstrumentMod 179 // implemented
#define SE_AllInstrumentMod 179 // implemented, @Song, set mod for ALL instrument/singing skills that will be used if higher then item mods, base: mod%, limit: none, max: none
#define SE_ResistSpellChance 180 // implemented
#define SE_ResistFearChance 181 // implemented
#define SE_HundredHands 182 // implemented
@@ -892,7 +911,7 @@ typedef enum {
#define SE_AETaunt 206 // implemented
#define SE_FleshToBone 207 // implemented
//#define SE_PurgePoison 208 // not used
#define SE_DispelBeneficial 209 // implemented
#define SE_DispelBeneficial 209 // implemented, @Dispel, removes only beneficial effects on a target, base: pct chance (950=95%), limit: none, max: none
#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live)
#define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm).
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
@@ -925,7 +944,7 @@ typedef enum {
#define SE_FeignedCastOnChance 239 // implemented - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
//#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.]
#define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet.
#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr
#define SE_IncreaseChanceMemwipe 242 // implemented - @Memblur, increases the chance to wipe hate with memory blurr, base: chance pct, limit: none, max: none, Note: Mods final blur chance after other bonuses added.
#define SE_CharmBreakChance 243 // implemented - Total Domination
#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break.
#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
@@ -943,8 +962,8 @@ typedef enum {
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold - official name is GivePetHold
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live)
#define SE_AddSingingMod 260 // implemented, @Song, set mod for specific instrument/singing skills that will be used if higher then item mods, base: mod%, limit: ItemType ID, max: none
#define SE_SongModCap 261 // implemented, @Song, raise max song modifier cap, base: amt, limit: none, max: none, Note: No longer used on live
#define SE_RaiseStatCap 262 // implemented
#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master.
#define SE_HastenedAASkill 264 // implemented
@@ -953,7 +972,7 @@ typedef enum {
#define SE_AddPetCommand 267 // implemented - sets command base2 to base1
#define SE_ReduceTradeskillFail 268 // implemented - reduces chance to fail with given tradeskill by a percent chance
#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound.
#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo)
#define SE_BardSongRange 270 // implemented, @Song, increase range of beneficial bard songs, base: mod%, limit: none, max: none , Note: example Sionachie's Crescendo
#define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods
#define SE_CastingLevel2 272 // implemented
#define SE_CriticalDoTChance 273 // implemented
@@ -974,7 +993,7 @@ typedef enum {
#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch)
#define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration.
#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap
#define SE_Purify 291 // implemented - Removes determental effects
#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness
#define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte.
#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore
#define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier.
@@ -1065,7 +1084,7 @@ typedef enum {
#define SE_ShadowStepDirectional 379 // implemented - handled by client
#define SE_Knockdown 380 // implemented - small knock back(handled by client)
//#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront
#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none
#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff and prevent non-duration spell effect from working, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none
#define SE_SympatheticProc 383 // implemented, @Fc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid
#define SE_Leap 384 // implemented - Leap effect, ie stomping leap
#define SE_LimitSpellGroup 385 // implemented, @Ff, Spell group(s) that a spell focus can require or exclude, base1: spellgroup id, Include: Positive Exclude: Negative
@@ -1076,9 +1095,9 @@ typedef enum {
#define SE_FcTimerLockout 390 // implemented, @Fc, On Caster, set a spell to be on recast timer, base: recast duration milliseconds, Note: Applied from casted spells only
#define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt
#define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt
#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct
#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct
#define SE_FcHealAmtIncoming 394 // implemented, @Fc, On Target, heal received mod flat amt, base: amt
#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct
#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct
#define SE_FcHealAmtCrit 396 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt
#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC
#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets
@@ -1098,7 +1117,7 @@ typedef enum {
#define SE_LimitRace 412 // implemented, @Ff, Race that can use the spell focus, base1: race, Note: not used in any known live spells. Use only single race at a time.
#define SE_FcBaseEffects 413 // implemented, @Fc, On Caster, base spell effectiveness mod pct, base: pct
#define SE_LimitCastingSkill 414 // implemented, @Ff, Spell and singing skills(s) that a spell focus can require or exclude, base1: skill id, Include: Positive Exclude: Negative
//#define SE_FFItemClass 415 // not used - base1 matches ItemType, base2 matches SubType, -1 ignored, max is bitmask of valid slots
#define SE_FFItemClass 415 // implemented, @Ff, Limits focuses to be applied only from item click. base1: item ItemType (-1 to include for all ItemTypes,-1000 to exclude clicks from getting the focus, or exclude specific SubTypes or Slots if set), limit: item SubType (-1 for all SubTypes), max: item Slots (bitmask of valid slots, -1 ALL slots), Note: not used on live. See comments in Mob::CalcFocusEffect for more details.
#define SE_ACv2 416 // implemented - New AC spell effect
#define SE_ManaRegen_v2 417 // implemented - New mana regen effect
#define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks
@@ -1125,8 +1144,8 @@ typedef enum {
#define SE_Assassinate 439 // implemented[AA] - Assassinate damage
#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC
#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
#define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
#define SE_TriggerOnReqTarget 442 // implemented, @SpellTrigger, triggers a spell when Target Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually cast on a target
#define SE_TriggerOnReqCaster 443 // implemented, @SpellTrigger, triggers a spell when Caster Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually self only
#define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y
//#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs.
#define SE_AStacker 446 // implementet - bufff stacking blocker (26219 | Qirik's Watch)
@@ -1194,8 +1213,8 @@ typedef enum {
#define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt
#define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor
#define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt
//#define SE_Ff_FocusTimerMin 511 //
#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500).
#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds
#define SE_Proc_Timer_Modifier 512 // implemented - limits procs per amount of a time based on timer value, base: 1, limit: time ms, Note:, ie limit to 1 proc every 55 seconds)
//#define SE_Mana_Max_Percent 513 //
//#define SE_Endurance_Max_Percent 514 //
#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier
@@ -1238,44 +1257,44 @@ struct SPDat_Spell_Struct
/* 007 */ char cast_on_other[64]; // Message when spell is cast on someone else -- CASTEDOTHERTXT
/* 008 */ char spell_fades[64]; // Spell fades -- SPELLGONE
/* 009 */ float range; // -- RANGE
/* 010 */ float aoerange; // -- IMPACTRANGE
/* 011 */ float pushback; // -- OUTFORCE
/* 012 */ float pushup; // -- UPFORCE
/* 010 */ float aoe_range; // -- IMPACTRANGE
/* 011 */ float push_back; // -- OUTFORCE
/* 012 */ float push_up; // -- UPFORCE
/* 013 */ uint32 cast_time; // Cast time -- CASTINGTIME
/* 014 */ uint32 recovery_time; // Recovery time -- RECOVERYDELAY
/* 015 */ uint32 recast_time; // Recast same spell time -- SPELLDELAY
/* 016 */ uint32 buffdurationformula; // -- DURATIONBASE
/* 017 */ uint32 buffduration; // -- DURATIONCAP
/* 018 */ uint32 AEDuration; // sentinel, rain of something -- IMPACTDURATION
/* 016 */ uint32 buff_duration_formula; // -- DURATIONBASE
/* 017 */ uint32 buff_duration; // -- DURATIONCAP
/* 018 */ uint32 aoe_duration; // sentinel, rain of something -- IMPACTDURATION
/* 019 */ uint16 mana; // Mana Used -- MANACOST
/* 020 */ int base[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12
/* 032 */ int base2[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12
/* 044 */ int32 max[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP
/* 020 */ int base_value[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12
/* 032 */ int limit_value[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12
/* 044 */ int32 max_value[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP
/* 056 */ //uint16 icon; // Spell icon -- IMAGENUMBER
/* 057 */ //uint16 memicon; // Icon on membarthing -- MEMIMAGENUMBER
/* 058 */ int32 components[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4
/* 062 */ int component_counts[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4
/* 066 */ int NoexpendReagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.)
/* 058 */ int32 component[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4
/* 062 */ int component_count[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4
/* 066 */ int no_expend_reagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.)
// If it is a number between 1-4 it means components[number] is a focus and not to expend it
// If it is a valid itemid it means this item is a focus as well
// -- NOEXPENDREAGENT1 ... NOEXPENDREAGENT4
/* 070 */ uint16 formula[EFFECT_COUNT]; // Spell's value formula -- LEVELAFFECT1MOD ... LEVELAFFECT12MOD
/* 082 */ //int LightType; // probaly another effecttype flag -- LIGHTTYPE
/* 083 */ int8 goodEffect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL
/* 084 */ int Activated; // probably another effecttype flag -- ACTIVATED
/* 085 */ int resisttype; // -- RESISTTYPE
/* 086 */ int effectid[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12
/* 098 */ SpellTargetType targettype; // Spell's Target -- TYPENUMBER
/* 099 */ int basediff; // base difficulty fizzle adjustment -- BASEDIFFICULTY
/* 083 */ int8 good_effect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL
/* 084 */ int activated; // probably another effecttype flag -- ACTIVATED
/* 085 */ int resist_type; // -- RESISTTYPE
/* 086 */ int effect_id[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12
/* 098 */ SpellTargetType target_type; // Spell's Target -- TYPENUMBER
/* 099 */ int base_difficulty; // base difficulty fizzle adjustment -- BASEDIFFICULTY
/* 100 */ EQ::skills::SkillType skill; // -- CASTINGSKILL
/* 101 */ int8 zonetype; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE
/* 102 */ int8 EnvironmentType; // -- ENVIRONMENTTYPE
/* 103 */ int8 TimeOfDay; // -- TIMEOFDAY
/* 101 */ int8 zone_type; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE
/* 102 */ int8 environment_type; // -- ENVIRONMENTTYPE
/* 103 */ int8 time_of_day; // -- TIMEOFDAY
/* 104 */ uint8 classes[PLAYER_CLASS_COUNT]; // Classes, and their min levels -- WARRIORMIN ... BERSERKERMIN
/* 120 */ uint8 CastingAnim; // -- CASTINGANIM
/* 120 */ uint8 casting_animation; // -- CASTINGANIM
/* 121 */ //uint8 TargetAnim; // -- TARGETANIM
/* 122 */ //uint32 TravelType; // -- TRAVELTYPE
/* 123 */ uint16 SpellAffectIndex; // -- SPELLAFFECTINDEX
/* 123 */ uint16 spell_affect_index; // -- SPELLAFFECTINDEX
/* 124 */ int8 disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury -- CANCELONSIT
/* 125 */ int8 deity_agnostic;// 125: Words of the Skeptic -- DEITY_AGNOSTIC
/* 126 */ int8 deities[16]; // Deity check. 201 - 216 per http://www.eqemulator.net/wiki/wikka.php?wakka=DeityList
@@ -1287,36 +1306,36 @@ struct SPDat_Spell_Struct
/* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON
/* 145 */ //int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is, particles I think -- SPELL_EFFECT_INDEX
/* 146 */ bool uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) -- NO_INTERRUPT
/* 147 */ int16 ResistDiff; // -- RESIST_MOD
/* 148 */ bool dot_stacking_exempt; // -- NOT_STACKABLE_DOT
/* 147 */ int16 resist_difficulty; // -- RESIST_MOD
/* 148 */ bool unstackable_dot; // -- NOT_STACKABLE_DOT
/* 149 */ //int deletable; // -- DELETE_OK
/* 150 */ uint16 RecourseLink; // -- REFLECT_SPELLINDEX
/* 150 */ uint16 recourse_link; // -- REFLECT_SPELLINDEX
/* 151 */ bool no_partial_resist; // 151: -1, 0, or 1 -- NO_PARTIAL_SAVE
/* 152 */ //bool small_targets_only; // -- SMALL_TARGETS_ONLY
/* 153 */ //bool uses_persistent_particles; // -- USES_PERSISTENT_PARTICLES
/* 154 */ int8 short_buff_box; // != 0, goes to short buff box. -- BARD_BUFF_BOX
/* 155 */ int descnum; // eqstr of description of spell -- DESCRIPTION_INDEX
/* 156 */ int typedescnum; // eqstr of type description -- PRIMARY_CATEGORY
/* 157 */ int effectdescnum; // eqstr of effect description -- SECONDARY_CATEGORY_1
/* 155 */ int description_id; // eqstr of description of spell -- DESCRIPTION_INDEX
/* 156 */ int type_description_id; // eqstr of type description -- PRIMARY_CATEGORY
/* 157 */ int effect_description_id; // eqstr of effect description -- SECONDARY_CATEGORY_1
/* 158 */ //int secondary_category_2; //Category Desc ID 3 -- SECONDARY_CATEGORY_2
/* 159 */ bool npc_no_los; // -- NO_NPC_LOS
/* 160 */ //bool feedbackable; // -- FEEDBACKABLE
/* 160 */ bool feedbackable; // -- FEEDBACKABLE
/* 161 */ bool reflectable; // -- REFLECTABLE
/* 162 */ int bonushate; // -- HATE_MOD
/* 162 */ int bonus_hate; // -- HATE_MOD
/* 163 */ //int resist_per_level; // -- RESIST_PER_LEVEL
/* 164 */ //int resist_cap; // for most spells this appears to mimic ResistDiff -- RESIST_CAP
/* 165 */ bool ldon_trap; //Flag found on all LDON trap / chest related spells. -- AFFECT_INANIMATE
/* 166 */ int EndurCost; // -- STAMINA_COST
/* 167 */ int8 EndurTimerIndex; // bad name, used for all spells -- TIMER_INDEX
/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast -- IS_SKILL
/* 166 */ int endurance_cost; // -- STAMINA_COST
/* 167 */ int8 timer_id; // bad name, used for all spells -- TIMER_INDEX
/* 168 */ bool is_discipline; //Will goto the combat window when cast -- IS_SKILL
/* 169 - 172*/ //These are zero for ALL spells, also removed from live -- ATTACK_OPENING, DEFENSE_OPENING, SKILL_OPENING, NPC_ERROR_OPENING
/* 173 */ int HateAdded; // -- SPELL_HATE_GIVEN
/* 174 */ int EndurUpkeep; // -- ENDUR_UPKEEP
/* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE
/* 176 */ int numhits; // -- LIMITED_USE_COUNT
/* 177 */ int pvpresistbase; // -- PVP_RESIST_MOD
/* 178 */ int pvpresistcalc; // -- PVP_RESIST_PER_LEVEL
/* 179 */ int pvpresistcap; // -- PVP_RESIST_CAP
/* 173 */ int hate_added; // -- SPELL_HATE_GIVEN
/* 174 */ int endurance_upkeep; // -- ENDUR_UPKEEP
/* 175 */ int hit_number_type; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE
/* 176 */ int hit_number; // -- LIMITED_USE_COUNT
/* 177 */ int pvp_resist_base; // -- PVP_RESIST_MOD
/* 178 */ int pvp_resist_per_level; // -- PVP_RESIST_PER_LEVEL
/* 179 */ int pvp_resist_cap; // -- PVP_RESIST_CAP
/* 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
@@ -1326,11 +1345,11 @@ struct SPDat_Spell_Struct
/* 186 */ int dispel_flag; // -- NO_DISPELL
/* 187 */ //int npc_category; // -- NPC_MEM_CATEGORY
/* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS
/* 189 */ int MinResist; // -- MIN_RESIST
/* 190 */ int MaxResist; // -- MAX_RESIST
/* 189 */ int min_resist; // -- MIN_RESIST
/* 190 */ int max_resist; // -- MAX_RESIST
/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME
/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME
/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT
/* 193 */ int nimbus_effect; // -- DURATION_PARTICLE_EFFECT
/* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE
/* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE
/* 196 */ bool sneak; // effect can only be used if sneaking (rogue 'Daggerfall' ect) -- SNEAK_ATTACK
@@ -1339,35 +1358,35 @@ struct SPDat_Spell_Struct
/* 199 */ //bool show_wear_off_message; // -- SHOW_WEAR_OFF_MESSAGE
/* 200 */ bool suspendable; // buff is suspended in suspended buff zones -- IS_COUNTDOWN_HELD
/* 201 */ int viral_range; // -- SPREAD_RADIUS
/* 202 */ int songcap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP
/* 202 */ int song_cap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP
/* 203 */ //bool stacks_with_self; // -- STACKS_WITH_SELF
/* 204 */ //int not_shown_to_player; // client skips this -- NOT_SHOWN_TO_PLAYER
/* 205 */ bool no_block; // -- NO_BUFF_BLOCK
/* 206 */ //int8 anim_variation; // -- ANIM_VARIATION
/* 207 */ int spellgroup; // -- SPELL_GROUP
/* 207 */ int spell_group; // -- SPELL_GROUP
/* 208 */ int rank; //increments AA effects with same name -- SPELL_GROUP_RANK
/* 209 */ int no_resist; //makes spells unresistable, which makes charms unbreakable as well. -- NO_RESIST
/* 210 */ // bool allow_spellscribe; // -- ALLOW_SPELLSCRIBE
/* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID
/* 212 */ bool AllowRest; // -- BYPASS_REGEN_CHECK
/* 213 */ bool InCombat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT
/* 214 */ bool OutofCombat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT
/* 211 */ int cast_restriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID
/* 212 */ bool allow_rest; // -- BYPASS_REGEN_CHECK
/* 213 */ bool can_cast_in_combat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT
/* 214 */ bool can_cast_out_of_combat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT
/* 215 */ //bool show_dot_message; // -- SHOW_DOT_MESSAGE
/* 216 */ //bool invalid; // -- INVALID
/* 217 */ int override_crit_chance; //Places a cap on the max chance to critical -- OVERRIDE_CRIT_CHANCE
/* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS
/* 218 */ int aoe_max_targets; //Is used for various AE effects -- MAX_TARGETS
/* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD
/* 220 */ int caster_requirement_id; // -- CASTER_REQUIREMENT_ID
/* 221 */ int spell_class; // -- SPELL_CLASS
/* 222 */ int spell_subclass; // -- SPELL_SUBCLASS
/* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS
/* 224 */ bool persistdeath; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH
/* 224 */ bool persist_death; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH
/* 225 */ //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE
/* 226 */ //float base_effects_focus_offset; // -- BASE_EFFECTS_FOCUS_OFFSET
/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST
/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT
/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST
/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT
/* 227 */ float min_distance; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST
/* 228 */ float min_distance_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT
/* 229 */ float max_distance; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST
/* 230 */ float max_distance_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT
/* The client also does this
* v26 = *(float *)&v4->DistanceModFarDist - *(float *)&v4->DistanceModCloseDist;
* if ( v26 > -0.00000011920929 && v26 < 0.00000011920929 )
@@ -1381,7 +1400,7 @@ struct SPDat_Spell_Struct
/* 234 */ //bool only_during_fast_regen; // -- ONLY_DURING_FAST_REGEN
/* 235 */ //bool is_beta_only; // -- IS_BETA_ONLY
/* 236 */ //int spell_subgroup; // -- SPELL_SUBGROUP
uint8 DamageShieldType; // This field does not exist in spells_us.txt
uint8 damage_shield_type; // This field does not exist in spells_us.txt
};
extern const SPDat_Spell_Struct* spells;
@@ -1461,6 +1480,7 @@ bool IsPartialDeathSaveSpell(uint16 spell_id);
bool IsShadowStepSpell(uint16 spell_id);
bool IsSuccorSpell(uint16 spell_id);
bool IsTeleportSpell(uint16 spell_id);
bool IsTranslocateSpell(uint16 spell_id);
bool IsGateSpell(uint16 spell_id);
bool IsPlayerIllusionSpell(uint16 spell_id); // seveian 2008-09-23
bool IsLDoNObjectSpell(uint16 spell_id);
@@ -1493,7 +1513,13 @@ bool IsBardOnlyStackEffect(int effect);
bool IsCastWhileInvis(uint16 spell_id);
bool IsEffectIgnoredInStacking(int spa);
bool IsFocusLimit(int spa);
bool SpellRequiresTarget(int targettype);
bool SpellRequiresTarget(int target_type);
bool IsVirusSpell(int32 spell_id);
int GetViralMinSpreadTime(int32 spell_id);
int GetViralMaxSpreadTime(int32 spell_id);
int GetViralSpreadRange(int32 spell_id);
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect);
uint32 GetProcLimitTimer(int32 spell_id, int proc_type);
int CalcPetHp(int levelb, int classb, int STA = 75);
int GetSpellEffectDescNum(uint16 spell_id);
+560 -146
View File
@@ -16,34 +16,36 @@
#include "string_util.h"
#include <algorithm>
#include <cctype>
#ifdef _WINDOWS
#include <windows.h>
#include <windows.h>
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#else
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#endif
#ifndef va_copy
#define va_copy(d,s) ((d) = (s))
#define va_copy(d,s) ((d) = (s))
#endif
// original source:
// https://github.com/facebook/folly/blob/master/folly/String.cpp
//
const std::string vStringFormat(const char* format, va_list args)
const std::string vStringFormat(const char *format, va_list args)
{
std::string output;
va_list tmpargs;
va_list tmpargs;
va_copy(tmpargs,args);
va_copy(tmpargs, args);
int characters_used = vsnprintf(nullptr, 0, format, tmpargs);
va_end(tmpargs);
@@ -51,7 +53,7 @@ const std::string vStringFormat(const char* format, va_list args)
if (characters_used > 0) {
output.resize(characters_used + 1);
va_copy(tmpargs,args);
va_copy(tmpargs, args);
characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs);
va_end(tmpargs);
@@ -59,8 +61,9 @@ const std::string vStringFormat(const char* format, va_list args)
// We shouldn't have a format error by this point, but I can't imagine what error we
// could have by this point. Still, return empty string;
if (characters_used < 0)
if (characters_used < 0) {
output.clear();
}
}
return output;
}
@@ -86,8 +89,9 @@ const std::string str_toupper(std::string s)
const std::string ucfirst(std::string s)
{
std::string output = s;
if (!s.empty())
if (!s.empty()) {
output[0] = static_cast<char>(::toupper(s[0]));
}
return output;
}
@@ -101,18 +105,20 @@ const std::string StringFormat(const char *format, ...)
return output;
}
std::vector<std::string> SplitString(const std::string &str, const char delim) {
std::vector<std::string> SplitString(const std::string &str, const char delim)
{
std::vector<std::string> ret;
std::string::size_type start = 0;
auto end = str.find(delim);
std::string::size_type start = 0;
auto end = str.find(delim);
while (end != std::string::npos) {
ret.emplace_back(str, start, end - start);
start = end + 1;
end = str.find(delim, start);
end = str.find(delim, start);
}
// this will catch the last word since the string is unlikely to end with a delimiter
if (str.length() > start)
if (str.length() > start) {
ret.emplace_back(str, start, str.length() - start);
}
return ret;
}
@@ -152,14 +158,16 @@ std::string get_between(const std::string &s, std::string start_delim, std::stri
return "";
}
std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator)
std::string::size_type
search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator)
{
// this shouldn't go out of bounds, even without obvious bounds checks
auto pos = haystack.find(needle);
while (pos != std::string::npos) {
auto c = haystack[pos + needle.length()];
if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator))
if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) {
return pos;
}
pos = haystack.find(needle, pos + needle.length());
}
return std::string::npos;
@@ -179,7 +187,7 @@ std::string implode(std::string glue, std::vector<std::string> src)
}
std::string final_output = output.str();
final_output.resize (output.str().size () - glue.size());
final_output.resize(output.str().size() - glue.size());
return final_output;
}
@@ -201,80 +209,83 @@ std::vector<std::string> wrap(std::vector<std::string> &src, std::string charact
return new_vector;
}
std::string EscapeString(const std::string &s) {
std::string EscapeString(const std::string &s)
{
std::string ret;
size_t sz = s.length();
for(size_t i = 0; i < sz; ++i) {
size_t sz = s.length();
for (size_t i = 0; i < sz; ++i) {
char c = s[i];
switch(c) {
case '\x00':
ret += "\\x00";
break;
case '\n':
ret += "\\n";
break;
case '\r':
ret += "\\r";
break;
case '\\':
ret += "\\\\";
break;
case '\'':
ret += "\\'";
break;
case '\"':
ret += "\\\"";
break;
case '\x1a':
ret += "\\x1a";
break;
default:
ret.push_back(c);
break;
switch (c) {
case '\x00':
ret += "\\x00";
break;
case '\n':
ret += "\\n";
break;
case '\r':
ret += "\\r";
break;
case '\\':
ret += "\\\\";
break;
case '\'':
ret += "\\'";
break;
case '\"':
ret += "\\\"";
break;
case '\x1a':
ret += "\\x1a";
break;
default:
ret.push_back(c);
break;
}
}
return ret;
}
std::string EscapeString(const char *src, size_t sz) {
std::string EscapeString(const char *src, size_t sz)
{
std::string ret;
for(size_t i = 0; i < sz; ++i) {
for (size_t i = 0; i < sz; ++i) {
char c = src[i];
switch(c) {
case '\x00':
ret += "\\x00";
break;
case '\n':
ret += "\\n";
break;
case '\r':
ret += "\\r";
break;
case '\\':
ret += "\\\\";
break;
case '\'':
ret += "\\'";
break;
case '\"':
ret += "\\\"";
break;
case '\x1a':
ret += "\\x1a";
break;
default:
ret.push_back(c);
break;
switch (c) {
case '\x00':
ret += "\\x00";
break;
case '\n':
ret += "\\n";
break;
case '\r':
ret += "\\r";
break;
case '\\':
ret += "\\\\";
break;
case '\'':
ret += "\\'";
break;
case '\"':
ret += "\\\"";
break;
case '\x1a':
ret += "\\x1a";
break;
default:
ret.push_back(c);
break;
}
}
return ret;
}
bool StringIsNumber(const std::string &s) {
bool StringIsNumber(const std::string &s)
{
try {
auto r = stod(s);
return true;
@@ -284,15 +295,18 @@ bool StringIsNumber(const std::string &s) {
}
}
void ToLowerString(std::string &s) {
void ToLowerString(std::string &s)
{
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
}
void ToUpperString(std::string &s) {
void ToUpperString(std::string &s)
{
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
}
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim) {
std::string JoinString(const std::vector<std::string> &ar, const std::string &delim)
{
std::string ret;
for (size_t i = 0; i < ar.size(); ++i) {
if (i != 0) {
@@ -312,7 +326,7 @@ void find_replace(std::string &string_subject, const std::string &search_string,
}
size_t start_pos = 0;
while((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) {
while ((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) {
string_subject.replace(start_pos, search_string.length(), replace_string);
start_pos += replace_string.length();
}
@@ -333,9 +347,9 @@ void ParseAccountString(const std::string &s, std::string &account, std::string
auto split = SplitString(s, ':');
if (split.size() == 2) {
loginserver = split[0];
account = split[1];
account = split[1];
}
else if(split.size() == 1) {
else if (split.size() == 1) {
account = split[0];
}
}
@@ -344,9 +358,11 @@ void ParseAccountString(const std::string &s, std::string &account, std::string
// normal strncpy doesnt put a null term on copied strings, this one does
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
char* strn0cpy(char* dest, const char* source, uint32 size) {
if (!dest)
char *strn0cpy(char *dest, const char *source, uint32 size)
{
if (!dest) {
return 0;
}
if (size == 0 || source == 0) {
dest[0] = 0;
return dest;
@@ -358,123 +374,159 @@ char* strn0cpy(char* dest, const char* source, uint32 size) {
// String N w/null Copy Truncated?
// return value =true if entire string(source) fit, false if it was truncated
bool strn0cpyt(char* dest, const char* source, uint32 size) {
if (!dest)
bool strn0cpyt(char *dest, const char *source, uint32 size)
{
if (!dest) {
return 0;
}
if (size == 0 || source == 0) {
dest[0] = 0;
return false;
}
strncpy(dest, source, size);
dest[size - 1] = 0;
return (bool)(source[strlen(dest)] == 0);
return (bool) (source[strlen(dest)] == 0);
}
const char *MakeLowerString(const char *source) {
const char *MakeLowerString(const char *source)
{
static char str[128];
if (!source)
if (!source) {
return nullptr;
}
MakeLowerString(source, str);
return str;
}
void MakeLowerString(const char *source, char *target) {
void MakeLowerString(const char *source, char *target)
{
if (!source || !target) {
*target = 0;
return;
}
while (*source)
{
while (*source) {
*target = tolower(*source);
target++; source++;
target++;
source++;
}
*target = 0;
}
uint32 hextoi(const char* num) {
if (num == nullptr)
uint32 hextoi(const char *num)
{
if (num == nullptr) {
return 0;
}
int len = strlen(num);
if (len < 3)
if (len < 3) {
return 0;
}
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) {
return 0;
}
uint32 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
uint32 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F') {
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
}
else if (num[i] >= 'a' && num[i] <= 'f') {
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
}
else if (num[i] >= '0' && num[i] <= '9') {
ret += (num[i] - '0') * mul;
else
}
else {
return 0;
}
mul *= 16;
}
return ret;
}
uint64 hextoi64(const char* num) {
if (num == nullptr)
uint64 hextoi64(const char *num)
{
if (num == nullptr) {
return 0;
}
int len = strlen(num);
if (len < 3)
if (len < 3) {
return 0;
}
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) {
return 0;
}
uint64 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F')
uint64 ret = 0;
int mul = 1;
for (int i = len - 1; i >= 2; i--) {
if (num[i] >= 'A' && num[i] <= 'F') {
ret += ((num[i] - 'A') + 10) * mul;
else if (num[i] >= 'a' && num[i] <= 'f')
}
else if (num[i] >= 'a' && num[i] <= 'f') {
ret += ((num[i] - 'a') + 10) * mul;
else if (num[i] >= '0' && num[i] <= '9')
}
else if (num[i] >= '0' && num[i] <= '9') {
ret += (num[i] - '0') * mul;
else
}
else {
return 0;
}
mul *= 16;
}
return ret;
}
bool atobool(const char* iBool) {
bool atobool(const char *iBool)
{
if (iBool == nullptr)
if (iBool == nullptr) {
return false;
if (!strcasecmp(iBool, "true"))
}
if (!strcasecmp(iBool, "true")) {
return true;
if (!strcasecmp(iBool, "false"))
}
if (!strcasecmp(iBool, "false")) {
return false;
if (!strcasecmp(iBool, "yes"))
}
if (!strcasecmp(iBool, "yes")) {
return true;
if (!strcasecmp(iBool, "no"))
}
if (!strcasecmp(iBool, "no")) {
return false;
if (!strcasecmp(iBool, "on"))
}
if (!strcasecmp(iBool, "on")) {
return true;
if (!strcasecmp(iBool, "off"))
}
if (!strcasecmp(iBool, "off")) {
return false;
if (!strcasecmp(iBool, "enable"))
}
if (!strcasecmp(iBool, "enable")) {
return true;
if (!strcasecmp(iBool, "disable"))
}
if (!strcasecmp(iBool, "disable")) {
return false;
if (!strcasecmp(iBool, "enabled"))
}
if (!strcasecmp(iBool, "enabled")) {
return true;
if (!strcasecmp(iBool, "disabled"))
}
if (!strcasecmp(iBool, "disabled")) {
return false;
if (!strcasecmp(iBool, "y"))
}
if (!strcasecmp(iBool, "y")) {
return true;
if (!strcasecmp(iBool, "n"))
}
if (!strcasecmp(iBool, "n")) {
return false;
if (atoi(iBool))
}
if (atoi(iBool)) {
return true;
}
return false;
}
@@ -483,21 +535,19 @@ char *CleanMobName(const char *in, char *out)
{
unsigned i, j;
for (i = j = 0; i < strlen(in); i++)
{
for (i = j = 0; i < strlen(in); i++) {
// convert _ to space.. any other conversions like this? I *think* this
// is the only non alpha char that's not stripped but converted.
if (in[i] == '_')
{
if (in[i] == '_') {
out[j++] = ' ';
}
else
{
if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
else {
if (isalpha(in[i]) || (in[i] == '`')) { // numbers, #, or any other crap just gets skipped
out[j++] = in[i];
}
}
}
out[j] = 0; // terimnate the string before returning it
out[j] = 0; // terimnate the string before returning it
return out;
}
@@ -505,8 +555,9 @@ char *CleanMobName(const char *in, char *out)
void RemoveApostrophes(std::string &s)
{
for (unsigned int i = 0; i < s.length(); ++i)
if (s[i] == '\'')
if (s[i] == '\'') {
s[i] = '_';
}
}
char *RemoveApostrophes(const char *s)
@@ -516,8 +567,9 @@ char *RemoveApostrophes(const char *s)
strcpy(NewString, s);
for (unsigned int i = 0; i < strlen(NewString); ++i)
if (NewString[i] == '\'')
if (NewString[i] == '\'') {
NewString[i] = '_';
}
return NewString;
}
@@ -536,11 +588,12 @@ const char *ConvertArrayF(float input, char *returnchar)
bool isAlphaNumeric(const char *text)
{
for (unsigned int charIndex = 0; charIndex<strlen(text); charIndex++) {
for (unsigned int charIndex = 0; charIndex < strlen(text); charIndex++) {
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
(text[charIndex] < '0' || text[charIndex] > '9'))
(text[charIndex] < '0' || text[charIndex] > '9')) {
return false;
}
}
return true;
@@ -595,13 +648,374 @@ std::string numberToWords(unsigned long long int n)
}
// first letter capitalized and rest made lower case
std::string FormatName(const std::string& char_name)
std::string FormatName(const std::string &char_name)
{
std::string formatted(char_name);
if (!formatted.empty())
{
if (!formatted.empty()) {
std::transform(formatted.begin(), formatted.end(), formatted.begin(), ::tolower);
formatted[0] = ::toupper(formatted[0]);
}
return formatted;
}
bool IsAllowedWorldServerCharacterList(char c)
{
const char *valid_characters = ":[](){}.!@#$%^&*-=+<>/\\|'\"";
if (strchr(valid_characters, c)) {
return true;
}
return false;
}
void SanitizeWorldServerName(char *name)
{
std::string server_long_name = name;
strcpy(name, SanitizeWorldServerName(server_long_name).c_str());
}
std::string SanitizeWorldServerName(std::string server_long_name)
{
server_long_name.erase(
std::remove_if(
server_long_name.begin(),
server_long_name.end(),
[](char c) {
return !(std::isalpha(c) || std::isalnum(c) || std::isspace(c) || IsAllowedWorldServerCharacterList(c));
}
), server_long_name.end()
);
server_long_name = trim(server_long_name);
// bad word filter
for (auto &piece: split_string(server_long_name, " ")) {
for (auto &word: GetBadWords()) {
// for shorter words that can actually be part of legitimate words
// make sure that it isn't part of another word by matching on a space
if (str_tolower(piece) == word) {
find_replace(
server_long_name,
piece,
repeat("*", (int) word.length())
);
continue;
}
auto pos = str_tolower(piece).find(word);
if (str_tolower(piece).find(word) != std::string::npos && piece.length() > 4 && word.length() > 4) {
auto found_word = piece.substr(pos, word.length());
std::string replaced_piece = piece.substr(pos, word.length());
find_replace(
server_long_name,
replaced_piece,
repeat("*", (int) word.length())
);
}
}
}
return server_long_name;
}
std::string repeat(std::string s, int n)
{
std::string s1 = s;
for (int i = 1; i < n; i++) {
s += s1;
}
return s;
}
std::vector<std::string> GetBadWords()
{
return std::vector<std::string>{
"2g1c",
"acrotomophilia",
"anal",
"anilingus",
"anus",
"apeshit",
"arsehole",
"ass",
"asshole",
"assmunch",
"autoerotic",
"babeland",
"bangbros",
"bangbus",
"bareback",
"barenaked",
"bastard",
"bastardo",
"bastinado",
"bbw",
"bdsm",
"beaner",
"beaners",
"beaver",
"beastiality",
"bestiality",
"bimbos",
"birdlock",
"bitch",
"bitches",
"blowjob",
"blumpkin",
"bollocks",
"bondage",
"boner",
"boob",
"boobs",
"bukkake",
"bulldyke",
"bullshit",
"bung",
"bunghole",
"busty",
"butt",
"buttcheeks",
"butthole",
"camel toe",
"camgirl",
"camslut",
"camwhore",
"carpetmuncher",
"cialis",
"circlejerk",
"clit",
"clitoris",
"clusterfuck",
"cock",
"cocks",
"coprolagnia",
"coprophilia",
"cornhole",
"coon",
"coons",
"creampie",
"cum",
"cumming",
"cumshot",
"cumshots",
"cunnilingus",
"cunt",
"darkie",
"daterape",
"deepthroat",
"dendrophilia",
"dick",
"dildo",
"dingleberry",
"dingleberries",
"doggiestyle",
"doggystyle",
"dolcett",
"domination",
"dominatrix",
"dommes",
"hump",
"dvda",
"ecchi",
"ejaculation",
"erotic",
"erotism",
"escort",
"eunuch",
"fag",
"faggot",
"fecal",
"felch",
"fellatio",
"feltch",
"femdom",
"figging",
"fingerbang",
"fingering",
"fisting",
"footjob",
"frotting",
"fuck",
"fuckin",
"fucking",
"fucktards",
"fudgepacker",
"futanari",
"gangbang",
"gangbang",
"gaysex",
"genitals",
"goatcx",
"goatse",
"gokkun",
"goodpoop",
"goregasm",
"grope",
"g-spot",
"guro",
"handjob",
"hentai",
"homoerotic",
"honkey",
"hooker",
"horny",
"humping",
"incest",
"intercourse",
"jailbait",
"jigaboo",
"jiggaboo",
"jiggerboo",
"jizz",
"juggs",
"kike",
"kinbaku",
"kinkster",
"kinky",
"knobbing",
"livesex",
"lolita",
"lovemaking",
"masturbate",
"masturbating",
"masturbation",
"milf",
"mong",
"motherfucker",
"muffdiving",
"nambla",
"nawashi",
"negro",
"neonazi",
"nigga",
"nigger",
"nimphomania",
"nipple",
"nipples",
"nsfw",
"nude",
"nudity",
"nutten",
"nympho",
"nymphomania",
"octopussy",
"omorashi",
"orgasm",
"orgy",
"paedophile",
"paki",
"panties",
"panty",
"pedobear",
"pedophile",
"pegging",
"penis",
"pikey",
"pissing",
"pisspig",
"playboy",
"ponyplay",
"poof",
"poon",
"poontang",
"punany",
"poopchute",
"porn",
"porno",
"pornography",
"pthc",
"pubes",
"pussy",
"queaf",
"queef",
"quim",
"raghead",
"rape",
"raping",
"rapist",
"rectum",
"rimjob",
"rimming",
"sadism",
"santorum",
"scat",
"schlong",
"scissoring",
"semen",
"sex",
"sexcam",
"sexo",
"sexy",
"sexual",
"sexually",
"sexuality",
"shemale",
"shibari",
"shit",
"shitblimp",
"shitty",
"shota",
"shrimping",
"skeet",
"slanteye",
"slut",
"s&m",
"smut",
"snatch",
"snowballing",
"sodomize",
"sodomy",
"spastic",
"spic",
"splooge",
"spooge",
"spunk",
"strapon",
"strappado",
"suck",
"sucks",
"swastika",
"swinger",
"threesome",
"throating",
"thumbzilla",
"tight white",
"tit",
"tits",
"titties",
"titty",
"topless",
"tosser",
"towelhead",
"tranny",
"tribadism",
"tubgirl",
"tushy",
"twat",
"twink",
"twinkie",
"undressing",
"upskirt",
"urophilia",
"vagina",
"viagra",
"vibrator",
"vorarephilia",
"voyeur",
"voyeurweb",
"voyuer",
"vulva",
"wank",
"wetback",
"whore",
"worldsex",
"xx",
"xxx",
"yaoi",
"yiffy",
"zoophilia"
};
}
+5
View File
@@ -208,6 +208,11 @@ void RemoveApostrophes(std::string &s);
std::string convert2digit(int n, std::string suffix);
std::string numberToWords(unsigned long long int n);
std::string FormatName(const std::string& char_name);
bool IsAllowedWorldServerCharacterList(char c);
void SanitizeWorldServerName(char *name);
std::string SanitizeWorldServerName(std::string server_long_name);
std::string repeat(std::string s, int n);
std::vector<std::string> GetBadWords();
template<typename InputIterator, typename OutputIterator>
auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
+1 -1
View File
@@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9172
#define CURRENT_BINARY_DATABASE_VERSION 9174
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028
+1 -1
View File
@@ -22,7 +22,7 @@ SET(eqlogin_headers
loginserver_command_handler.h
loginserver_webserver.h
login_server.h
login_structures.h
login_types.h
options.h
server_manager.h
world_server.h
+138 -30
View File
@@ -1,28 +1,9 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "account_management.h"
#include "login_server.h"
#include "../common/event/task_scheduler.h"
#include "../common/event/event_loop.h"
#include "../common/net/dns.h"
#include "../common/string_util.h"
extern LoginServer server;
EQ::Event::TaskScheduler task_runner;
@@ -300,6 +281,8 @@ bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordById(
return updated_account;
}
constexpr int REQUEST_TIMEOUT_MS = 1500;
/**
* @param in_account_username
* @param in_account_password
@@ -395,19 +378,33 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
}
);
EQ::Net::DNSLookup(
"login.eqemulator.net", 5999, false, [&](const std::string &addr) {
if (addr.empty()) {
ret = 0;
running = false;
}
auto s = SplitString(server.options.GetEQEmuLoginServerAddress(), ':');
if (s.size() == 2) {
auto address = s[0];
auto port = std::stoi(s[1]);
mgr.Connect(addr, 5999);
}
);
EQ::Net::DNSLookup(
address, port, false, [&](const std::string &addr) {
if (addr.empty()) {
ret = 0;
running = false;
}
mgr.Connect(addr, port);
}
);
}
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
auto &loop = EQ::EventLoop::Get();
while (running) {
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > REQUEST_TIMEOUT_MS) {
LogInfo("[CheckExternalLoginserverUserCredentials] Deadline exceeded [{}]", REQUEST_TIMEOUT_MS);
running = false;
}
loop.Process();
}
@@ -416,4 +413,115 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
);
return res.get();
}
}
uint32 AccountManagement::HealthCheckUserLogin()
{
std::string in_account_username = "healthcheckuser";
std::string in_account_password = "healthcheckpassword";
auto res = task_runner.Enqueue(
[&]() -> uint32 {
bool running = true;
uint32 ret = 0;
EQ::Net::DaybreakConnectionManager mgr;
std::shared_ptr<EQ::Net::DaybreakConnection> c;
mgr.OnNewConnection(
[&](std::shared_ptr<EQ::Net::DaybreakConnection> connection) {
c = connection;
}
);
mgr.OnConnectionStateChange(
[&](
std::shared_ptr<EQ::Net::DaybreakConnection> conn,
EQ::Net::DbProtocolStatus from,
EQ::Net::DbProtocolStatus to
) {
if (EQ::Net::StatusConnected == to) {
EQ::Net::DynamicPacket p;
p.PutUInt16(0, 1); //OP_SessionReady
p.PutUInt32(2, 2);
c->QueuePacket(p);
}
else if (EQ::Net::StatusDisconnected == to) {
running = false;
}
}
);
mgr.OnPacketRecv(
[&](std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p) {
auto opcode = p.GetUInt16(0);
switch (opcode) {
case 0x0017: //OP_ChatMessage
{
size_t buffer_len =
in_account_username.length() + in_account_password.length() + 2;
std::unique_ptr<char[]> buffer(new char[buffer_len]);
strcpy(&buffer[0], in_account_username.c_str());
strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str());
size_t encrypted_len = buffer_len;
if (encrypted_len % 8 > 0) {
encrypted_len = ((encrypted_len / 8) + 1) * 8;
}
EQ::Net::DynamicPacket p;
p.Resize(12 + encrypted_len);
p.PutUInt16(0, 2); //OP_Login
p.PutUInt32(2, 3);
eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true);
c->QueuePacket(p);
break;
}
case 0x0018: {
auto encrypt_size = p.Length() - 12;
if (encrypt_size % 8 > 0) {
encrypt_size = (encrypt_size / 8) * 8;
}
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false);
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
auto response_error = sp.GetUInt16(1);
{
// we only care to see the response code
ret = response_error;
running = false;
}
break;
}
}
}
);
mgr.Connect("127.0.0.1", 5999);
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
auto &loop = EQ::EventLoop::Get();
while (running) {
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > 2000) {
ret = 0;
running = false;
}
loop.Process();
}
return ret;
}
);
return res.get();
}
+2 -19
View File
@@ -1,22 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_ACCOUNT_MANAGEMENT_H
#define EQEMU_ACCOUNT_MANAGEMENT_H
@@ -108,6 +89,8 @@ public:
uint32 in_account_id,
const std::string &in_account_password_hash
);
static uint32 HealthCheckUserLogin();
};
+150 -199
View File
@@ -1,29 +1,10 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "client.h"
#include "login_server.h"
#include "../common/misc_functions.h"
#include "../common/eqemu_logsys.h"
#include "../common/string_util.h"
#include "encryption.h"
#include "account_management.h"
extern LoginServer server;
@@ -33,17 +14,17 @@ extern LoginServer server;
*/
Client::Client(std::shared_ptr<EQStreamInterface> c, LSClientVersion v)
{
connection = c;
version = v;
status = cs_not_sent_session_ready;
account_id = 0;
play_server_id = 0;
play_sequence_id = 0;
m_connection = c;
m_client_version = v;
m_client_status = cs_not_sent_session_ready;
m_account_id = 0;
m_play_server_id = 0;
m_play_sequence_id = 0;
}
bool Client::Process()
{
EQApplicationPacket *app = connection->PopPacket();
EQApplicationPacket *app = m_connection->PopPacket();
while (app) {
if (server.options.IsTraceOn()) {
LogDebug("Application packet received from client (size {0})", app->Size());
@@ -53,9 +34,9 @@ bool Client::Process()
DumpPacket(app);
}
if (status == cs_failed_to_login) {
if (m_client_status == cs_failed_to_login) {
delete app;
app = connection->PopPacket();
app = m_connection->PopPacket();
continue;
}
@@ -112,7 +93,7 @@ bool Client::Process()
}
delete app;
app = connection->PopPacket();
app = m_connection->PopPacket();
}
return true;
@@ -126,7 +107,7 @@ bool Client::Process()
*/
void Client::Handle_SessionReady(const char *data, unsigned int size)
{
if (status != cs_not_sent_session_ready) {
if (m_client_status != cs_not_sent_session_ready) {
LogError("Session ready received again after already being received");
return;
}
@@ -136,39 +117,23 @@ void Client::Handle_SessionReady(const char *data, unsigned int size)
return;
}
status = cs_waiting_for_login;
m_client_status = cs_waiting_for_login;
/**
* The packets are mostly the same but slightly different between the two versions
* The packets are identical between the two versions
*/
if (version == cv_sod) {
auto *outapp = new EQApplicationPacket(OP_ChatMessage, 17);
outapp->pBuffer[0] = 0x02;
outapp->pBuffer[10] = 0x01;
outapp->pBuffer[11] = 0x65;
auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply_Struct));
auto buf = reinterpret_cast<LoginHandShakeReply_Struct*>(outapp->pBuffer);
buf->base_header.sequence = 0x02;
buf->base_reply.success = true;
buf->base_reply.error_str_id = 0x65; // 101 "No Error"
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(outapp);
}
connection->QueuePacket(outapp);
delete outapp;
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(outapp);
}
else {
const char *msg = "ChatMessage";
auto *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg));
outapp->pBuffer[0] = 0x02;
outapp->pBuffer[10] = 0x01;
outapp->pBuffer[11] = 0x65;
strcpy((char *) (outapp->pBuffer + 15), msg);
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(outapp);
}
connection->QueuePacket(outapp);
delete outapp;
}
m_connection->QueuePacket(outapp);
delete outapp;
}
/**
@@ -179,19 +144,23 @@ void Client::Handle_SessionReady(const char *data, unsigned int size)
*/
void Client::Handle_Login(const char *data, unsigned int size)
{
if (status != cs_waiting_for_login) {
if (m_client_status != cs_waiting_for_login) {
LogError("Login received after already having logged in");
return;
}
if ((size - 12) % 8 != 0) {
LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size);
// login user/pass are variable length after unencrypted opcode and base message header (size includes opcode)
constexpr int header_size = sizeof(uint16_t) + sizeof(LoginBaseMessage_Struct);
int data_size = size - header_size;
if (size <= header_size) {
LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size);
return;
}
if (size < sizeof(LoginLoginRequest_Struct)) {
LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size);
if (data_size % 8 != 0) {
LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size);
return;
}
@@ -208,13 +177,14 @@ void Client::Handle_Login(const char *data, unsigned int size)
std::string db_account_password_hash;
std::string outbuffer;
outbuffer.resize(size - 12);
outbuffer.resize(data_size);
if (outbuffer.length() == 0) {
LogError("Corrupt buffer sent to server, no length");
return;
}
auto r = eqcrypt_block(data + 10, size - 12, &outbuffer[0], 0);
// data starts at base message header (opcode not included)
auto r = eqcrypt_block(data + sizeof(LoginBaseMessage_Struct), data_size, &outbuffer[0], 0);
if (r == nullptr) {
LogError("Failed to decrypt eqcrypt block");
return;
@@ -228,7 +198,8 @@ void Client::Handle_Login(const char *data, unsigned int size)
return;
}
memcpy(&llrs, data, sizeof(LoginLoginRequest_Struct));
// only need to copy the base header for reply options, ignore login info
memcpy(&m_llrs, data, sizeof(LoginBaseMessage_Struct));
bool result = false;
if (outbuffer[0] == 0 && outbuffer[1] == 0) {
@@ -236,7 +207,7 @@ void Client::Handle_Login(const char *data, unsigned int size)
cred = (&outbuffer[2 + user.length()]);
result = server.db->GetLoginTokenDataFromToken(
cred,
connection->GetRemoteAddr(),
m_connection->GetRemoteAddr(),
db_account_id,
db_loginserver,
user
@@ -252,11 +223,16 @@ void Client::Handle_Login(const char *data, unsigned int size)
user = components[1];
}
// health checks
if (ProcessHealthCheck(user)) {
DoFailedLogin();
return;
}
LogInfo(
"Attempting password based login [{0}] login [{1}] user [{2}]",
"Attempting password based login [{0}] login [{1}]",
user,
db_loginserver,
user
db_loginserver
);
ParseAccountString(user, user, db_loginserver);
@@ -267,7 +243,7 @@ void Client::Handle_Login(const char *data, unsigned int size)
LogDebug("[VerifyLoginHash] Success [{0}]", (result ? "true" : "false"));
}
else {
status = cs_creating_account;
m_client_status = cs_creating_account;
AttemptLoginAccountCreation(user, cred, db_loginserver);
return;
@@ -305,14 +281,14 @@ void Client::Handle_Login(const char *data, unsigned int size)
*/
void Client::Handle_Play(const char *data)
{
if (status != cs_logged_in) {
if (m_client_status != cs_logged_in) {
LogError("Client sent a play request when they were not logged in, discarding");
return;
}
const auto *play = (const PlayEverquestRequest_Struct *) data;
auto server_id_in = (unsigned int) play->ServerNumber;
auto sequence_in = (unsigned int) play->Sequence;
auto server_id_in = (unsigned int) play->server_number;
auto sequence_in = (unsigned int) play->base_header.sequence;
if (server.options.IsTraceOn()) {
LogInfo(
@@ -323,10 +299,10 @@ void Client::Handle_Play(const char *data)
);
}
this->play_server_id = (unsigned int) play->ServerNumber;
play_sequence_id = sequence_in;
play_server_id = server_id_in;
server.server_manager->SendUserToWorldRequest(server_id_in, account_id, loginserver_name);
m_play_server_id = (unsigned int) play->server_number;
m_play_sequence_id = sequence_in;
m_play_server_id = server_id_in;
server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name);
}
/**
@@ -334,14 +310,13 @@ void Client::Handle_Play(const char *data)
*/
void Client::SendServerListPacket(uint32 seq)
{
EQApplicationPacket *outapp = server.server_manager->CreateServerListPacket(this, seq);
auto outapp = server.server_manager->CreateServerListPacket(this, seq);
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(outapp);
DumpPacket(outapp.get());
}
connection->QueuePacket(outapp);
delete outapp;
m_connection->QueuePacket(outapp.get());
}
void Client::SendPlayResponse(EQApplicationPacket *outapp)
@@ -350,12 +325,12 @@ void Client::SendPlayResponse(EQApplicationPacket *outapp)
LogDebug("Sending play response for {0}", GetAccountName());
// server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size);
}
connection->QueuePacket(outapp);
m_connection->QueuePacket(outapp);
}
void Client::GenerateKey()
{
key.clear();
m_key.clear();
int count = 0;
while (count < 10) {
static const char key_selection[] =
@@ -367,7 +342,7 @@ void Client::GenerateKey()
'6', '7', '8', '9'
};
key.append((const char *) &key_selection[random.Int(0, 35)], 1);
m_key.append((const char *) &key_selection[m_random.Int(0, 35)], 1);
count++;
}
}
@@ -383,6 +358,8 @@ void Client::AttemptLoginAccountCreation(
const std::string &loginserver
)
{
LogInfo("[AttemptLoginAccountCreation] user [{}] loginserver [{}]", user, loginserver);
#ifdef LSPX
if (loginserver == "eqemu") {
LogInfo("Attempting login account creation via '{0}'", loginserver);
@@ -393,59 +370,19 @@ void Client::AttemptLoginAccountCreation(
return;
}
if (server.options.GetEQEmuLoginServerAddress().length() == 0) {
DoFailedLogin();
return;
}
auto addr_components = SplitString(server.options.GetEQEmuLoginServerAddress(), ':');
if (addr_components.size() != 2) {
DoFailedLogin();
return;
}
stored_user = user;
stored_pass = pass;
auto address = addr_components[0];
auto port = std::stoi(addr_components[1]);
EQ::Net::DNSLookup(
address, port, false, [=](const std::string &addr) {
if (addr.empty()) {
DoFailedLogin();
return;
}
login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
login_connection_manager->OnNewConnection(
std::bind(
&Client::LoginOnNewConnection,
this,
std::placeholders::_1
)
);
login_connection_manager->OnConnectionStateChange(
std::bind(
&Client::LoginOnStatusChange,
this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3
)
);
login_connection_manager->OnPacketRecv(
std::bind(
&Client::LoginOnPacketRecv,
this,
std::placeholders::_1,
std::placeholders::_2
)
);
login_connection_manager->Connect(addr, port);
}
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(
user,
pass
);
if (account_id > 0) {
LogInfo("[AttemptLoginAccountCreation] Found and creating eqemu account [{}]", account_id);
CreateEQEmuAccount(user, pass, account_id);
return;
}
DoFailedLogin();
return;
}
#endif
@@ -461,26 +398,36 @@ void Client::AttemptLoginAccountCreation(
void Client::DoFailedLogin()
{
stored_user.clear();
stored_pass.clear();
m_stored_user.clear();
m_stored_pass.clear();
EQApplicationPacket outapp(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct));
auto *login_failed = (LoginLoginFailed_Struct *) outapp.pBuffer;
// unencrypted
LoginBaseMessage_Struct base_header{};
base_header.sequence = m_llrs.sequence; // login (3)
base_header.encrypt_type = m_llrs.encrypt_type;
login_failed->unknown1 = llrs.unknown1;
login_failed->unknown2 = llrs.unknown2;
login_failed->unknown3 = llrs.unknown3;
login_failed->unknown4 = llrs.unknown4;
login_failed->unknown5 = llrs.unknown5;
// encrypted
PlayerLoginReply_Struct login_reply{};
login_reply.base_reply.success = false;
login_reply.base_reply.error_str_id = 105; // Error - The username and/or password were not valid
memcpy(login_failed->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData));
char encrypted_buffer[80] = {0};
auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1);
if (rc == nullptr) {
LogDebug("Failed to encrypt eqcrypt block for failed login");
}
constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer);
EQApplicationPacket outapp(OP_LoginAccepted, outsize);
outapp.WriteData(&base_header, sizeof(base_header));
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(&outapp);
}
connection->QueuePacket(&outapp);
status = cs_failed_to_login;
m_connection->QueuePacket(&outapp);
m_client_status = cs_failed_to_login;
}
/**
@@ -576,68 +523,64 @@ void Client::DoSuccessfulLogin(
const std::string &db_loginserver
)
{
stored_user.clear();
stored_pass.clear();
m_stored_user.clear();
m_stored_pass.clear();
server.client_manager->RemoveExistingClient(db_account_id, db_loginserver);
in_addr in{};
in.s_addr = connection->GetRemoteIP();
in.s_addr = m_connection->GetRemoteIP();
server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in)));
GenerateKey();
account_id = db_account_id;
account_name = in_account_name;
loginserver_name = db_loginserver;
m_account_id = db_account_id;
m_account_name = in_account_name;
m_loginserver_name = db_loginserver;
auto *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80);
auto *login_accepted = (LoginAccepted_Struct *) outapp->pBuffer;
login_accepted->unknown1 = llrs.unknown1;
login_accepted->unknown2 = llrs.unknown2;
login_accepted->unknown3 = llrs.unknown3;
login_accepted->unknown4 = llrs.unknown4;
login_accepted->unknown5 = llrs.unknown5;
// unencrypted
LoginBaseMessage_Struct base_header{};
base_header.sequence = m_llrs.sequence;
base_header.compressed = false;
base_header.encrypt_type = m_llrs.encrypt_type;
base_header.unk3 = m_llrs.unk3;
auto *login_failed_attempts = new LoginFailedAttempts_Struct;
memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct));
login_failed_attempts->failed_attempts = 0;
login_failed_attempts->message = 0x01;
login_failed_attempts->lsid = db_account_id;
login_failed_attempts->unknown3[3] = 0x03;
login_failed_attempts->unknown4[3] = 0x02;
login_failed_attempts->unknown5[0] = 0xe7;
login_failed_attempts->unknown5[1] = 0x03;
login_failed_attempts->unknown6[0] = 0xff;
login_failed_attempts->unknown6[1] = 0xff;
login_failed_attempts->unknown6[2] = 0xff;
login_failed_attempts->unknown6[3] = 0xff;
login_failed_attempts->unknown7[0] = 0xa0;
login_failed_attempts->unknown7[1] = 0x05;
login_failed_attempts->unknown8[3] = 0x02;
login_failed_attempts->unknown9[0] = 0xff;
login_failed_attempts->unknown9[1] = 0x03;
login_failed_attempts->unknown11[0] = 0x63;
login_failed_attempts->unknown12[0] = 0x01;
memcpy(login_failed_attempts->key, key.c_str(), key.size());
// not serializing any of the variable length strings so just use struct directly
PlayerLoginReply_Struct login_reply{};
login_reply.base_reply.success = true;
login_reply.base_reply.error_str_id = 101; // No Error
login_reply.unk1 = 0;
login_reply.unk2 = 0;
login_reply.lsid = db_account_id;
login_reply.failed_attempts = 0;
login_reply.show_player_count = server.options.IsShowPlayerCountEnabled();
login_reply.offer_min_days = 99;
login_reply.offer_min_views = -1;
login_reply.offer_cooldown_minutes = 0;
login_reply.web_offer_number = 0;
login_reply.web_offer_min_days = 99;
login_reply.web_offer_min_views = -1;
login_reply.web_offer_cooldown_minutes = 0;
memcpy(login_reply.key, m_key.c_str(), m_key.size());
char encrypted_buffer[80] = {0};
auto rc = eqcrypt_block((const char *) login_failed_attempts, 75, encrypted_buffer, 1);
auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1);
if (rc == nullptr) {
LogDebug("Failed to encrypt eqcrypt block");
}
memcpy(login_accepted->encrypt, encrypted_buffer, 80);
constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer);
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
outapp->WriteData(&base_header, sizeof(base_header));
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
if (server.options.IsDumpOutPacketsOn()) {
DumpPacket(outapp);
DumpPacket(outapp.get());
}
connection->QueuePacket(outapp);
delete outapp;
m_connection->QueuePacket(outapp.get());
status = cs_logged_in;
m_client_status = cs_logged_in;
}
/**
@@ -689,7 +632,7 @@ void Client::CreateEQEmuAccount(
*/
void Client::LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
{
login_connection = connection;
m_login_connection = connection;
}
/**
@@ -751,16 +694,16 @@ void Client::LoginSendSessionReady()
p.PutUInt16(0, 1); //OP_SessionReady
p.PutUInt32(2, 2);
login_connection->QueuePacket(p);
m_login_connection->QueuePacket(p);
}
void Client::LoginSendLogin()
{
size_t buffer_len = stored_user.length() + stored_pass.length() + 2;
size_t buffer_len = m_stored_user.length() + m_stored_pass.length() + 2;
std::unique_ptr<char[]> buffer(new char[buffer_len]);
strcpy(&buffer[0], stored_user.c_str());
strcpy(&buffer[stored_user.length() + 1], stored_pass.c_str());
strcpy(&buffer[0], m_stored_user.c_str());
strcpy(&buffer[m_stored_user.length() + 1], m_stored_pass.c_str());
size_t encrypted_len = buffer_len;
@@ -775,7 +718,7 @@ void Client::LoginSendLogin()
eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true);
login_connection->QueuePacket(p);
m_login_connection->QueuePacket(p);
}
/**
@@ -796,7 +739,7 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p)
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
auto response_error = sp.GetUInt16(1);
login_connection_manager->OnConnectionStateChange(
m_login_connection_manager->OnConnectionStateChange(
std::bind(
&Client::LoginOnStatusChangeIgnored,
this,
@@ -809,18 +752,26 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p)
if (response_error > 101) {
LogDebug("response [{0}] failed login", response_error);
DoFailedLogin();
login_connection->Close();
m_login_connection->Close();
}
else {
LogDebug(
"response [{0}] login succeeded user [{1}]",
response_error,
stored_user
m_stored_user
);
auto m_dbid = sp.GetUInt32(8);
CreateEQEmuAccount(stored_user, stored_pass, m_dbid);
login_connection->Close();
CreateEQEmuAccount(m_stored_user, m_stored_pass, m_dbid);
m_login_connection->Close();
}
}
bool Client::ProcessHealthCheck(std::string username)
{
if (username == "healthcheckuser") {
return true;
}
return false;
}
+24 -56
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_CLIENT_H
#define EQEMU_CLIENT_H
@@ -27,22 +7,9 @@
#include "../common/eq_stream_intf.h"
#include "../common/net/dns.h"
#include "../common/net/daybreak_connection.h"
#include "login_structures.h"
#include "login_types.h"
#include <memory>
enum LSClientVersion {
cv_titanium,
cv_sod
};
enum LSClientStatus {
cs_not_sent_session_ready,
cs_waiting_for_login,
cs_creating_account,
cs_failed_to_login,
cs_logged_in
};
/**
* Client class, controls a single client and it's connection to the login server
*/
@@ -116,49 +83,49 @@ public:
*
* @return
*/
unsigned int GetAccountID() const { return account_id; }
unsigned int GetAccountID() const { return m_account_id; }
/**
* Gets the loginserver name of this client
*
* @return
*/
std::string GetLoginServerName() const { return loginserver_name; }
std::string GetLoginServerName() const { return m_loginserver_name; }
/**
* Gets the account name of this client
*
* @return
*/
std::string GetAccountName() const { return account_name; }
std::string GetAccountName() const { return m_account_name; }
/**
* Gets the key generated at login for this client
*
* @return
*/
std::string GetKey() const { return key; }
std::string GetKey() const { return m_key; }
/**
* Gets the server selected to be played on for this client
*
* @return
*/
unsigned int GetPlayServerID() const { return play_server_id; }
unsigned int GetPlayServerID() const { return m_play_server_id; }
/**
* Gets the play sequence state for this client
*
* @return
*/
unsigned int GetPlaySequence() const { return play_sequence_id; }
unsigned int GetPlaySequence() const { return m_play_sequence_id; }
/**
* Gets the connection for this client
*
* @return
*/
std::shared_ptr<EQStreamInterface> GetConnection() { return connection; }
std::shared_ptr<EQStreamInterface> GetConnection() { return m_connection; }
/**
* Attempts to create a login account
@@ -195,24 +162,24 @@ public:
void CreateEQEmuAccount(const std::string &in_account_name, const std::string &in_account_password, unsigned int loginserver_account_id);
private:
EQ::Random random;
std::shared_ptr<EQStreamInterface> connection;
LSClientVersion version;
LSClientStatus status;
EQ::Random m_random;
std::shared_ptr<EQStreamInterface> m_connection;
LSClientVersion m_client_version;
LSClientStatus m_client_status;
std::string account_name;
unsigned int account_id;
std::string loginserver_name;
unsigned int play_server_id;
unsigned int play_sequence_id;
std::string key;
std::string m_account_name;
unsigned int m_account_id;
std::string m_loginserver_name;
unsigned int m_play_server_id;
unsigned int m_play_sequence_id;
std::string m_key;
std::unique_ptr<EQ::Net::DaybreakConnectionManager> login_connection_manager;
std::shared_ptr<EQ::Net::DaybreakConnection> login_connection;
LoginLoginRequest_Struct llrs;
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_login_connection_manager;
std::shared_ptr<EQ::Net::DaybreakConnection> m_login_connection;
LoginBaseMessage_Struct m_llrs;
std::string stored_user;
std::string stored_pass;
std::string m_stored_user;
std::string m_stored_pass;
void LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
void LoginOnStatusChange(
std::shared_ptr<EQ::Net::DaybreakConnection> conn,
@@ -228,6 +195,7 @@ private:
void LoginSendSessionReady();
void LoginSendLogin();
void LoginProcessLoginResponse(const EQ::Net::Packet &p);
static bool ProcessHealthCheck(std::string username);
};
#endif
+6 -25
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "client_manager.h"
#include "login_server.h"
@@ -25,6 +5,7 @@ extern LoginServer server;
extern bool run_server;
#include "../common/eqemu_logsys.h"
#include "../common/misc.h"
ClientManager::ClientManager()
{
@@ -52,8 +33,8 @@ ClientManager::ClientManager()
titanium_stream->OnNewConnection(
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
LogInfo(
"New Titanium client connection from {0}:{1}",
stream->GetRemoteIP(),
"New Titanium client connection from [{0}:{1}]",
long2ip(stream->GetRemoteIP()),
stream->GetRemotePort()
);
@@ -87,13 +68,13 @@ ClientManager::ClientManager()
sod_stream->OnNewConnection(
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
LogInfo(
"New SoD client connection from {0}:{1}",
stream->GetRemoteIP(),
"New SoD+ client connection from [{0}:{1}]",
long2ip(stream->GetRemoteIP()),
stream->GetRemotePort()
);
stream->SetOpcodeManager(&sod_ops);
Client *c = new Client(stream, cv_sod);
auto *c = new Client(stream, cv_sod);
clients.push_back(c);
}
);
-20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_CLIENTMANAGER_H
#define EQEMU_CLIENTMANAGER_H
+44 -66
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "../common/global_define.h"
#include "database.h"
@@ -45,11 +25,6 @@ Database::Database(
std::string name
)
{
this->user = user;
this->pass = pass;
this->host = host;
this->name = name;
uint32 errnum = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
if (!Open(
@@ -75,8 +50,8 @@ Database::Database(
*/
Database::~Database()
{
if (database) {
mysql_close(database);
if (m_database) {
mysql_close(m_database);
}
}
@@ -355,11 +330,13 @@ void Database::UpdateLoginserverAccountPasswordHash(
/**
* @param short_name
* @param long_name
* @param login_world_server_admin_id
* @return
*/
Database::DbWorldRegistration Database::GetWorldRegistration(
const std::string &short_name,
const std::string &long_name,
uint32 login_world_server_admin_id
)
{
@@ -375,45 +352,46 @@ Database::DbWorldRegistration Database::GetWorldRegistration(
" login_world_servers AS WSR\n"
" JOIN login_server_list_types AS SLT ON WSR.login_server_list_type_id = SLT.id\n"
"WHERE\n"
" WSR.short_name = '{0}' AND WSR.login_server_admin_id = {1} LIMIT 1",
" WSR.short_name = '{}' AND WSR.long_name = '{}' AND WSR.login_server_admin_id = {} LIMIT 1",
EscapeString(short_name),
EscapeString(long_name),
login_world_server_admin_id
);
Database::DbWorldRegistration world_registration{};
Database::DbWorldRegistration r{};
auto results = QueryDatabase(query);
if (!results.Success() || results.RowCount() != 1) {
return world_registration;
return r;
}
auto row = results.begin();
world_registration.loaded = true;
world_registration.server_id = std::stoi(row[0]);
world_registration.server_description = row[1];
world_registration.server_list_type = std::stoi(row[3]);
world_registration.is_server_trusted = std::stoi(row[2]) > 0;
world_registration.server_list_description = row[4];
world_registration.server_admin_id = std::stoi(row[5]);
r.loaded = true;
r.server_id = std::stoi(row[0]);
r.server_description = row[1];
r.server_list_type = std::stoi(row[3]);
r.is_server_trusted = std::stoi(row[2]) > 0;
r.server_list_description = row[4];
r.server_admin_id = std::stoi(row[5]);
if (world_registration.server_admin_id <= 0) {
return world_registration;
if (r.server_admin_id <= 0) {
return r;
}
auto world_registration_query = fmt::format(
"SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1",
world_registration.server_admin_id
r.server_admin_id
);
auto world_registration_results = QueryDatabase(world_registration_query);
if (world_registration_results.Success() && world_registration_results.RowCount() == 1) {
auto world_registration_row = world_registration_results.begin();
world_registration.server_admin_account_name = world_registration_row[0];
world_registration.server_admin_account_password = world_registration_row[1];
r.server_admin_account_name = world_registration_row[0];
r.server_admin_account_password = world_registration_row[1];
}
return world_registration;
return r;
}
/**
@@ -665,21 +643,21 @@ Database::DbLoginServerAdmin Database::GetLoginServerAdmin(const std::string &ac
auto results = QueryDatabase(query);
Database::DbLoginServerAdmin login_server_admin{};
Database::DbLoginServerAdmin r{};
if (results.RowCount() == 1) {
auto row = results.begin();
login_server_admin.loaded = true;
login_server_admin.id = std::stoi(row[0]);
login_server_admin.account_name = row[1];
login_server_admin.account_password = row[2];
login_server_admin.first_name = row[3];
login_server_admin.last_name = row[4];
login_server_admin.email = row[5];
login_server_admin.registration_date = row[7];
login_server_admin.registration_ip_address = row[8];
r.loaded = true;
r.id = std::stoi(row[0]);
r.account_name = row[1];
r.account_password = row[2];
r.first_name = row[3];
r.last_name = row[4];
r.email = row[5];
r.registration_date = row[7];
r.registration_ip_address = row[8];
}
return login_server_admin;
return r;
}
/**
@@ -701,20 +679,20 @@ Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName(
auto results = QueryDatabase(query);
Database::DbLoginServerAccount login_server_account{};
Database::DbLoginServerAccount r{};
if (results.RowCount() == 1) {
auto row = results.begin();
login_server_account.loaded = true;
login_server_account.id = std::stoi(row[0]);
login_server_account.account_name = row[1];
login_server_account.account_password = row[2];
login_server_account.account_email = row[3];
login_server_account.source_loginserver = row[4];
login_server_account.last_ip_address = row[5];
login_server_account.last_login_date = row[6];
login_server_account.created_at = row[7];
login_server_account.updated_at = row[8];
r.loaded = true;
r.id = std::stoi(row[0]);
r.account_name = row[1];
r.account_password = row[2];
r.account_email = row[3];
r.source_loginserver = row[4];
r.last_ip_address = row[5];
r.last_login_date = row[6];
r.created_at = row[7];
r.updated_at = row[8];
}
return login_server_account;
return r;
}
+5 -24
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_DATABASEMYSQL_H
#define EQEMU_DATABASEMYSQL_H
@@ -32,7 +12,7 @@
class Database : public DBcore {
public:
Database() { database = nullptr; }
Database() { m_database = nullptr; }
/**
* Constructor, tries to set our database to connect to the supplied options.
@@ -49,7 +29,7 @@ public:
* Destructor, frees our database if needed.
*/
~Database();
bool IsConnected() { return (database != nullptr); }
bool IsConnected() { return (m_database != nullptr); }
/**
* Retrieves the login data (password hash and account id) from the account name provided needed for client login procedure.
@@ -158,11 +138,13 @@ public:
* Returns true if the record was found, false otherwise
*
* @param short_name
* @param long_name
* @param login_world_server_admin_id
* @return
*/
Database::DbWorldRegistration GetWorldRegistration(
const std::string &short_name,
const std::string &long_name,
uint32 login_world_server_admin_id
);
@@ -298,8 +280,7 @@ public:
);
protected:
std::string user, pass, host, port, name;
MYSQL *database{};
MYSQL *m_database{};
};
#endif
-20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#pragma once
#include <string>
-20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMUCAPI__H
#define EQEMUCAPI__H
+1 -23
View File
@@ -1,29 +1,7 @@
#include <utility>
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_LOGINSERVER_H
#define EQEMU_LOGINSERVER_H
#include <utility>
#include "../common/json_config.h"
#include "database.h"
#include "encryption.h"
-117
View File
@@ -1,117 +0,0 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_LOGINSTRUCTURES_H
#define EQEMU_LOGINSTRUCTURES_H
#pragma pack(1)
struct LoginChatMessage_Struct {
short Unknown0;
uint32 Unknown1;
uint32 Unknown2;
uint32 Unknown3;
uint8 Unknown4;
char ChatMessage[1];
};
struct LoginLoginRequest_Struct {
short unknown1;
short unknown2;
short unknown3;
short unknown4;
short unknown5;
char unknown6[16];
};
struct LoginAccepted_Struct {
short unknown1;
short unknown2;
short unknown3;
short unknown4;
short unknown5;
char encrypt[80];
};
struct LoginFailedAttempts_Struct {
char message; //0x01
char unknown2[7]; //0x00
uint32 lsid;
char key[11]; //10 char + null term;
uint32 failed_attempts;
char unknown3[4]; //0x00, 0x00, 0x00, 0x03
char unknown4[4]; //0x00, 0x00, 0x00, 0x02
char unknown5[4]; //0xe7, 0x03, 0x00, 0x00
char unknown6[4]; //0xff, 0xff, 0xff, 0xff
char unknown7[4]; //0xa0, 0x05, 0x00, 0x00
char unknown8[4]; //0x00, 0x00, 0x00, 0x02
char unknown9[4]; //0xff, 0x03, 0x00, 0x00
char unknown10[4]; //0x00, 0x00, 0x00, 0x00
char unknown11[4]; //0x63, 0x00, 0x00, 0x00
char unknown12[4]; //0x01, 0x00, 0x00, 0x00
char unknown13[4]; //0x00, 0x00, 0x00, 0x00
char unknown14[4]; //0x00, 0x00, 0x00, 0x00
};
struct LoginLoginFailed_Struct {
short unknown1;
short unknown2;
short unknown3;
short unknown4;
short unknown5;
char unknown6[74];
};
struct ServerListHeader_Struct {
uint32 Unknown1;
uint32 Unknown2;
uint32 Unknown3;
uint32 Unknown4;
uint32 NumberOfServers;
};
struct PlayEverquestRequest_Struct {
uint16 Sequence;
uint32 Unknown1;
uint32 Unknown2;
uint32 ServerNumber;
};
struct PlayEverquestResponse_Struct {
uint8 Sequence;
uint8 Unknown1[9];
uint8 Allowed;
uint16 Message;
uint8 Unknown2[3];
uint32 ServerNumber;
};
static const unsigned char FailedLoginResponseData[] = {
0xf6, 0x85, 0x9c, 0x23, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16,
0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16,
0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3
};
#pragma pack()
#endif
+143
View File
@@ -0,0 +1,143 @@
#ifndef EQEMU_LOGINSTRUCTURES_H
#define EQEMU_LOGINSTRUCTURES_H
#pragma pack(1)
// unencrypted base message header in all packets
struct LoginBaseMessage_Struct {
int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...)
bool compressed; // true: deflated
int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply)
int32_t unk3; // unused?
};
struct LoginBaseReplyMessage_Struct {
bool success; // 0: failure (shows error string) 1: success
int32_t error_str_id; // last error eqlsstr id, default: 101 (no error)
char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere)
};
struct LoginHandShakeReply_Struct {
LoginBaseMessage_Struct base_header;
LoginBaseReplyMessage_Struct base_reply;
char unknown[1]; // variable length string
};
// for reference, login buffer is variable (minimum size 8 due to encryption)
struct PlayerLogin_Struct {
LoginBaseMessage_Struct base_header;
char username[1];
char password[1];
};
// variable length, can use directly if not serializing strings
struct PlayerLoginReply_Struct {
// base header excluded to make struct data easier to encrypt
//LoginBaseMessage_Struct base_header;
LoginBaseReplyMessage_Struct base_reply;
int8_t unk1; // (default: 0)
int8_t unk2; // (default: 0)
int32_t lsid; // (default: -1)
char key[11]; // client reads until null (variable length)
int32_t failed_attempts;
bool show_player_count; // admin flag, enables admin button and shows server player counts (default: false)
int32_t offer_min_days; // guess, needs more investigation, maybe expansion offers (default: 99)
int32_t offer_min_views; // guess (default: -1)
int32_t offer_cooldown_minutes; // guess (default: 0)
int32_t web_offer_number; // web order view number, 0 nothing (default: 0)
int32_t web_offer_min_days; // number of days to show offer (based on first offer time in client eqls ini) (default: 99)
int32_t web_offer_min_views; // mininum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
int32_t web_offer_cooldown_minutes; // minimum minutes between offers (based on last offer time in client eqls ini) (default: 0)
char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct
char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select
};
// variable length, for reference
struct LoginClientServerData_Struct {
char ip[1];
int32_t server_type; // legends, preferred, standard
int32_t server_id;
char server_name[1];
char country_code[1]; // if doesn't match client locale then server is colored dark grey in list and joining is prevented (to block for "us" use one of "kr", "tw", "jp", "de", "fr", or "cn") (ISO 3166-1 alpha-2)
char language_code[1];
int32_t server_status; // see ServerStatusFlags
int32_t player_count;
};
// variable length, for reference
struct ServerListReply_Struct {
LoginBaseMessage_Struct base_header;
LoginBaseReplyMessage_Struct base_reply;
int32_t server_count;
LoginClientServerData_Struct servers[0];
};
struct PlayEverquestRequest_Struct {
LoginBaseMessage_Struct base_header;
uint32 server_number;
};
// SCJoinServerReply
struct PlayEverquestResponse_Struct {
LoginBaseMessage_Struct base_header;
LoginBaseReplyMessage_Struct base_reply;
uint32 server_number;
};
#pragma pack()
enum LSClientVersion {
cv_titanium,
cv_sod
};
enum LSClientStatus {
cs_not_sent_session_ready,
cs_waiting_for_login,
cs_creating_account,
cs_failed_to_login,
cs_logged_in
};
namespace LS {
namespace ServerStatusFlags {
enum eServerStatusFlags {
Up = 0, // default
Down = 1,
Unused = 2,
Locked = 4 // can be combined with Down to show "Locked (Down)"
};
}
namespace ServerTypeFlags {
enum eServerTypeFlags {
None = 0,
Standard = 1,
Unknown2 = 2,
Unknown4 = 4,
Preferred = 8,
Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green)
};
}
enum ServerType {
Standard = 3,
Preferred = 2,
Legends = 1,
};
namespace ErrStr {
constexpr static int ERROR_NONE = 101; // No Error
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
};
}
#endif
+3
View File
@@ -11,6 +11,9 @@
},
"worldservers": {
"unregistered_allowed": true,
"show_player_count": false,
"dev_test_servers_list_bottom": false,
"special_character_start_list_bottom": false,
"reject_duplicate_servers": false
},
"web_api": {
+4 -4
View File
@@ -5,7 +5,7 @@ CREATE TABLE `login_accounts` (
`account_password` text NOT NULL,
`account_email` varchar(100) NOT NULL,
`source_loginserver` varchar(64) DEFAULT NULL,
`last_ip_address` varchar(15) NOT NULL,
`last_ip_address` varchar(30) NOT NULL,
`last_login_date` datetime NOT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT current_timestamp(),
@@ -22,7 +22,7 @@ CREATE TABLE `login_server_admins` (
`last_name` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`registration_date` datetime NOT NULL,
`registration_ip_address` varchar(15) NOT NULL,
`registration_ip_address` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
@@ -45,7 +45,7 @@ CREATE TABLE `login_world_servers` (
`tag_description` varchar(50) NOT NULL DEFAULT '',
`login_server_list_type_id` int(11) NOT NULL,
`last_login_date` datetime DEFAULT NULL,
`last_ip_address` varchar(15) DEFAULT NULL,
`last_ip_address` varchar(30) DEFAULT NULL,
`login_server_admin_id` int(11) NOT NULL,
`is_server_trusted` int(11) NOT NULL,
`note` varchar(255) DEFAULT NULL,
@@ -61,4 +61,4 @@ CREATE TABLE `login_api_tokens` (
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
+21 -20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <iostream>
#include <random>
#include "loginserver_command_handler.h"
@@ -58,6 +38,7 @@ namespace LoginserverCommandHandler {
function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens;
function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount;
function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword;
function_map["health:check-login"] = &LoginserverCommandHandler::HealthCheckLogin;
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
}
@@ -301,4 +282,24 @@ namespace LoginserverCommandHandler {
cmd(3).str()
);
}
/**
* @param argc
* @param argv
* @param cmd
* @param description
*/
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Checks login health using a test user";
std::vector<std::string> arguments = {};
std::vector<std::string> options = {};
if (cmd[{"-h", "--help"}]) {
return;
}
LogInfo("[CLI] [HealthCheck] Response code [{}]", AccountManagement::HealthCheckUserLogin());
}
}
+1 -20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "iostream"
#include "../common/cli/eqemu_command_handler.h"
@@ -34,6 +14,7 @@ namespace LoginserverCommandHandler {
void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description);
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description);
};
+27 -28
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "loginserver_webserver.h"
#include "server_manager.h"
#include "login_server.h"
@@ -51,14 +31,15 @@ namespace LoginserverWebserver {
auto iter = server.server_manager->getWorldServers().begin();
while (iter != server.server_manager->getWorldServers().end()) {
Json::Value row;
row["server_long_name"] = (*iter)->GetServerLongName();
row["server_short_name"] = (*iter)->GetServerLongName();
row["server_list_id"] = (*iter)->GetServerListID();
row["server_status"] = (*iter)->GetStatus();
row["zones_booted"] = (*iter)->GetZonesBooted();
row["local_ip"] = (*iter)->GetLocalIP();
row["remote_ip"] = (*iter)->GetRemoteIP();
row["players_online"] = (*iter)->GetPlayersOnline();
row["server_long_name"] = (*iter)->GetServerLongName();
row["server_short_name"] = (*iter)->GetServerShortName();
row["server_list_type_id"] = (*iter)->GetServerListID();
row["server_status"] = (*iter)->GetStatus();
row["zones_booted"] = (*iter)->GetZonesBooted();
row["local_ip"] = (*iter)->GetLocalIP();
row["remote_ip"] = (*iter)->GetRemoteIP();
row["players_online"] = (*iter)->GetPlayersOnline();
row["world_id"] = (*iter)->GetServerId();
response.append(row);
++iter;
}
@@ -316,6 +297,24 @@ namespace LoginserverWebserver {
LoginserverWebserver::SendResponse(response, res);
}
);
api.Get(
"/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) {
Json::Value response;
uint32 login_response = AccountManagement::HealthCheckUserLogin();
response["status"] = login_response;
if (login_response == 0) {
response["message"] = "Process unresponsive, exiting...";
LogInfo("Probes healthcheck unresponsive, exiting...");
}
LoginserverWebserver::SendResponse(response, res);
if (login_response == 0) {
std::exit(0);
}
}
);
}
/**
-20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_LOGINSERVER_WEBSERVER_H
#define EQEMU_LOGINSERVER_WEBSERVER_H
+41 -26
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "../common/global_define.h"
#include "../common/types.h"
#include "../common/opcodemgr.h"
@@ -30,6 +10,7 @@
#include "login_server.h"
#include "loginserver_webserver.h"
#include "loginserver_command_handler.h"
#include "../common/string_util.h"
#include <time.h>
#include <stdlib.h>
#include <string>
@@ -40,6 +21,7 @@ LoginServer server;
EQEmuLogSys LogSys;
bool run_server = true;
void ResolveAddresses();
void CatchSignal(int sig_num)
{
}
@@ -80,8 +62,30 @@ void LoadServerConfig()
"worldservers",
"reject_duplicate_servers",
false
));
server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true));
)
);
server.options.SetShowPlayerCount(server.config.GetVariableBool("worldservers", "show_player_count", false));
server.options.AllowUnregistered(
server.config.GetVariableBool(
"worldservers",
"unregistered_allowed",
true
)
);
server.options.SetWorldDevTestServersListBottom(
server.config.GetVariableBool(
"worldservers",
"dev_test_servers_list_bottom",
false
)
);
server.options.SetWorldSpecialCharacterStartListBottom(
server.config.GetVariableBool(
"worldservers",
"special_character_start_list_bottom",
false
)
);
/**
* Account
@@ -138,11 +142,13 @@ void start_web_server()
httplib::Server api;
api.set_logger([](const auto& req, const auto& res) {
if (!req.path.empty()) {
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port);
api.set_logger(
[](const auto &req, const auto &res) {
if (!req.path.empty()) {
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port);
}
}
});
);
LoginserverWebserver::RegisterRoutes(api);
api.listen("0.0.0.0", web_api_port);
@@ -256,6 +262,15 @@ int main(int argc, char **argv)
#endif
LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers());
LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed());
LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled());
LogInfo(
"[Config] [WorldServer] DevAndTestServersListBottom [{0}]",
server.options.IsWorldDevTestServersListBottom()
);
LogInfo(
"[Config] [WorldServer] SpecialCharactersStartListBottom [{0}]",
server.options.IsWorldSpecialCharacterStartListBottom()
);
LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode());
LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed());
LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed());
+27 -20
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_OPTIONS_H
#define EQEMU_OPTIONS_H
@@ -133,6 +113,29 @@ public:
inline void UpdateInsecurePasswords(bool b) { update_insecure_passwords = b; }
inline bool IsUpdatingInsecurePasswords() const { return update_insecure_passwords; }
inline bool IsShowPlayerCountEnabled() const
{
return show_player_count;
}
inline void SetShowPlayerCount(bool show_player_count)
{
Options::show_player_count = show_player_count;
}
inline bool IsWorldDevTestServersListBottom() const { return world_dev_test_servers_list_bottom; }
inline void SetWorldDevTestServersListBottom(bool dev_test_servers_list_bottom)
{
Options::world_dev_test_servers_list_bottom = dev_test_servers_list_bottom;
}
inline bool IsWorldSpecialCharacterStartListBottom() const
{
return world_special_character_start_list_bottom;
}
inline void SetWorldSpecialCharacterStartListBottom(bool world_special_character_start_list_bottom)
{
Options::world_special_character_start_list_bottom = world_special_character_start_list_bottom;
}
private:
bool allow_unregistered;
bool trace;
@@ -140,8 +143,11 @@ private:
bool dump_in_packets;
bool dump_out_packets;
bool reject_duplicate_servers;
bool world_dev_test_servers_list_bottom;
bool world_special_character_start_list_bottom;
bool allow_token_login;
bool allow_password_login;
bool show_player_count;
bool auto_create_accounts;
bool auto_link_accounts;
bool update_insecure_passwords;
@@ -150,5 +156,6 @@ private:
std::string default_loginserver_name;
};
#endif
+68 -173
View File
@@ -1,26 +1,6 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "server_manager.h"
#include "login_server.h"
#include "login_structures.h"
#include "login_types.h"
#include <stdlib.h>
#include "../common/eqemu_logsys.h"
@@ -33,15 +13,15 @@ ServerManager::ServerManager()
{
int listen_port = server.config.GetVariableInt("general", "listen_port", 5998);
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
m_server_connection = std::make_unique<EQ::Net::ServertalkServer>();
EQ::Net::ServertalkServerOptions opts;
opts.port = listen_port;
opts.ipv6 = false;
server_connection->Listen(opts);
opts.ipv6 = false;
m_server_connection->Listen(opts);
LogInfo("Loginserver now listening on port [{0}]", listen_port);
server_connection->OnConnectionIdentified(
m_server_connection->OnConnectionIdentified(
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> world_connection) {
LogInfo(
"New World Server connection from {0}:{1}",
@@ -49,8 +29,8 @@ ServerManager::ServerManager()
world_connection->Handle()->RemotePort()
);
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
auto iter = m_world_servers.begin();
while (iter != m_world_servers.end()) {
if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) ==
0 &&
(*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) {
@@ -61,27 +41,27 @@ ServerManager::ServerManager()
world_connection->Handle()->RemotePort()
);
world_servers.erase(iter);
m_world_servers.erase(iter);
break;
}
++iter;
}
world_servers.push_back(std::make_unique<WorldServer>(world_connection));
m_world_servers.push_back(std::make_unique<WorldServer>(world_connection));
}
);
server_connection->OnConnectionRemoved(
m_server_connection->OnConnectionRemoved(
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> c) {
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
auto iter = m_world_servers.begin();
while (iter != m_world_servers.end()) {
if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) {
LogInfo(
"World server {0} has been disconnected, removing.",
(*iter)->GetServerLongName()
);
world_servers.erase(iter);
m_world_servers.erase(iter);
return;
}
@@ -100,8 +80,8 @@ ServerManager::~ServerManager() = default;
*/
WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, int port)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
auto iter = m_world_servers.begin();
while (iter != m_world_servers.end()) {
if ((*iter)->GetConnection()->Handle()->RemoteIP() == ip_address &&
(*iter)->GetConnection()->Handle()->RemotePort()) {
return (*iter).get();
@@ -117,9 +97,8 @@ WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, in
* @param sequence
* @return
*/
EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint32 sequence)
std::unique_ptr<EQApplicationPacket> ServerManager::CreateServerListPacket(Client *client, uint32 sequence)
{
unsigned int packet_size = sizeof(ServerListHeader_Struct);
unsigned int server_count = 0;
in_addr in{};
in.s_addr = client->GetConnection()->GetRemoteIP();
@@ -127,144 +106,60 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3
LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip);
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
if (!(*iter)->IsAuthorized()) {
for (const auto& world_server : m_world_servers)
{
if (world_server->IsAuthorized()) {
++server_count;
}
}
SerializeBuffer buf;
// LoginBaseMessage_Struct header
buf.WriteInt32(sequence);
buf.WriteInt8(0);
buf.WriteInt8(0);
buf.WriteInt32(0);
// LoginBaseReplyMessage_Struct
buf.WriteInt8(true); // success (no error)
buf.WriteInt32(0x65); // 101 "No Error" eqlsstr
buf.WriteString("");
// ServerListReply_Struct
buf.WriteInt32(server_count);
for (const auto& world_server : m_world_servers)
{
if (!world_server->IsAuthorized()) {
LogDebug(
"ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed",
(*iter)->GetServerLongName(),
(*iter)->GetConnection()->Handle()->RemoteIP()
"ServerManager::CreateServerListPacket | Server [{}] via IP [{}] is not authorized to be listed",
world_server->GetServerLongName(),
world_server->GetConnection()->Handle()->RemoteIP()
);
++iter;
continue;
}
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
if (world_ip == client_ip) {
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24;
bool use_local_ip = false;
LogDebug(
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)",
client->GetAccountName(),
client_ip,
(*iter)->GetServerLongName(),
(*iter)->GetLocalIP()
);
}
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24;
LogDebug(
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)",
client->GetAccountName(),
client_ip,
(*iter)->GetServerLongName(),
(*iter)->GetLocalIP()
);
}
else {
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetRemoteIP().size() + 24;
LogDebug(
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Remote)",
client->GetAccountName(),
client_ip,
(*iter)->GetServerLongName(),
(*iter)->GetRemoteIP()
);
std::string world_ip = world_server->GetConnection()->Handle()->RemoteIP();
if (world_ip == client_ip || IpUtil::IsIpInPrivateRfc1918(client_ip)) {
use_local_ip = true;
}
server_count++;
++iter;
LogDebug(
"CreateServerListPacket | Building list entry | Client [{}] IP [{}] Server Long Name [{}] Server IP [{}] ({})",
client->GetAccountName(),
client_ip,
world_server->GetServerLongName(),
use_local_ip ? world_server->GetLocalIP() : world_server->GetRemoteIP(),
use_local_ip ? "Local" : "Remote"
);
world_server->SerializeForClientServerList(buf, use_local_ip);
}
auto *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size);
auto *server_list = (ServerListHeader_Struct *) outapp->pBuffer;
server_list->Unknown1 = sequence;
server_list->Unknown2 = 0x00000000;
server_list->Unknown3 = 0x01650000;
/**
* Not sure what this is but it should be noted setting it to
* 0xFFFFFFFF crashes the client so: don't do that.
*/
server_list->Unknown4 = 0x00000000;
server_list->NumberOfServers = server_count;
unsigned char *data_pointer = outapp->pBuffer;
data_pointer += sizeof(ServerListHeader_Struct);
iter = world_servers.begin();
while (iter != world_servers.end()) {
if (!(*iter)->IsAuthorized()) {
++iter;
continue;
}
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
if (world_ip == client_ip) {
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
data_pointer += ((*iter)->GetLocalIP().size() + 1);
}
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
data_pointer += ((*iter)->GetLocalIP().size() + 1);
}
else {
memcpy(data_pointer, (*iter)->GetRemoteIP().c_str(), (*iter)->GetRemoteIP().size());
data_pointer += ((*iter)->GetRemoteIP().size() + 1);
}
switch ((*iter)->GetServerListID()) {
case 1: {
*(unsigned int *) data_pointer = 0x00000030;
break;
}
case 2: {
*(unsigned int *) data_pointer = 0x00000009;
break;
}
default: {
*(unsigned int *) data_pointer = 0x00000001;
}
}
data_pointer += 4;
*(unsigned int *) data_pointer = (*iter)->GetServerId();
data_pointer += 4;
memcpy(data_pointer, (*iter)->GetServerLongName().c_str(), (*iter)->GetServerLongName().size());
data_pointer += ((*iter)->GetServerLongName().size() + 1);
memcpy(data_pointer, "EN", 2);
data_pointer += 3;
memcpy(data_pointer, "US", 2);
data_pointer += 3;
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
if ((*iter)->GetStatus() < 0) {
if ((*iter)->GetZonesBooted() == 0) {
*(uint32 *) data_pointer = 0x01;
}
else {
*(uint32 *) data_pointer = 0x04;
}
}
else {
*(uint32 *) data_pointer = 0x02;
}
data_pointer += 4;
*(uint32 *) data_pointer = (*iter)->GetPlayersOnline();
data_pointer += 4;
++iter;
}
return outapp;
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
}
/**
@@ -278,9 +173,9 @@ void ServerManager::SendUserToWorldRequest(
const std::string &client_loginserver
)
{
auto iter = world_servers.begin();
auto iter = m_world_servers.begin();
bool found = false;
while (iter != world_servers.end()) {
while (iter != m_world_servers.end()) {
if ((*iter)->GetServerId() == server_id) {
EQ::Net::DynamicPacket outapp;
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
@@ -316,8 +211,8 @@ bool ServerManager::ServerExists(
WorldServer *ignore
)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
auto iter = m_world_servers.begin();
while (iter != m_world_servers.end()) {
if ((*iter).get() == ignore) {
++iter;
continue;
@@ -343,8 +238,8 @@ void ServerManager::DestroyServerByName(
WorldServer *ignore
)
{
auto iter = world_servers.begin();
while (iter != world_servers.end()) {
auto iter = m_world_servers.begin();
while (iter != m_world_servers.end()) {
if ((*iter).get() == ignore) {
++iter;
continue;
@@ -353,7 +248,7 @@ void ServerManager::DestroyServerByName(
if ((*iter)->GetServerLongName().compare(server_long_name) == 0 &&
(*iter)->GetServerShortName().compare(server_short_name) == 0) {
(*iter)->GetConnection()->Handle()->Disconnect();
iter = world_servers.erase(iter);
iter = m_world_servers.erase(iter);
continue;
}
@@ -366,5 +261,5 @@ void ServerManager::DestroyServerByName(
*/
const std::list<std::unique_ptr<WorldServer>> &ServerManager::getWorldServers() const
{
return world_servers;
return m_world_servers;
}
+3 -23
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_SERVERMANAGER_H
#define EQEMU_SERVERMANAGER_H
@@ -65,7 +45,7 @@ public:
* @param sequence
* @return
*/
EQApplicationPacket *CreateServerListPacket(Client *client, uint32 sequence);
std::unique_ptr<EQApplicationPacket> CreateServerListPacket(Client *client, uint32 sequence);
/**
* Checks to see if there is a server exists with this name, ignoring option
@@ -103,8 +83,8 @@ private:
*/
WorldServer *GetServerByAddress(const std::string &ip_address, int port);
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
std::list<std::unique_ptr<WorldServer>> world_servers;
std::unique_ptr<EQ::Net::ServertalkServer> m_server_connection;
std::list<std::unique_ptr<WorldServer>> m_world_servers;
};
+227 -155
View File
@@ -1,28 +1,8 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "world_server.h"
#include "login_server.h"
#include "login_structures.h"
#include "../common/eqemu_logsys.h"
#include "login_types.h"
#include "../common/ip_util.h"
#include "../common/string_util.h"
extern LoginServer server;
@@ -31,16 +11,16 @@ extern LoginServer server;
*/
WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection)
{
connection = worldserver_connection;
zones_booted = 0;
players_online = 0;
server_status = 0;
server_id = 0;
server_list_type_id = 0;
server_process_type = 0;
is_server_authorized = false;
is_server_trusted = false;
is_server_logged_in = false;
m_connection = worldserver_connection;
m_zones_booted = 0;
m_players_online = 0;
m_server_status = 0;
m_server_id = 0;
m_server_list_type_id = 0;
m_server_process_type = 0;
m_is_server_authorized = false;
m_is_server_trusted = false;
m_is_server_logged_in = false;
worldserver_connection->OnMessage(
ServerOP_NewLSInfo,
@@ -72,21 +52,25 @@ WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> wo
std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
);
m_keepalive = std::make_unique<EQ::Timer>(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1));
m_keepalive = std::make_unique<EQ::Timer>(
1000,
true,
std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)
);
}
WorldServer::~WorldServer() = default;
void WorldServer::Reset()
{
server_id;
zones_booted = 0;
players_online = 0;
server_status = 0;
server_list_type_id = 0;
server_process_type = 0;
is_server_authorized = false;
is_server_logged_in = false;
m_server_id;
m_zones_booted = 0;
m_players_online = 0;
m_server_status = 0;
m_server_list_type_id = 0;
m_server_process_type = 0;
m_is_server_authorized = false;
m_is_server_logged_in = false;
}
/**
@@ -116,9 +100,15 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe
return;
}
auto *info = (ServerNewLSInfo_Struct *) packet.Data();
// if for whatever reason the world server is not sending an address, use the local address it sends
std::string remote_ip_addr = info->remote_ip_address;
std::string local_ip_addr = info->local_ip_address;
if (remote_ip_addr.empty() && !local_ip_addr.empty() && local_ip_addr != "127.0.0.1") {
strcpy(info->remote_ip_address, local_ip_addr.c_str());
}
LogInfo(
"New World Server Info | name [{0}] shortname [{1}] remote_address [{2}] local_address [{3}] account [{4}] password [{5}] server_type [{6}]",
info->server_long_name,
@@ -208,15 +198,15 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
LogDebug("User-To-World Response received");
}
auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data();
auto *r = (UsertoWorldResponseLegacy_Struct *) packet.Data();
LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid);
Client *client = server.client_manager->GetClient(user_to_world_response->lsaccountid, "eqemu");
LogDebug("Trying to find client with user id of [{0}]", r->lsaccountid);
Client *client = server.client_manager->GetClient(r->lsaccountid, "eqemu");
if (client) {
LogDebug(
"Found client with user id of [{0}] and account name of [{1}]",
user_to_world_response->lsaccountid,
r->lsaccountid,
client->GetAccountName()
);
@@ -226,11 +216,11 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
);
auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer;
per->Sequence = client->GetPlaySequence();
per->ServerNumber = client->GetPlayServerID();
per->base_header.sequence = client->GetPlaySequence();
per->server_number = client->GetPlayServerID();
if (user_to_world_response->response > 0) {
per->Allowed = 1;
if (r->response > 0) {
per->base_reply.success = true;
SendClientAuth(
client->GetConnection()->GetRemoteAddr(),
client->GetAccountName(),
@@ -240,36 +230,37 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
);
}
switch (user_to_world_response->response) {
switch (r->response) {
case UserToWorldStatusSuccess:
per->Message = 101;
per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
break;
case UserToWorldStatusWorldUnavail:
per->Message = 326;
per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
break;
case UserToWorldStatusSuspended:
per->Message = 337;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
break;
case UserToWorldStatusBanned:
per->Message = 338;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
break;
case UserToWorldStatusWorldAtCapacity:
per->Message = 339;
per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
break;
case UserToWorldStatusAlreadyOnline:
per->Message = 111;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
break;
default:
per->Message = 102;
per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
break;
}
if (server.options.IsWorldTraceOn()) {
LogDebug(
"Sending play response: allowed [{0}] sequence [{1}] server number [{2}] message [{3}]",
per->Allowed,
per->Sequence,
per->ServerNumber,
per->Message
per->base_reply.success,
per->base_header.sequence,
per->server_number,
per->base_reply.error_str_id
);
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
@@ -285,7 +276,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
else {
LogError(
"Received User-To-World Response for [{0}] but could not find the client referenced!",
user_to_world_response->lsaccountid
r->lsaccountid
);
}
}
@@ -344,8 +335,8 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
);
auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer;
per->Sequence = client->GetPlaySequence();
per->ServerNumber = client->GetPlayServerID();
per->base_header.sequence = client->GetPlaySequence();
per->server_number = client->GetPlayServerID();
LogDebug(
"Found sequence and play of [{0}] [{1}]",
@@ -356,7 +347,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
if (user_to_world_response->response > 0) {
per->Allowed = 1;
per->base_reply.success = true;
SendClientAuth(
client->GetConnection()->GetRemoteAddr(),
client->GetAccountName(),
@@ -368,34 +359,35 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
switch (user_to_world_response->response) {
case UserToWorldStatusSuccess:
per->Message = 101;
per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
break;
case UserToWorldStatusWorldUnavail:
per->Message = 326;
per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
break;
case UserToWorldStatusSuspended:
per->Message = 337;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
break;
case UserToWorldStatusBanned:
per->Message = 338;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
break;
case UserToWorldStatusWorldAtCapacity:
per->Message = 339;
per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
break;
case UserToWorldStatusAlreadyOnline:
per->Message = 111;
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
break;
default:
per->Message = 102;
per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
break;
}
if (server.options.IsTraceOn()) {
LogDebug(
"Sending play response with following data, allowed [{0}], sequence {1}, server number {2}, message {3}",
per->Allowed,
per->Sequence,
per->ServerNumber,
per->Message
per->base_reply.success,
per->base_header.sequence,
per->server_number,
per->base_reply.error_str_id
);
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
}
@@ -443,7 +435,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
}
if (server.options.IsWorldTraceOn()) {
LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", short_name);
LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", m_short_name);
}
auto *loginserver_update = (ServerLSAccountUpdate_Struct *) packet.Data();
@@ -486,6 +478,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
return;
}
SanitizeWorldServerName(new_world_server_info_packet->server_long_name);
SetAccountPassword(new_world_server_info_packet->account_password)
->SetLongName(new_world_server_info_packet->server_long_name)
->SetShortName(new_world_server_info_packet->server_short_name)
@@ -506,8 +500,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
else {
if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) {
LogInfo("World tried to login but there already exists a server that has that name, destroying [{}]",
long_name);
server.server_manager->DestroyServerByName(long_name, short_name, this);
m_long_name);
server.server_manager->DestroyServerByName(m_long_name, m_short_name, this);
}
}
@@ -551,6 +545,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
Database::DbWorldRegistration
world_registration = server.db->GetWorldRegistration(
GetServerShortName(),
GetServerLongName(),
world_server_admin_id
);
@@ -578,6 +573,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
GetServerLongName(),
GetRemoteIp()
);
WorldServer::FormatWorldServerName(
new_world_server_info_packet->server_long_name,
world_registration.server_list_type
);
SetLongName(new_world_server_info_packet->server_long_name);
}
/**
@@ -619,7 +620,7 @@ void WorldServer::SendClientAuth(
strncpy(client_auth.loginserver_name, &loginserver_name[0], 64);
const std::string &client_address(ip);
std::string world_address(connection->Handle()->RemoteIP());
std::string world_address(m_connection->Handle()->RemoteIP());
if (client_address == world_address) {
client_auth.is_client_from_local_network = 1;
@@ -655,13 +656,22 @@ void WorldServer::SendClientAuth(
);
outapp.PutSerialize(0, client_auth);
connection->Send(ServerOP_LSClientAuth, outapp);
m_connection->Send(ServerOP_LSClientAuth, outapp);
if (server.options.IsDumpInPacketsOn()) {
DumpPacket(ServerOP_LSClientAuth, outapp);
}
}
constexpr static int MAX_ACCOUNT_NAME_LENGTH = 30;
constexpr static int MAX_ACCOUNT_PASSWORD_LENGTH = 30;
constexpr static int MAX_SERVER_LONG_NAME_LENGTH = 200;
constexpr static int MAX_SERVER_SHORT_NAME_LENGTH = 50;
constexpr static int MAX_SERVER_LOCAL_ADDRESS_LENGTH = 125;
constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125;
constexpr static int MAX_SERVER_VERSION_LENGTH = 64;
constexpr static int MAX_SERVER_PROTOCOL_VERSION = 25;
/**
* @param new_world_server_info_packet
* @return
@@ -670,41 +680,32 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
ServerNewLSInfo_Struct *new_world_server_info_packet
)
{
const int max_account_name_length = 30;
const int max_account_password_length = 30;
const int max_server_long_name_length = 200;
const int max_server_short_name_length = 50;
const int max_server_local_address_length = 125;
const int max_server_remote_address_length = 125;
const int max_server_version_length = 64;
const int max_server_protocol_version = 25;
if (strlen(new_world_server_info_packet->account_name) >= max_account_name_length) {
LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", max_account_name_length);
if (strlen(new_world_server_info_packet->account_name) >= MAX_ACCOUNT_NAME_LENGTH) {
LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", MAX_ACCOUNT_NAME_LENGTH);
return false;
}
else if (strlen(new_world_server_info_packet->account_password) >= max_account_password_length) {
LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", max_account_password_length);
else if (strlen(new_world_server_info_packet->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) {
LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", MAX_ACCOUNT_PASSWORD_LENGTH);
return false;
}
else if (strlen(new_world_server_info_packet->server_long_name) >= max_server_long_name_length) {
LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", max_server_long_name_length);
else if (strlen(new_world_server_info_packet->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) {
LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", MAX_SERVER_LONG_NAME_LENGTH);
return false;
}
else if (strlen(new_world_server_info_packet->server_short_name) >= max_server_short_name_length) {
LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", max_server_short_name_length);
else if (strlen(new_world_server_info_packet->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) {
LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", MAX_SERVER_SHORT_NAME_LENGTH);
return false;
}
else if (strlen(new_world_server_info_packet->server_version) >= max_server_short_name_length) {
LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", max_server_version_length);
else if (strlen(new_world_server_info_packet->server_version) >= MAX_SERVER_VERSION_LENGTH) {
LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", MAX_SERVER_VERSION_LENGTH);
return false;
}
else if (strlen(new_world_server_info_packet->protocol_version) >= max_server_protocol_version) {
LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", max_server_protocol_version);
else if (strlen(new_world_server_info_packet->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) {
LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", MAX_SERVER_PROTOCOL_VERSION);
return false;
}
if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) {
if (strlen(new_world_server_info_packet->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) {
if (strlen(new_world_server_info_packet->local_ip_address) == 0) {
LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost");
SetLocalIp("127.0.0.1");
@@ -714,17 +715,17 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
}
}
else {
LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", max_server_local_address_length);
LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH);
return false;
}
if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) {
if (strlen(new_world_server_info_packet->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) {
if (strlen(new_world_server_info_packet->remote_ip_address) == 0) {
SetRemoteIp(GetConnection()->Handle()->RemoteIP());
LogWarning(
"Remote address was null, defaulting to stream address [{0}]",
remote_ip_address
m_remote_ip_address
);
}
else {
@@ -736,7 +737,7 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
LogWarning(
"Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]",
remote_ip_address
m_remote_ip_address
);
}
@@ -796,7 +797,7 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
if (IsServerTrusted()) {
LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world");
EQ::Net::DynamicPacket outapp;
connection->Send(ServerOP_LSAccountUpdate, outapp);
m_connection->Send(ServerOP_LSAccountUpdate, outapp);
}
}
else {
@@ -847,6 +848,8 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
->SetIsServerTrusted(world_registration.is_server_trusted)
->SetServerListTypeId(world_registration.server_list_type);
SetIsServerAuthorized(true);
bool does_world_server_pass_authentication_check = (
world_registration.server_admin_account_name == GetAccountName() &&
WorldServer::ValidateWorldServerAdminLogin(
@@ -864,7 +867,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
if (does_world_server_have_non_empty_credentials) {
if (does_world_server_pass_authentication_check) {
SetIsServerAuthorized(true);
SetIsServerLoggedIn(true);
LogInfo(
"Server long_name [{0}] short_name [{1}] successfully logged in",
@@ -875,16 +878,13 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
if (IsServerTrusted()) {
LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world");
EQ::Net::DynamicPacket outapp;
connection->Send(ServerOP_LSAccountUpdate, outapp);
m_connection->Send(ServerOP_LSAccountUpdate, outapp);
}
}
else {
/**
* this is the first of two cases where we should deny access even if unregistered is allowed
*/
// server is authorized to be on the loginserver list but didn't pass login check
LogInfo(
"Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database.",
"Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database. Unregistered still allowed",
GetServerLongName(),
GetServerShortName()
);
@@ -892,9 +892,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
}
else {
/**
* this is the second of two cases where we should deny access even if unregistered is allowed
*/
// server is authorized to be on the loginserver list but didn't pass login check
if (!GetAccountName().empty() || !GetAccountPassword().empty()) {
LogInfo(
"Server [{0}] [{1}] did not login but this server required a password to login",
@@ -903,7 +901,6 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
);
}
else {
SetIsServerAuthorized(true);
LogInfo(
"Server [{0}] [{1}] did not login but unregistered servers are allowed",
GetServerLongName(),
@@ -939,14 +936,12 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
}
}
/**
* Auto create a registration
*/
// Auto create a registration
if (!server.db->CreateWorldRegistration(
GetServerLongName(),
GetServerShortName(),
GetRemoteIp(),
server_id,
m_server_id,
server_admin_id
)) {
return false;
@@ -1038,13 +1033,56 @@ bool WorldServer::ValidateWorldServerAdminLogin(
return false;
}
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const
{
// see LoginClientServerData_Struct
if (use_local_ip) {
out.WriteString(GetLocalIP());
}
else {
out.WriteString(GetRemoteIP());
}
switch (GetServerListID()) {
case LS::ServerType::Legends:
out.WriteInt32(LS::ServerTypeFlags::Legends);
break;
case LS::ServerType::Preferred:
out.WriteInt32(LS::ServerTypeFlags::Preferred);
break;
default:
out.WriteInt32(LS::ServerTypeFlags::Standard);
break;
}
out.WriteUInt32(GetServerId());
out.WriteString(GetServerLongName());
out.WriteString("us"); // country code
out.WriteString("en"); // language code
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
if (GetStatus() < 0) {
if (GetZonesBooted() == 0) {
out.WriteInt32(LS::ServerStatusFlags::Down);
}
else {
out.WriteInt32(LS::ServerStatusFlags::Locked);
}
}
else {
out.WriteInt32(LS::ServerStatusFlags::Up);
}
out.WriteUInt32(GetPlayersOnline());
}
/**
* @param in_server_list_id
* @return
*/
WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id)
{
server_list_type_id = in_server_list_id;
m_server_list_type_id = in_server_list_id;
return this;
}
@@ -1054,7 +1092,7 @@ WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id)
*/
const std::string &WorldServer::GetServerDescription() const
{
return server_description;
return m_server_description;
}
/**
@@ -1062,7 +1100,7 @@ const std::string &WorldServer::GetServerDescription() const
*/
WorldServer *WorldServer::SetServerDescription(const std::string &in_server_description)
{
WorldServer::server_description = in_server_description;
WorldServer::m_server_description = in_server_description;
return this;
}
@@ -1072,7 +1110,7 @@ WorldServer *WorldServer::SetServerDescription(const std::string &in_server_desc
*/
bool WorldServer::IsServerAuthorized() const
{
return is_server_authorized;
return m_is_server_authorized;
}
/**
@@ -1080,7 +1118,7 @@ bool WorldServer::IsServerAuthorized() const
*/
WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized)
{
WorldServer::is_server_authorized = in_is_server_authorized;
WorldServer::m_is_server_authorized = in_is_server_authorized;
return this;
}
@@ -1090,7 +1128,7 @@ WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized)
*/
bool WorldServer::IsServerLoggedIn() const
{
return is_server_logged_in;
return m_is_server_logged_in;
}
/**
@@ -1098,7 +1136,7 @@ bool WorldServer::IsServerLoggedIn() const
*/
WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in)
{
WorldServer::is_server_logged_in = in_is_server_logged_in;
WorldServer::m_is_server_logged_in = in_is_server_logged_in;
return this;
}
@@ -1108,7 +1146,7 @@ WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in)
*/
bool WorldServer::IsServerTrusted() const
{
return is_server_trusted;
return m_is_server_trusted;
}
/**
@@ -1116,7 +1154,7 @@ bool WorldServer::IsServerTrusted() const
*/
WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted)
{
WorldServer::is_server_trusted = in_is_server_trusted;
WorldServer::m_is_server_trusted = in_is_server_trusted;
return this;
}
@@ -1126,7 +1164,7 @@ WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted)
*/
WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted)
{
WorldServer::zones_booted = in_zones_booted;
WorldServer::m_zones_booted = in_zones_booted;
return this;
}
@@ -1136,7 +1174,7 @@ WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted)
*/
WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online)
{
WorldServer::players_online = in_players_online;
WorldServer::m_players_online = in_players_online;
return this;
}
@@ -1146,7 +1184,7 @@ WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online)
*/
WorldServer *WorldServer::SetServerStatus(int in_server_status)
{
WorldServer::server_status = in_server_status;
WorldServer::m_server_status = in_server_status;
return this;
}
@@ -1156,7 +1194,7 @@ WorldServer *WorldServer::SetServerStatus(int in_server_status)
*/
WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_type)
{
WorldServer::server_process_type = in_server_process_type;
WorldServer::m_server_process_type = in_server_process_type;
return this;
}
@@ -1166,7 +1204,7 @@ WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_ty
*/
WorldServer *WorldServer::SetLongName(const std::string &in_long_name)
{
WorldServer::long_name = in_long_name;
WorldServer::m_long_name = in_long_name;
return this;
}
@@ -1176,7 +1214,7 @@ WorldServer *WorldServer::SetLongName(const std::string &in_long_name)
*/
WorldServer *WorldServer::SetShortName(const std::string &in_short_name)
{
WorldServer::short_name = in_short_name;
WorldServer::m_short_name = in_short_name;
return this;
}
@@ -1186,7 +1224,7 @@ WorldServer *WorldServer::SetShortName(const std::string &in_short_name)
*/
WorldServer *WorldServer::SetAccountName(const std::string &in_account_name)
{
WorldServer::account_name = in_account_name;
WorldServer::m_account_name = in_account_name;
return this;
}
@@ -1196,7 +1234,7 @@ WorldServer *WorldServer::SetAccountName(const std::string &in_account_name)
*/
WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_password)
{
WorldServer::account_password = in_account_password;
WorldServer::m_account_password = in_account_password;
return this;
}
@@ -1206,7 +1244,7 @@ WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_passw
*/
WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip)
{
WorldServer::remote_ip_address = in_remote_ip;
WorldServer::m_remote_ip_address = in_remote_ip;
return this;
}
@@ -1216,7 +1254,7 @@ WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip)
*/
WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip)
{
WorldServer::local_ip = in_local_ip;
WorldServer::m_local_ip = in_local_ip;
return this;
}
@@ -1226,7 +1264,7 @@ WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip)
*/
WorldServer *WorldServer::SetProtocol(const std::string &in_protocol)
{
WorldServer::protocol = in_protocol;
WorldServer::m_protocol = in_protocol;
return this;
}
@@ -1236,7 +1274,7 @@ WorldServer *WorldServer::SetProtocol(const std::string &in_protocol)
*/
WorldServer *WorldServer::SetVersion(const std::string &in_version)
{
WorldServer::version = in_version;
WorldServer::m_version = in_version;
return this;
}
@@ -1246,7 +1284,7 @@ WorldServer *WorldServer::SetVersion(const std::string &in_version)
*/
int WorldServer::GetServerStatus() const
{
return server_status;
return m_server_status;
}
/**
@@ -1254,7 +1292,7 @@ int WorldServer::GetServerStatus() const
*/
unsigned int WorldServer::GetServerListTypeId() const
{
return server_list_type_id;
return m_server_list_type_id;
}
/**
@@ -1262,7 +1300,7 @@ unsigned int WorldServer::GetServerListTypeId() const
*/
unsigned int WorldServer::GetServerProcessType() const
{
return server_process_type;
return m_server_process_type;
}
/**
@@ -1270,7 +1308,7 @@ unsigned int WorldServer::GetServerProcessType() const
*/
const std::string &WorldServer::GetAccountName() const
{
return account_name;
return m_account_name;
}
/**
@@ -1278,7 +1316,7 @@ const std::string &WorldServer::GetAccountName() const
*/
const std::string &WorldServer::GetAccountPassword() const
{
return account_password;
return m_account_password;
}
/**
@@ -1286,7 +1324,7 @@ const std::string &WorldServer::GetAccountPassword() const
*/
const std::string &WorldServer::GetRemoteIp() const
{
return remote_ip_address;
return m_remote_ip_address;
}
/**
@@ -1294,7 +1332,7 @@ const std::string &WorldServer::GetRemoteIp() const
*/
const std::string &WorldServer::GetLocalIp() const
{
return local_ip;
return m_local_ip;
}
/**
@@ -1302,7 +1340,7 @@ const std::string &WorldServer::GetLocalIp() const
*/
const std::string &WorldServer::GetProtocol() const
{
return protocol;
return m_protocol;
}
/**
@@ -1310,11 +1348,45 @@ const std::string &WorldServer::GetProtocol() const
*/
const std::string &WorldServer::GetVersion() const
{
return version;
return m_version;
}
void WorldServer::OnKeepAlive(EQ::Timer *t)
{
ServerPacket pack(ServerOP_KeepAlive, 0);
connection->SendPacket(&pack);
m_connection->SendPacket(&pack);
}
void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
{
std::string server_long_name = name;
server_long_name = trim(server_long_name);
bool name_set_to_bottom = false;
if (server_list_type == LS::ServerType::Standard) {
if (server.options.IsWorldDevTestServersListBottom()) {
std::string s = str_tolower(server_long_name);
if (s.find("dev") != std::string::npos) {
server_long_name = fmt::format("|D| {}", server_long_name);
name_set_to_bottom = true;
}
else if (s.find("test") != std::string::npos) {
server_long_name = fmt::format("|T| {}", server_long_name);
name_set_to_bottom = true;
}
else if (s.find("installer") != std::string::npos) {
server_long_name = fmt::format("|I| {}", server_long_name);
name_set_to_bottom = true;
}
}
if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) {
auto first_char = server_long_name.c_str()[0];
if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') {
server_long_name = fmt::format("|*| {}", server_long_name);
name_set_to_bottom = true;
}
}
}
strn0cpy(name, server_long_name.c_str(), 201);
}
+35 -53
View File
@@ -1,23 +1,3 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_WORLDSERVER_H
#define EQEMU_WORLDSERVER_H
@@ -50,44 +30,44 @@ public:
/**
* Accesses connection, it is intentional that this is not const (trust me).
*/
std::shared_ptr<EQ::Net::ServertalkServerConnection> GetConnection() { return connection; }
void SetConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> c) { connection = c; }
std::shared_ptr<EQ::Net::ServertalkServerConnection> GetConnection() { return m_connection; }
void SetConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> c) { m_connection = c; }
/**
* @return
*/
unsigned int GetServerId() const { return server_id; }
unsigned int GetServerId() const { return m_server_id; }
WorldServer *SetServerId(unsigned int id)
{
server_id = id;
m_server_id = id;
return this;
}
/**
* @return
*/
std::string GetServerLongName() const { return long_name; }
std::string GetServerShortName() const { return short_name; }
std::string GetServerLongName() const { return m_long_name; }
std::string GetServerShortName() const { return m_short_name; }
/**
* Gets whether the server is authorized to show up on the server list or not
* @return
*/
bool IsAuthorized() const { return is_server_authorized; }
std::string GetLocalIP() const { return local_ip; }
std::string GetRemoteIP() const { return remote_ip_address; }
bool IsAuthorized() const { return m_is_server_authorized; }
std::string GetLocalIP() const { return m_local_ip; }
std::string GetRemoteIP() const { return m_remote_ip_address; }
/**
* Gets what kind of server this server is (legends, preferred, normal)
*
* @return
*/
unsigned int GetServerListID() const { return server_list_type_id; }
unsigned int GetServerListID() const { return m_server_list_type_id; }
WorldServer *SetServerListTypeId(unsigned int in_server_list_id);
int GetStatus() const { return server_status; }
unsigned int GetZonesBooted() const { return zones_booted; }
unsigned int GetPlayersOnline() const { return players_online; }
int GetStatus() const { return m_server_status; }
unsigned int GetZonesBooted() const { return m_zones_booted; }
unsigned int GetPlayersOnline() const { return m_players_online; }
/**
* Takes the info struct we received from world and processes it
@@ -170,6 +150,8 @@ public:
bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration);
bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration);
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const;
private:
/**
@@ -184,27 +166,26 @@ private:
void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet);
void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet);
std::shared_ptr<EQ::Net::ServertalkServerConnection> connection;
std::shared_ptr<EQ::Net::ServertalkServerConnection> m_connection;
unsigned int zones_booted;
unsigned int players_online;
int server_status;
unsigned int server_id;
unsigned int server_list_type_id;
unsigned int server_process_type;
std::string server_description;
std::string long_name;
std::string short_name;
std::string account_name;
std::string account_password;
std::string remote_ip_address;
std::string local_ip;
std::string protocol;
std::string version;
bool is_server_authorized;
bool is_server_logged_in;
bool is_server_trusted;
unsigned int m_zones_booted;
unsigned int m_players_online;
int m_server_status;
unsigned int m_server_id;
unsigned int m_server_list_type_id;
unsigned int m_server_process_type;
std::string m_server_description;
std::string m_long_name;
std::string m_short_name;
std::string m_account_name;
std::string m_account_password;
std::string m_remote_ip_address;
std::string m_local_ip;
std::string m_protocol;
std::string m_version;
bool m_is_server_authorized;
bool m_is_server_logged_in;
bool m_is_server_trusted;
/**
* Keepalive
@@ -213,6 +194,7 @@ private:
void OnKeepAlive(EQ::Timer *t);
std::unique_ptr<EQ::Timer> m_keepalive;
static void FormatWorldServerName(char *name, int8 server_list_type);
};
#endif
@@ -157,7 +157,7 @@ foreach my $table_to_generate (@tables) {
$table_found_in_schema = 0;
}
if ($table_found_in_schema == 0) {
if ($table_found_in_schema == 0 && ($requested_table_to_generate eq "" || $requested_table_to_generate eq "all")) {
print "Table [$table_to_generate] not found in schema, skipping\n";
next;
}
+2
View File
@@ -426,6 +426,8 @@
9170|2021_03_03_instance_safereturns.sql|SHOW TABLES LIKE 'character_instance_safereturns'|empty|
9171|2021_03_30_remove_dz_is_current_member.sql|SHOW COLUMNS FROM `dynamic_zone_members` LIKE 'is_current_member'|not_empty|
9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty|
9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty|
9174|2021_10_09_not_null_door_columns.sql|SELECT * FROM db_version WHERE version >= 9174|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not
@@ -0,0 +1,3 @@
ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL;
ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL;
ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL;
@@ -10,4 +10,4 @@ CREATE TABLE `character_instance_safereturns` (
`safe_heading` float NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `character_id` (`character_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
@@ -0,0 +1 @@
ALTER TABLE zone ADD lava_damage INT(11) NULL DEFAULT '50' AFTER underworld_teleport_index, ADD min_lava_damage INT(11) NOT NULL DEFAULT '10' AFTER lava_damage;
@@ -0,0 +1,18 @@
-- update any null columns to non-null value first to avoid data truncation errors
-- this will likely only affect the buffer column
update `doors` set `doors`.`dest_x` = 0 where `doors`.`dest_x` is null;
update `doors` set `doors`.`dest_y` = 0 where `doors`.`dest_y` is null;
update `doors` set `doors`.`dest_z` = 0 where `doors`.`dest_z` is null;
update `doors` set `doors`.`dest_heading` = 0 where `doors`.`dest_heading` is null;
update `doors` set `doors`.`invert_state` = 0 where `doors`.`invert_state` is null;
update `doors` set `doors`.`incline` = 0 where `doors`.`incline` is null;
update `doors` set `doors`.`buffer` = 0 where `doors`.`buffer` is null;
ALTER TABLE `doors`
CHANGE COLUMN `dest_x` `dest_x` FLOAT NOT NULL DEFAULT '0' AFTER `dest_instance`,
CHANGE COLUMN `dest_y` `dest_y` FLOAT NOT NULL DEFAULT '0' AFTER `dest_x`,
CHANGE COLUMN `dest_z` `dest_z` FLOAT NOT NULL DEFAULT '0' AFTER `dest_y`,
CHANGE COLUMN `dest_heading` `dest_heading` FLOAT NOT NULL DEFAULT '0' AFTER `dest_z`,
CHANGE COLUMN `invert_state` `invert_state` INT(11) NOT NULL DEFAULT '0' AFTER `dest_heading`,
CHANGE COLUMN `incline` `incline` INT(11) NOT NULL DEFAULT '0' AFTER `invert_state`,
CHANGE COLUMN `buffer` `buffer` FLOAT NOT NULL DEFAULT '0' AFTER `size`;
+57 -59
View File
@@ -1343,71 +1343,67 @@ void ClientList::GetClients(const char *zone_name, std::vector<ClientListEntry *
void ClientList::SendClientVersionSummary(const char *Name)
{
uint32 ClientTitaniumCount = 0;
uint32 ClientSoFCount = 0;
uint32 ClientSoDCount = 0;
uint32 ClientUnderfootCount = 0;
uint32 ClientRoFCount = 0;
uint32 ClientRoF2Count = 0;
std::vector<uint32> unique_ips;
std::map<EQ::versions::ClientVersion,int> client_count = {
{ EQ::versions::ClientVersion::Titanium, 0 },
{ EQ::versions::ClientVersion::SoF, 0 },
{ EQ::versions::ClientVersion::SoD, 0 },
{ EQ::versions::ClientVersion::UF, 0 },
{ EQ::versions::ClientVersion::RoF, 0 },
{ EQ::versions::ClientVersion::RoF2, 0 }
};
LinkedListIterator<ClientListEntry*> Iterator(clientlist);
Iterator.Reset();
while(Iterator.MoreElements())
{
while (Iterator.MoreElements()) {
ClientListEntry* CLE = Iterator.GetData();
if (CLE && CLE->zone()) {
auto client_version = CLE->GetClientVersion();
if (
client_version >= (uint8) EQ::versions::ClientVersion::Titanium &&
client_version <= (uint8) EQ::versions::ClientVersion::RoF2
) {
client_count[(EQ::versions::ClientVersion)client_version]++;
}
if(CLE && CLE->zone())
{
switch(CLE->GetClientVersion())
{
case 1:
{
break;
}
case 2:
{
++ClientTitaniumCount;
break;
}
case 3:
{
++ClientSoFCount;
break;
}
case 4:
{
++ClientSoDCount;
break;
}
case 5:
{
++ClientUnderfootCount;
break;
}
case 6:
{
++ClientRoFCount;
break;
}
case 7:
{
++ClientRoF2Count;
break;
}
default:
break;
if (std::find(unique_ips.begin(), unique_ips.begin(), CLE->GetIP()) == unique_ips.end()) {
unique_ips.push_back(CLE->GetIP());
}
}
Iterator.Advance();
}
zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i Titanium, %i SoF, %i SoD, %i UF, %i RoF, %i RoF2 clients currently connected.",
ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount, ClientRoF2Count);
uint32 total_clients = (
client_count[EQ::versions::ClientVersion::Titanium] +
client_count[EQ::versions::ClientVersion::SoF] +
client_count[EQ::versions::ClientVersion::SoD] +
client_count[EQ::versions::ClientVersion::UF] +
client_count[EQ::versions::ClientVersion::RoF] +
client_count[EQ::versions::ClientVersion::RoF2]
);
zoneserver_list.SendEmoteMessage(
Name,
0,
0,
Chat::White,
fmt::format(
"There {} {} Titanium, {} SoF, {} SoD, {} UF, {} RoF, and {} RoF2 Client{} currently connected for a total of {} Client{} and {} Unique IP{} connected.",
(total_clients != 1 ? "are" : "is"),
client_count[EQ::versions::ClientVersion::Titanium],
client_count[EQ::versions::ClientVersion::SoF],
client_count[EQ::versions::ClientVersion::SoD],
client_count[EQ::versions::ClientVersion::UF],
client_count[EQ::versions::ClientVersion::RoF],
client_count[EQ::versions::ClientVersion::RoF2],
(total_clients != 1 ? "s" : ""),
total_clients,
(total_clients != 1 ? "s" : ""),
unique_ips.size(),
(unique_ips.size() != 1 ? "s" : "")
).c_str()
);
}
void ClientList::OnTick(EQ::Timer *t)
@@ -1589,12 +1585,14 @@ void ClientList::SendCharacterMessage(ClientListEntry* character, int chat_type,
return;
}
uint32_t pack_size = sizeof(CZMessagePlayer_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_CZMessagePlayer, pack_size);
auto buf = reinterpret_cast<CZMessagePlayer_Struct*>(pack->pBuffer);
uint32_t pack_size = sizeof(CZMessage_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_CZMessage, pack_size);
auto buf = reinterpret_cast<CZMessage_Struct*>(pack->pBuffer);
buf->update_type = CZUpdateType_ClientName;
buf->update_identifier = 0;
buf->type = chat_type;
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
strn0cpy(buf->message, message.c_str(), sizeof(buf->message));
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
character->Server()->SendPacket(pack.get());
}
@@ -1633,7 +1631,7 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character,
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
buf->string_id = eqstr_id;
buf->chat_type = chat_type;
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
buf->args_size = args_size;
memcpy(buf->args, serialized_args.buffer(), serialized_args.size());
+17 -10
View File
@@ -778,8 +778,14 @@ void ConsoleWorldShutdown(
zoneserver_list.WorldShutDown(0, 0);
}
else if (strcasecmp(args[0].c_str(), "disable") == 0) {
connection->SendLine("<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
zoneserver_list.SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
connection->SendLine("[SYSTEM] World shutdown has been aborted.");
zoneserver_list.SendEmoteMessage(
0,
0,
0,
Chat::Yellow,
"[SYSTEM] World shutdown has been aborted."
);
zoneserver_list.shutdowntimer->Disable();
zoneserver_list.reminder->Disable();
}
@@ -825,14 +831,15 @@ void ConsoleSignalCharByName(
}
connection->SendLine(StringFormat("Signal Sent to %s with ID %i", (char *) args[0].c_str(), atoi(args[1].c_str())));
uint32 message_len = strlen((char *) args[0].c_str()) + 1;
auto pack = new ServerPacket(
ServerOP_CZSignalClientByName,
sizeof(CZClientSignalByName_Struct) + message_len
);
CZClientSignalByName_Struct *CZSC = (CZClientSignalByName_Struct *) pack->pBuffer;
strn0cpy(CZSC->character_name, (char *) args[0].c_str(), 64);
CZSC->signal = atoi(args[1].c_str());
uint32 message_len = strlen((char *) args[0].c_str()) + 1;
auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct) + message_len);
CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer;
uint8 update_type = CZUpdateType_ClientName;
int update_identifier = 0;
CZS->update_type = update_type;
CZS->update_identifier = update_identifier;
CZS->signal = atoi(args[1].c_str());
strn0cpy(CZS->client_name, (char *) args[0].c_str(), 64);
zoneserver_list.SendPacket(pack);
safe_delete(pack);
}
+14 -2
View File
@@ -750,8 +750,20 @@ void Console::ProcessCommand(const char* command) {
zoneserver_list.WorldShutDown(0, 0);
}
else if(strcasecmp(sep.arg[1], "disable") == 0) {
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
zoneserver_list.SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
SendEmoteMessage(
0,
0,
0,
Chat::Yellow,
"[SYSTEM] World shutdown has been aborted."
);
zoneserver_list.SendEmoteMessage(
0,
0,
0,
Chat::Yellow,
"[SYSTEM] World shutdown has been aborted."
);
zoneserver_list.shutdowntimer->Disable();
zoneserver_list.reminder->Disable();
}
+107 -76
View File
@@ -46,12 +46,12 @@ extern volatile bool RunLoops;
LoginServer::LoginServer(const char *iAddress, uint16 iPort, const char *Account, const char *Password, bool legacy)
{
strn0cpy(LoginServerAddress, iAddress, 256);
LoginServerPort = iPort;
LoginAccount = Account;
LoginPassword = Password;
CanAccountUpdate = false;
IsLegacy = legacy;
strn0cpy(m_loginserver_address, iAddress, 256);
m_loginserver_port = iPort;
m_login_account = Account;
m_login_password = Password;
m_can_account_update = false;
m_is_legacy = legacy;
Connect();
}
@@ -93,7 +93,10 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
if (Config->Locked) {
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
LogDebug("[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid);
LogDebug(
"[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]",
utwr->lsaccountid
);
utwrs->response = UserToWorldStatusWorldUnavail;
SendPacket(&outpack);
return;
@@ -171,7 +174,10 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
if (Config->Locked == true) {
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
LogDebug("[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid);
LogDebug(
"[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]",
utwr->lsaccountid
);
utwrs->response = UserToWorldStatusWorldUnavail;
SendPacket(&outpack);
return;
@@ -320,61 +326,63 @@ void LoginServer::ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p)
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
LogNetcode("Received ServerOP_LSAccountUpdate packet from loginserver");
CanAccountUpdate = true;
m_can_account_update = true;
}
bool LoginServer::Connect()
{
char errbuf[1024];
if ((LoginServerIP = ResolveIP(LoginServerAddress, errbuf)) == 0) {
LogInfo("Unable to resolve [{}] to an IP", LoginServerAddress);
if ((m_loginserver_ip = ResolveIP(m_loginserver_address, errbuf)) == 0) {
LogInfo("Unable to resolve [{}] to an IP", m_loginserver_address);
return false;
}
if (LoginServerIP == 0 || LoginServerPort == 0) {
if (m_loginserver_ip == 0 || m_loginserver_port == 0) {
LogInfo(
"Connect info incomplete, cannot connect: [{0}:{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
return false;
}
if (IsLegacy) {
legacy_client = std::make_unique<EQ::Net::ServertalkLegacyClient>(LoginServerAddress, LoginServerPort, false);
legacy_client->OnConnect(
if (m_is_legacy) {
m_legacy_client = std::make_unique<EQ::Net::ServertalkLegacyClient>(
m_loginserver_address,
m_loginserver_port,
false
);
m_legacy_client->OnConnect(
[this](EQ::Net::ServertalkLegacyClient *client) {
if (client) {
LogInfo(
"Connected to Legacy Loginserver: [{0}:{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
SendInfo();
SendStatus();
zoneserver_list.SendLSZones();
statusupdate_timer = std::make_unique<EQ::Timer>(
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
SendStatus();
}
m_statusupdate_timer = std::make_unique<EQ::Timer>(
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
SendStatus();
}
);
}
else {
LogInfo(
"Could not connect to Legacy Loginserver: [{0}:{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
}
}
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_UsertoWorldReqLeg,
std::bind(
&LoginServer::ProcessUsertoWorldReqLeg,
@@ -383,7 +391,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_UsertoWorldReq,
std::bind(
&LoginServer::ProcessUsertoWorldReq,
@@ -392,7 +400,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_LSClientAuthLeg,
std::bind(
&LoginServer::ProcessLSClientAuthLegacy,
@@ -401,7 +409,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_LSClientAuth,
std::bind(
&LoginServer::ProcessLSClientAuth,
@@ -410,7 +418,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_LSFatalError,
std::bind(
&LoginServer::ProcessLSFatalError,
@@ -419,7 +427,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_SystemwideMessage,
std::bind(
&LoginServer::ProcessSystemwideMessage,
@@ -428,7 +436,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_LSRemoteAddr,
std::bind(
&LoginServer::ProcessLSRemoteAddr,
@@ -437,7 +445,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
legacy_client->OnMessage(
m_legacy_client->OnMessage(
ServerOP_LSAccountUpdate,
std::bind(
&LoginServer::ProcessLSAccountUpdate,
@@ -448,37 +456,43 @@ bool LoginServer::Connect()
);
}
else {
client = std::make_unique<EQ::Net::ServertalkClient>(LoginServerAddress, LoginServerPort, false, "World", "");
client->OnConnect(
m_client = std::make_unique<EQ::Net::ServertalkClient>(
m_loginserver_address,
m_loginserver_port,
false,
"World",
""
);
m_client->OnConnect(
[this](EQ::Net::ServertalkClient *client) {
if (client) {
LogInfo(
"Connected to Loginserver: [{0}:{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
SendInfo();
SendStatus();
zoneserver_list.SendLSZones();
statusupdate_timer = std::make_unique<EQ::Timer>(
m_statusupdate_timer = std::make_unique<EQ::Timer>(
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
SendStatus();
}
);
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
SendStatus();
}
);
}
else {
LogInfo(
"Could not connect to Loginserver: [{0}:{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
}
}
);
client->OnMessage(
m_client->OnMessage(
ServerOP_UsertoWorldReqLeg,
std::bind(
&LoginServer::ProcessUsertoWorldReqLeg,
@@ -487,7 +501,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_UsertoWorldReq,
std::bind(
&LoginServer::ProcessUsertoWorldReq,
@@ -496,7 +510,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_LSClientAuthLeg,
std::bind(
&LoginServer::ProcessLSClientAuthLegacy,
@@ -505,7 +519,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_LSClientAuth,
std::bind(
&LoginServer::ProcessLSClientAuth,
@@ -514,7 +528,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_LSFatalError,
std::bind(
&LoginServer::ProcessLSFatalError,
@@ -523,7 +537,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_SystemwideMessage,
std::bind(
&LoginServer::ProcessSystemwideMessage,
@@ -532,7 +546,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_LSRemoteAddr,
std::bind(
&LoginServer::ProcessLSRemoteAddr,
@@ -541,7 +555,7 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
client->OnMessage(
m_client->OnMessage(
ServerOP_LSAccountUpdate,
std::bind(
&LoginServer::ProcessLSAccountUpdate,
@@ -552,7 +566,10 @@ bool LoginServer::Connect()
);
}
m_keepalive = std::make_unique<EQ::Timer>(1000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1));
m_keepalive = std::make_unique<EQ::Timer>(
1000,
true,
std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1));
return true;
}
@@ -566,28 +583,42 @@ void LoginServer::SendInfo()
pack->size = sizeof(ServerNewLSInfo_Struct);
pack->pBuffer = new uchar[pack->size];
memset(pack->pBuffer, 0, pack->size);
ServerNewLSInfo_Struct *lsi = (ServerNewLSInfo_Struct *) pack->pBuffer;
strcpy(lsi->protocol_version, EQEMU_PROTOCOL_VERSION);
strcpy(lsi->server_version, LOGIN_VERSION);
strcpy(lsi->server_long_name, Config->LongName.c_str());
strcpy(lsi->server_short_name, Config->ShortName.c_str());
strn0cpy(lsi->account_name, LoginAccount.c_str(), 30);
strn0cpy(lsi->account_password, LoginPassword.c_str(), 30);
auto *l = (ServerNewLSInfo_Struct *) pack->pBuffer;
strcpy(l->protocol_version, EQEMU_PROTOCOL_VERSION);
strcpy(l->server_version, LOGIN_VERSION);
strcpy(l->server_long_name, Config->LongName.c_str());
strcpy(l->server_short_name, Config->ShortName.c_str());
strn0cpy(l->account_name, m_login_account.c_str(), 30);
strn0cpy(l->account_password, m_login_password.c_str(), 30);
if (Config->WorldAddress.length()) {
strcpy(lsi->remote_ip_address, Config->WorldAddress.c_str());
strcpy(l->remote_ip_address, Config->WorldAddress.c_str());
}
if (Config->LocalAddress.length()) {
strcpy(lsi->local_ip_address, Config->LocalAddress.c_str());
strcpy(l->local_ip_address, Config->LocalAddress.c_str());
}
else {
auto local_addr = IsLegacy ? legacy_client->Handle()->LocalIP() : client->Handle()->LocalIP();
strcpy(lsi->local_ip_address, local_addr.c_str());
WorldConfig::SetLocalAddress(lsi->local_ip_address);
auto local_addr = m_is_legacy ? m_legacy_client->Handle()->LocalIP() : m_client->Handle()->LocalIP();
strcpy(l->local_ip_address, local_addr.c_str());
WorldConfig::SetLocalAddress(l->local_ip_address);
}
LogInfo(
"[LoginServer::SendInfo] protocol_version [{}] server_version [{}] long_name [{}] short_name [{}] account_name [{}] remote_ip_address [{}] local_ip [{}]",
l->protocol_version,
l->server_version,
l->server_long_name,
l->server_short_name,
l->account_name,
l->remote_ip_address,
l->local_ip_address
);
SendPacket(pack);
delete pack;
}
void LoginServer::SendStatus()
{
auto pack = new ServerPacket;
@@ -618,14 +649,14 @@ void LoginServer::SendStatus()
*/
void LoginServer::SendPacket(ServerPacket *pack)
{
if (IsLegacy) {
if (legacy_client) {
legacy_client->SendPacket(pack);
if (m_is_legacy) {
if (m_legacy_client) {
m_legacy_client->SendPacket(pack);
}
}
else {
if (client) {
client->SendPacket(pack);
if (m_client) {
m_client->SendPacket(pack);
}
}
}
@@ -636,11 +667,11 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack)
if (CanUpdate()) {
LogInfo(
"Sending ServerOP_LSAccountUpdate packet to loginserver: [{0}]:[{1}]",
LoginServerAddress,
LoginServerPort
m_loginserver_address,
m_loginserver_port
);
strn0cpy(ls_account_update->worldaccount, LoginAccount.c_str(), 30);
strn0cpy(ls_account_update->worldpassword, LoginPassword.c_str(), 30);
strn0cpy(ls_account_update->worldaccount, m_login_account.c_str(), 30);
strn0cpy(ls_account_update->worldpassword, m_login_password.c_str(), 30);
SendPacket(pack);
}
}
+16 -33
View File
@@ -1,20 +1,3 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef LOGINSERVER_H
#define LOGINSERVER_H
@@ -42,20 +25,20 @@ public:
void SendAccountUpdate(ServerPacket *pack);
bool Connected()
{
if (IsLegacy) {
if (legacy_client) {
return legacy_client->Connected();
if (m_is_legacy) {
if (m_legacy_client) {
return m_legacy_client->Connected();
}
}
else {
if (client) {
return client->Connected();
if (m_client) {
return m_client->Connected();
}
}
return false;
}
bool CanUpdate() { return CanAccountUpdate; }
bool CanUpdate() { return m_can_account_update; }
private:
void ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p);
@@ -70,15 +53,15 @@ private:
void OnKeepAlive(EQ::Timer *t);
std::unique_ptr<EQ::Timer> m_keepalive;
std::unique_ptr<EQ::Net::ServertalkClient> client;
std::unique_ptr<EQ::Net::ServertalkLegacyClient> legacy_client;
std::unique_ptr<EQ::Timer> statusupdate_timer;
char LoginServerAddress[256];
uint32 LoginServerIP;
uint16 LoginServerPort;
std::string LoginAccount;
std::string LoginPassword;
bool CanAccountUpdate;
bool IsLegacy;
std::unique_ptr<EQ::Net::ServertalkClient> m_client;
std::unique_ptr<EQ::Net::ServertalkLegacyClient> m_legacy_client;
std::unique_ptr<EQ::Timer> m_statusupdate_timer;
char m_loginserver_address[256];
uint32 m_loginserver_ip;
uint16 m_loginserver_port;
std::string m_login_account;
std::string m_login_password;
bool m_can_account_update;
bool m_is_legacy;
};
#endif
+1 -1
View File
@@ -1311,7 +1311,7 @@ void SharedTaskManager::SendMembersMessageID(
for (const auto &member : shared_task->GetMembers()) {
auto character = client_list.FindCLEByCharacterID(member.character_id);
if (character && character->Server()) {
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
character->Server()->SendPacket(pack.get());
}
}
+101 -90
View File
@@ -645,7 +645,12 @@ bool WorldDatabase::GetStartZone(
}
void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){
if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
int sof_start_zone_id = RuleI(World, SoFStartZoneID);
if (sof_start_zone_id > 0) {
in_pp->zone_id = sof_start_zone_id;
in_cc->start_zone = in_pp->zone_id;
}
else if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
in_pp->zone_id = in_cc->start_zone;
}
else {
@@ -653,105 +658,111 @@ void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCrea
in_pp->y = in_pp->binds[0].y = -20.0f;
in_pp->z = in_pp->binds[0].z = 0.79f;
in_pp->heading = in_pp->binds[0].heading = 0.0f;
in_pp->zone_id = in_pp->binds[0].zone_id = 394; // Crescent Reach.
in_pp->zone_id = in_pp->binds[0].zone_id = Zones::CRESCENT; // Crescent Reach.
}
}
void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc)
{
switch (in_cc->start_zone)
{
case 0:
int titanium_start_zone_id = RuleI(World, TitaniumStartZoneID);
if (titanium_start_zone_id > 0) {
in_pp->zone_id = titanium_start_zone_id;
in_pp->binds[0].zone_id = titanium_start_zone_id;
} else {
switch (in_cc->start_zone)
{
if (in_cc->deity == 203) // Cazic-Thule Erudites go to Paineel
case StartZoneIndex::Odus:
{
in_pp->zone_id = 75; // paineel
in_pp->binds[0].zone_id = 75;
if (in_cc->deity == EQ::deity::DeityCazicThule) // Cazic-Thule Erudites go to Paineel
{
in_pp->zone_id = Zones::PAINEEL; // paineel
in_pp->binds[0].zone_id = Zones::PAINEEL;
}
else
{
in_pp->zone_id = Zones::ERUDNEXT; // erudnext
in_pp->binds[0].zone_id = Zones::TOX; // tox
}
break;
}
else
case StartZoneIndex::Qeynos:
{
in_pp->zone_id = 24; // erudnext
in_pp->binds[0].zone_id = 38; // tox
in_pp->zone_id = Zones::QEYNOS2; // qeynos2
in_pp->binds[0].zone_id = Zones::QEYNOS2; // qeynos2
break;
}
case StartZoneIndex::Halas:
{
in_pp->zone_id = Zones::HALAS; // halas
in_pp->binds[0].zone_id = Zones::EVERFROST; // everfrost
break;
}
case StartZoneIndex::Rivervale:
{
in_pp->zone_id = Zones::RIVERVALE; // rivervale
in_pp->binds[0].zone_id = Zones::KITHICOR; // kithicor
break;
}
case StartZoneIndex::Freeport:
{
in_pp->zone_id = Zones::FREPORTW; // freportw
in_pp->binds[0].zone_id = Zones::FREPORTW; // freportw
break;
}
case StartZoneIndex::Neriak:
{
in_pp->zone_id = Zones::NERIAKA; // neriaka
in_pp->binds[0].zone_id = Zones::NEKTULOS; // nektulos
break;
}
case StartZoneIndex::Grobb:
{
in_pp->zone_id = Zones::GROBB; // grobb
in_pp->binds[0].zone_id = Zones::INNOTHULE; // innothule
break;
}
case StartZoneIndex::Oggok:
{
in_pp->zone_id = Zones::OGGOK; // oggok
in_pp->binds[0].zone_id = Zones::FEERROTT; // feerrott
break;
}
case StartZoneIndex::Kaladim:
{
in_pp->zone_id = Zones::KALADIMA; // kaladima
in_pp->binds[0].zone_id = Zones::BUTCHER; // butcher
break;
}
case StartZoneIndex::GreaterFaydark:
{
in_pp->zone_id = Zones::GFAYDARK; // gfaydark
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
break;
}
case StartZoneIndex::Felwithe:
{
in_pp->zone_id = Zones::FELWITHEA; // felwithea
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
break;
}
case StartZoneIndex::Akanon:
{
in_pp->zone_id = Zones::AKANON; // akanon
in_pp->binds[0].zone_id = Zones::STEAMFONT; // steamfont
break;
}
case StartZoneIndex::Cabilis:
{
in_pp->zone_id = Zones::CABWEST; // cabwest
in_pp->binds[0].zone_id = Zones::FIELDOFBONE; // fieldofbone
break;
}
case StartZoneIndex::SharVahl:
{
in_pp->zone_id = Zones::SHARVAHL; // sharvahl
in_pp->binds[0].zone_id = Zones::SHARVAHL; // sharvahl
break;
}
break;
}
case 1:
{
in_pp->zone_id = 2; // qeynos2
in_pp->binds[0].zone_id = 2; // qeynos2
break;
}
case 2:
{
in_pp->zone_id = 29; // halas
in_pp->binds[0].zone_id = 30; // everfrost
break;
}
case 3:
{
in_pp->zone_id = 19; // rivervale
in_pp->binds[0].zone_id = 20; // kithicor
break;
}
case 4:
{
in_pp->zone_id = 9; // freportw
in_pp->binds[0].zone_id = 9; // freportw
break;
}
case 5:
{
in_pp->zone_id = 40; // neriaka
in_pp->binds[0].zone_id = 25; // nektulos
break;
}
case 6:
{
in_pp->zone_id = 52; // gukta
in_pp->binds[0].zone_id = 46; // innothule
break;
}
case 7:
{
in_pp->zone_id = 49; // oggok
in_pp->binds[0].zone_id = 47; // feerrott
break;
}
case 8:
{
in_pp->zone_id = 60; // kaladima
in_pp->binds[0].zone_id = 68; // butcher
break;
}
case 9:
{
in_pp->zone_id = 54; // gfaydark
in_pp->binds[0].zone_id = 54; // gfaydark
break;
}
case 10:
{
in_pp->zone_id = 61; // felwithea
in_pp->binds[0].zone_id = 54; // gfaydark
break;
}
case 11:
{
in_pp->zone_id = 55; // akanon
in_pp->binds[0].zone_id = 56; // steamfont
break;
}
case 12:
{
in_pp->zone_id = 82; // cabwest
in_pp->binds[0].zone_id = 78; // fieldofbone
break;
}
case 13:
{
in_pp->zone_id = 155; // sharvahl
in_pp->binds[0].zone_id = 155; // sharvahl
break;
}
}
}
+21 -3
View File
@@ -110,7 +110,16 @@ void ZSList::Process() {
}
if (reminder && reminder->Check() && shutdowntimer) {
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime() / 1000) / 60));
SendEmoteMessage(
0,
0,
0,
Chat::Yellow,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
((shutdowntimer->GetRemainingTime() / 1000) / 60)
).c_str()
);
}
}
@@ -677,7 +686,16 @@ void ZSList::UpdateUCSServerAvailable(bool ucss_available) {
void ZSList::WorldShutDown(uint32 time, uint32 interval)
{
if (time > 0) {
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60));
SendEmoteMessage(
0,
0,
0,
Chat::Yellow,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
(time / 60)
).c_str()
);
time *= 1000;
interval *= 1000;
@@ -690,7 +708,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval)
reminder->Start();
}
else {
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now.");
SendEmoteMessage(0, 0, 0, 15, "[SYSTEM] World is shutting down.");
auto pack = new ServerPacket;
pack->opcode = ServerOP_ShutdownAll;
pack->size = 0;
+30 -80
View File
@@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "expedition_message.h"
#include "shared_task_world_messaging.h"
#include "../common/shared_tasks.h"
#include "shared_task_manager.h"
extern ClientList client_list;
extern GroupLFPList LFPGroupList;
@@ -50,6 +51,8 @@ extern volatile bool UCSServerAvailable_;
extern AdventureManager adventure_manager;
extern UCSConnection UCSLink;
extern QueryServConnection QSLink;
extern SharedTaskManager shared_task_manager;
void CatchSignal(int sig_num);
ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, EQ::Net::ConsoleServer *console)
@@ -450,6 +453,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
scm->noreply = true;
scm->queued = 3; // offline
scm->chan_num = ChatChannel_TellEcho;
strcpy(scm->deliverto, scm->from);
// ideally this would be trimming off the message too, oh well
sender->Server()->SendPacket(pack);
@@ -463,6 +467,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
scm->noreply = true;
scm->queued = 2; // queue full
scm->chan_num = ChatChannel_TellEcho;
strcpy(scm->deliverto, scm->from);
sender->Server()->SendPacket(pack);
}
@@ -478,6 +483,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
scm->noreply = true;
scm->queued = 1; // queued
scm->chan_num = ChatChannel_TellEcho;
strcpy(scm->deliverto, scm->from);
sender->Server()->SendPacket(pack);
}
@@ -608,7 +614,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
if (sci->local_address[0]) {
strn0cpy(client_local_address, sci->local_address, 250);
LogInfo("Zone specified local address [{}]", sci->address);
LogInfo("Zone specified local address [{}]", sci->local_address);
}
if (sci->process_id) {
@@ -1240,98 +1246,42 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
QSLink.SendPacket(pack);
break;
}
case ServerOP_CZCastSpellPlayer:
case ServerOP_CZCastSpellGroup:
case ServerOP_CZCastSpellRaid:
case ServerOP_CZCastSpellGuild:
case ServerOP_CZMarqueePlayer:
case ServerOP_CZMarqueeGroup:
case ServerOP_CZMarqueeRaid:
case ServerOP_CZMarqueeGuild:
case ServerOP_CZMessagePlayer:
case ServerOP_CZMessageGroup:
case ServerOP_CZMessageRaid:
case ServerOP_CZMessageGuild:
case ServerOP_CZMovePlayer:
case ServerOP_CZMoveGroup:
case ServerOP_CZMoveRaid:
case ServerOP_CZMoveGuild:
case ServerOP_CZMoveInstancePlayer:
case ServerOP_CZMoveInstanceGroup:
case ServerOP_CZMoveInstanceRaid:
case ServerOP_CZMoveInstanceGuild:
case ServerOP_CZRemoveSpellPlayer:
case ServerOP_CZRemoveSpellGroup:
case ServerOP_CZRemoveSpellRaid:
case ServerOP_CZRemoveSpellGuild:
case ServerOP_CZSetEntityVariableByClientName:
case ServerOP_CZSetEntityVariableByNPCTypeID:
case ServerOP_CZSetEntityVariableByGroupID:
case ServerOP_CZSetEntityVariableByRaidID:
case ServerOP_CZSetEntityVariableByGuildID:
case ServerOP_CZSignalNPC:
case ServerOP_CZSignalClient:
case ServerOP_CZSignalClientByName:
case ServerOP_CZSignalGroup:
case ServerOP_CZSignalRaid:
case ServerOP_CZSignalGuild:
case ServerOP_CZTaskActivityResetPlayer:
case ServerOP_CZTaskActivityResetGroup:
case ServerOP_CZTaskActivityResetRaid:
case ServerOP_CZTaskActivityResetGuild:
case ServerOP_CZTaskActivityUpdatePlayer:
case ServerOP_CZTaskActivityUpdateGroup:
case ServerOP_CZTaskActivityUpdateRaid:
case ServerOP_CZTaskActivityUpdateGuild:
case ServerOP_CZTaskAssignPlayer:
case ServerOP_CZTaskAssignGroup:
case ServerOP_CZTaskAssignRaid:
case ServerOP_CZTaskAssignGuild:
case ServerOP_CZTaskDisablePlayer:
case ServerOP_CZTaskDisableGroup:
case ServerOP_CZTaskDisableRaid:
case ServerOP_CZTaskDisableGuild:
case ServerOP_CZTaskEnablePlayer:
case ServerOP_CZTaskEnableGroup:
case ServerOP_CZTaskEnableRaid:
case ServerOP_CZTaskEnableGuild:
case ServerOP_CZTaskFailPlayer:
case ServerOP_CZTaskFailGroup:
case ServerOP_CZTaskFailRaid:
case ServerOP_CZTaskFailGuild:
case ServerOP_CZTaskRemovePlayer:
case ServerOP_CZTaskRemoveGroup:
case ServerOP_CZTaskRemoveRaid:
case ServerOP_CZTaskRemoveGuild:
case ServerOP_CZDialogueWindow:
case ServerOP_CZLDoNUpdate:
case ServerOP_WWAssignTask:
case ServerOP_WWCastSpell:
case ServerOP_WWDisableTask:
case ServerOP_WWEnableTask:
case ServerOP_WWFailTask:
case ServerOP_CZMarquee:
case ServerOP_CZMessage:
case ServerOP_CZMove:
case ServerOP_CZSetEntityVariable:
case ServerOP_CZSignal:
case ServerOP_CZSpell:
case ServerOP_CZTaskUpdate:
case ServerOP_WWDialogueWindow:
case ServerOP_WWLDoNUpdate:
case ServerOP_WWMarquee:
case ServerOP_WWMessage:
case ServerOP_WWMove:
case ServerOP_WWMoveInstance:
case ServerOP_WWRemoveSpell:
case ServerOP_WWRemoveTask:
case ServerOP_WWResetActivity:
case ServerOP_WWSetEntityVariableClient:
case ServerOP_WWSetEntityVariableNPC:
case ServerOP_WWSignalClient:
case ServerOP_WWSignalNPC:
case ServerOP_WWUpdateActivity:
case ServerOP_WWSetEntityVariable:
case ServerOP_WWSignal:
case ServerOP_WWSpell:
case ServerOP_WWTaskUpdate:
case ServerOP_DepopAllPlayersCorpses:
case ServerOP_DepopPlayerCorpse:
case ServerOP_ReloadTitles:
case ServerOP_SpawnStatusChange:
case ServerOP_ReloadTasks:
case ServerOP_ReloadWorld:
case ServerOP_UpdateSpawn:
{
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ReloadTasks:
{
// world needs to update its copy of task data as well
shared_task_manager.LoadTaskData();
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ChangeSharedMem: {
std::string hotfix_name = std::string((char*)pack->pBuffer);
@@ -1362,7 +1312,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_CZClientMessageString:
{
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
client_list.SendPacket(buf->character_name, pack);
client_list.SendPacket(buf->client_name, pack);
break;
}
case ServerOP_ExpeditionLockout:
+5
View File
@@ -26,6 +26,7 @@ SET(zone_sources
dialogue_window.cpp
dynamic_zone.cpp
effects.cpp
elixir.cpp
embparser.cpp
embparser_api.cpp
embperl.cpp
@@ -80,6 +81,7 @@ SET(zone_sources
fearpath.cpp
forage.cpp
global_loot_manager.cpp
gm_commands/door_manipulation.cpp
groups.cpp
guild.cpp
guild_mgr.cpp
@@ -120,6 +122,7 @@ SET(zone_sources
perl_player_corpse.cpp
perl_questitem.cpp
perl_raids.cpp
perl_spell.cpp
perlpacket.cpp
petitions.cpp
pets.cpp
@@ -185,6 +188,7 @@ SET(zone_headers
doors.h
dialogue_window.h
dynamic_zone.h
elixir.h
embparser.h
embperl.h
embxs.h
@@ -198,6 +202,7 @@ SET(zone_headers
fastmath.h
forage.h
global_loot_manager.h
gm_commands/door_manipulation.h
groups.h
guild_mgr.h
hate_list.h
+14 -13
View File
@@ -69,10 +69,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
for (int x = 0; x < MAX_SWARM_PETS; x++)
{
if (spells[spell_id].effectid[x] == SE_TemporaryPets)
if (spells[spell_id].effect_id[x] == SE_TemporaryPets)
{
pet.count = spells[spell_id].base[x];
pet.duration = spells[spell_id].max[x];
pet.count = spells[spell_id].base_value[x];
pet.duration = spells[spell_id].max_value[x];
}
}
@@ -492,7 +492,8 @@ void Client::ResetAA() {
m_pp.raid_leadership_points = 0;
m_pp.group_leadership_exp = 0;
m_pp.raid_leadership_exp = 0;
database.DeleteCharacterAAs(CharacterID());
database.DeleteCharacterLeadershipAAs(CharacterID());
}
@@ -764,7 +765,7 @@ void Client::InspectBuffs(Client* Inspector, int Rank)
continue;
ib->spell_id[packet_index] = buffs[i].spellid;
if (Rank > 1)
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buff_duration_formula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
packet_index++;
}
@@ -904,8 +905,8 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
outapp->SetWritePosition(sizeof(AARankInfo_Struct));
for(auto &effect : rank->effects) {
outapp->WriteSInt32(effect.effect_id);
outapp->WriteSInt32(effect.base1);
outapp->WriteSInt32(effect.base2);
outapp->WriteSInt32(effect.base_value);
outapp->WriteSInt32(effect.limit_value);
outapp->WriteSInt32(effect.slot);
}
@@ -1229,7 +1230,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
}
//
// Modern clients don't require pet targeted for AA casts that are ST_Pet
if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet)
target_id = GetPetID();
// extra handling for cast_not_standing spells
@@ -1249,7 +1250,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
else {
// Bards can cast instant cast AAs while they are casting another song
if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) {
return;
}
ExpendAlternateAdvancementCharge(ability->id);
@@ -1285,8 +1286,8 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
}
for(auto &effect : rank->effects) {
if(effect.effect_id == SE_HastenedAASkill && effect.base2 == ability_in->id) {
return effect.base1;
if(effect.effect_id == SE_HastenedAASkill && effect.limit_value == ability_in->id) {
return effect.base_value;
}
}
}
@@ -1744,8 +1745,8 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
int rank_id = atoi(row[0]);
effect.slot = atoi(row[1]);
effect.effect_id = atoi(row[2]);
effect.base1 = atoi(row[3]);
effect.base2 = atoi(row[4]);
effect.base_value = atoi(row[3]);
effect.limit_value = atoi(row[4]);
if(effect.slot < 1)
continue;
+2 -2
View File
@@ -29,8 +29,8 @@ struct RankEffect
{
int slot;
int effect_id;
int base1;
int base2;
int base_value;
int limit_value;
};
}
+38 -26
View File
@@ -851,6 +851,17 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
size_mod *= RuleR(Combat,HitBoxMod); // used for testing sizemods on different races.
size_mod *= fixed_size_mod; // used to extend the size_mod
// Melee chasing fleeing mobs is borked. The client updates don't
// come to the server quickly enough, especially when mob is running
// and/or PC has good run speed. This change is a hack, but it greatly
// improved playability and "you are too far away" while chasing
// a fleeing mob. The Blind check is to make sure that this does not
// apply to disoriented fleeing mobs who need proximity to turn and fight.
if (other->currently_fleeing && !other->IsBlind())
{
size_mod *= 3;
}
// prevention of ridiculously sized hit boxes
if (size_mod > 10000)
size_mod = size_mod / 7;
@@ -863,7 +874,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
bool DoLoSCheck = true;
float max_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0));
float min_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
float min_distance = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2))
DoLoSCheck = false; //Ignore line of sight check
@@ -873,12 +884,12 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
max_dist = max_dist * max_dist;
if (!min_dist)
min_dist = size_mod; //Default to melee range
if (!min_distance)
min_distance = size_mod; //Default to melee range
else
min_dist = min_dist * min_dist;
min_distance = min_distance * min_distance;
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist))
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_distance && _DistNoRoot <= max_dist))
SetPseudoRoot(true);
else
SetPseudoRoot(false);
@@ -901,6 +912,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
}
return true;
}
return false;
}
@@ -991,16 +1003,16 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
default_aggro = target_hp / 15;
for (int o = 0; o < EFFECT_COUNT; o++) {
switch (spells[spell_id].effectid[o]) {
switch (spells[spell_id].effect_id[o]) {
case SE_CurrentHPOnce:
case SE_CurrentHP: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if(val < 0)
AggroAmount -= val;
break;
}
case SE_MovementSpeed: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount += default_aggro;
break;
@@ -1008,7 +1020,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
case SE_AttackSpeed:
case SE_AttackSpeed2:
case SE_AttackSpeed3: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 100)
AggroAmount += default_aggro;
break;
@@ -1026,7 +1038,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
break;
case SE_ACv2:
case SE_ArmorClass: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount += default_aggro;
break;
@@ -1044,19 +1056,19 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
case SE_INT:
case SE_WIS:
case SE_CHA: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount += 10;
break;
}
case SE_ResistAll: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount += 50;
break;
}
case SE_AllStats: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount += 70;
break;
@@ -1098,7 +1110,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
case SE_ManaRegen_v2:
case SE_ManaPool:
case SE_CurrentEndurance: {
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
if (val < 0)
AggroAmount -= val * 2;
break;
@@ -1110,7 +1122,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
break;
case SE_ReduceHate:
case SE_InstantHate:
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
break;
}
}
@@ -1121,8 +1133,8 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
if (dispel && target && target->GetHateAmount(this) < 100)
AggroAmount += 50;
if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash)
AggroAmount = spells[spell_id].HateAdded;
if (spells[spell_id].hate_added > 0) // overrides the hate (ex. tash)
AggroAmount = spells[spell_id].hate_added;
if (GetOwner() && IsPet())
AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;
@@ -1137,10 +1149,10 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
// initial aggro gets a bonus 100 besides for dispel or hate override
// We add this 100 in AddToHateList so we need to account for the oddities here
if (dispel && spells[spell_id].HateAdded > 0 && !on_hatelist)
if (dispel && spells[spell_id].hate_added > 0 && !on_hatelist)
AggroAmount -= 100;
return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro;
return AggroAmount + spells[spell_id].bonus_hate + nonModifiedAggro;
}
//healing and buffing aggro
@@ -1151,7 +1163,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default
for (int o = 0; o < EFFECT_COUNT; o++) {
switch (spells[spell_id].effectid[o]) {
switch (spells[spell_id].effect_id[o]) {
case SE_CurrentHP:
case SE_PercentalHeal:
{
@@ -1161,7 +1173,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
}
// hate based on base healing power of the spell
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o],
spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id);
spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id);
if (val > 0) {
if (heal_possible < val)
val = heal_possible; // capped to amount healed
@@ -1177,7 +1189,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
}
case SE_Rune:
AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o],
spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id) * 2;
spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id) * 2;
ignore_default_buff = true;
break;
case SE_HealOverTime:
@@ -1257,7 +1269,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
if(!caster) return false;
if(spells[spell_id].ResistDiff <= -600)
if(spells[spell_id].resist_difficulty <= -600)
return true;
float resist_check = 0;
@@ -1272,9 +1284,9 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
return true;
if (RuleB(Spells, CharismaCharmDuration))
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,false,0,true,true);
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster,false,0,true,true);
else
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true);
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0, false, true);
//2: The mob makes a resistance check against the charm
if (resist_check == 100)
@@ -1298,7 +1310,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
{
// Assume this is a harmony/pacify spell
// If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro.
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0,true);
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0,true);
if (resist_check == 100)
return true;
}
-1
View File
@@ -478,7 +478,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection,
row["has_temp_pets_active"] = mob->HasTempPetsActive();
row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
row["has_virus"] = mob->HasVirus();
row["hate_summon"] = mob->HateSummon();
row["helm_texture"] = mob->GetHelmTexture();
row["hp"] = mob->GetHP();
+321 -127
View File
@@ -59,6 +59,7 @@ extern FastMath g_Math;
extern EntityList entity_list;
extern Zone* zone;
//SYNC WITH: tune.cpp, mob.h TuneAttackAnimation
EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse)
{
// Determine animation
@@ -140,14 +141,30 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea
}
// If we're attacking with the secondary hand, play the dual wield anim
if (Hand == EQ::invslot::slotSecondary) // DW anim
if (Hand == EQ::invslot::slotSecondary) {// DW anim
type = animDualWield;
//allow animation chance to fire to be similar to your dw chance
if (GetDualWieldingSameDelayWeapons() == 2) {
SetDualWieldingSameDelayWeapons(3);
}
}
//If both weapons have same delay this allows a chance for DW animation
if (GetDualWieldingSameDelayWeapons() && Hand == EQ::invslot::slotPrimary) {
if (GetDualWieldingSameDelayWeapons() == 3 && zone->random.Roll(50)) {
type = animDualWield;
SetDualWieldingSameDelayWeapons(2);//Don't roll again till you do another dw attack.
}
SetDualWieldingSameDelayWeapons(2);//Ensures first attack is always primary.
}
DoAnim(type, 0, false);
return skillinuse;
}
//SYNC WITH: tune.cpp, mob.h Tunecompute_tohit
int Mob::compute_tohit(EQ::skills::SkillType skillinuse)
{
int tohit = GetSkill(EQ::skills::SkillOffense) + 7;
@@ -168,6 +185,7 @@ int Mob::compute_tohit(EQ::skills::SkillType skillinuse)
}
// return -1 in cases that always hit
//SYNC WITH: tune.cpp, mob.h TuneGetTotalToHit
int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
{
if (chance_mod >= 10000) // override for stuff like SE_SkillAttack
@@ -231,6 +249,7 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
// based on dev quotes
// the AGI bonus has actually drastically changed from classic
//SYNC WITH: tune.cpp, mob.h Tunecompute_defense
int Mob::compute_defense()
{
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
@@ -259,6 +278,7 @@ int Mob::compute_defense()
}
// return -1 in cases that always miss
// SYNC WITH : tune.cpp, mob.h TuneGetTotalDefense()
int Mob::GetTotalDefense()
{
auto avoidance = compute_defense() + 10; // add 10 in case the NPC's stats are fucked
@@ -286,6 +306,7 @@ int Mob::GetTotalDefense()
// called when a mob is attacked, does the checks to see if it's a hit
// and does other mitigation checks. 'this' is the mob being attacked.
// SYNC WITH : tune.cpp, mob.h TuneCheckHitChance()
bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit)
{
#ifdef LUA_EQEMU
@@ -382,18 +403,32 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
ultimately end up being more useful as fields in npc_types.
*/
int counter_all = 0;
int counter_all = 0;
int counter_riposte = 0;
int counter_block = 0;
int counter_parry = 0;
int counter_dodge = 0;
int counter_block = 0;
int counter_parry = 0;
int counter_dodge = 0;
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)) {
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 1);
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
}
int modify_all = 0;
int modify_riposte = 0;
int modify_block = 0;
int modify_parry = 0;
int modify_dodge = 0;
if (GetSpecialAbility(MODIFY_AVOID_DAMAGE)) {
modify_all = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 0);
modify_riposte = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 1);
modify_block = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 2);
modify_parry = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 3);
modify_dodge = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 4);
}
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
@@ -420,6 +455,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
float counter = (counter_riposte + counter_all) / 100.0f;
chance -= chance * counter;
}
if (modify_riposte || modify_all) {
float npc_modifier = (modify_riposte + modify_all) / 100.0f;
chance += chance * npc_modifier;
}
// AA Slippery Attacks
if (hit.hand == EQ::invslot::slotSecondary) {
int slip = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail;
@@ -459,6 +498,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
float counter = (counter_block + counter_all) / 100.0f;
chance -= chance * counter;
}
if (modify_block || modify_all) {
float npc_modifier = (modify_block + modify_all) / 100.0f;
chance += chance * npc_modifier;
}
if (zone->random.Roll(chance)) {
hit.damage_done = DMG_BLOCKED;
return true;
@@ -482,6 +525,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
float counter = (counter_parry + counter_all) / 100.0f;
chance -= chance * counter;
}
if (modify_parry || modify_all) {
float npc_modifier = (modify_parry + modify_all) / 100.0f;
chance += chance * npc_modifier;
}
if (zone->random.Roll(chance)) {
hit.damage_done = DMG_PARRIED;
return true;
@@ -505,6 +552,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
float counter = (counter_dodge + counter_all) / 100.0f;
chance -= chance * counter;
}
if (modify_dodge || modify_all) {
float npc_modifier = (modify_dodge + modify_all) / 100.0f;
chance += chance * npc_modifier;
}
if (zone->random.Roll(chance)) {
hit.damage_done = DMG_DODGED;
return true;
@@ -788,7 +839,7 @@ int Mob::GetClassRaceACBonus()
return ac_bonus;
}
//SYNC WITH: tune.cpp, mob.h TuneACSum
int Mob::ACSum(bool skip_caps)
{
int ac = 0; // this should be base AC whenever shrouds come around
@@ -863,7 +914,7 @@ int Mob::ACSum(bool skip_caps)
}
int Mob::GetBestMeleeSkill()
{
{
int bestSkill=0;
EQ::skills::SkillType meleeSkills[]=
@@ -885,8 +936,8 @@ int Mob::GetBestMeleeSkill()
}
return bestSkill;
}
}
//SYNC WITH: tune.cpp, mob.h Tuneoffense
int Mob::offense(EQ::skills::SkillType skill)
{
int offense = GetSkill(skill);
@@ -941,7 +992,7 @@ double Mob::RollD20(int offense, int mitigation)
return mods[index];
}
//SYNC WITH: tune.cpp, mob.h TuneMeleeMitigation
void Mob::MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts)
{
#ifdef LUA_EQEMU
@@ -990,32 +1041,35 @@ int Mob::GetWeaponDamage(Mob *against, const EQ::ItemData *weapon_item) {
//check to see if our weapons or fists are magical.
if (against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)) {
if (weapon_item) {
if (GetSpecialAbility(SPECATK_MAGICAL)) {
dmg = 1;
}
//On live this occurs for ALL NPC's >= 10
else if (IsNPC() && GetLevel() >= RuleI(Combat, NPCAttackMagicLevel)) {
dmg = 1;
}
else if (weapon_item) {
if (weapon_item->Magic) {
dmg = weapon_item->Damage;
//this is more for non weapon items, ex: boots for kick
//they don't have a dmg but we should be able to hit magical
dmg = dmg <= 0 ? 1 : dmg;
if (weapon_item->Damage && (weapon_item->IsType1HWeapon() || weapon_item->IsType2HWeapon())) {
dmg = weapon_item->Damage;
}
//Non weapon items, ie. boots for kick.
else if (weapon_item->ItemType == EQ::item::ItemTypeArmor) {
dmg = 1;
}
else {
return 0;
}
}
else
else {
return 0;
}
}
else if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) {
dmg = GetHandToHandDamage();
}
else {
if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) {
dmg = GetHandToHandDamage();
}
else if (GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)) {
//pets wouldn't actually use this but...
//it gives us an idea if we can hit due to the dual nature of this function
dmg = 1;
}
else if (GetSpecialAbility(SPECATK_MAGICAL))
{
dmg = 1;
}
else
return 0;
return 0;
}
}
else {
@@ -1314,6 +1368,7 @@ int Client::DoDamageCaps(int base_damage)
}
// other is the defender, this is the attacker
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts)
{
if (!other)
@@ -1371,6 +1426,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts)
//note: throughout this method, setting `damage` to a negative is a way to
//stop the attack calculations
// IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.)
//SYNC WITH: tune.cpp, mob.h TuneClientAttack
bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
{
if (!other) {
@@ -1552,7 +1608,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff);
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty);
}
other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks);
@@ -1628,9 +1684,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
if (!spell)
spell = SPELL_UNKNOWN;
char buffer[48] = { 0 };
snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
if (parse->EventPlayer(EVENT_DEATH, this, buffer, 0) != 0) {
std::string export_string = fmt::format(
"{} {} {} {}",
killerMob ? killerMob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
@@ -1685,11 +1746,17 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
// #2: figure out things that affect the player dying and mark them dead
InterruptSpell();
Mob* m_pet = GetPet();
SetPet(0);
SetHorseId(0);
ShieldAbilityClearVariables();
dead = true;
if (m_pet && m_pet->IsCharmed()) {
m_pet->BuffFadeByEffect(SE_Charm);
}
if (GetMerc()) {
GetMerc()->Suspend();
}
@@ -1926,10 +1993,10 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc);
}
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0);
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0);
return true;
}
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
{
if (!other) {
@@ -2181,7 +2248,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQ::skills::SkillTyp
if (IsLDoNTrapped())
{
MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2);
SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].resist_difficulty, false);
SetLDoNTrapSpellID(0);
SetLDoNTrapped(false);
SetLDoNTrapDetected(false);
@@ -2206,11 +2273,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
Mob *oos = nullptr;
if (killer_mob) {
oos = killer_mob->GetOwnerOrSelf();
char buffer[48] = { 0 };
snprintf(buffer, 47, "%d %d %d %d", killer_mob->GetID(), damage, spell, static_cast<int>(attack_skill));
if (parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0) {
std::string buffer = fmt::format("{} {} {} {}", killer_mob->GetID(), damage, spell, static_cast<int>(attack_skill));
if (parse->EventNPC(EVENT_DEATH, this, oos, buffer.c_str(), 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
@@ -2233,10 +2297,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
}
}
else {
char buffer[48] = { 0 };
snprintf(buffer, 47, "%d %d %d %d", 0, damage, spell, static_cast<int>(attack_skill));
if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0) {
std::string buffer = fmt::format("{} {} {} {}", 0, damage, spell, static_cast<int>(attack_skill));
if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer.c_str(), 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
@@ -2357,9 +2419,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
give_exp_client->GetCleanName(),
GetNPCTypeID()
);
give_exp_client
->GetTaskState()
->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID());
task_manager->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID());
}
if (kr) {
@@ -2629,16 +2689,15 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
entity_list.UpdateFindableNPCState(this, true);
char buffer[48] = { 0 };
snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0);
std::string buffer = fmt::format("{} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer.c_str(), 0);
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
if (RuleB(Zone, UseZoneController)) {
if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && this->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) {
char data_pass[100] = { 0 };
snprintf(data_pass, 99, "%d %d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill), this->GetNPCTypeID());
parse->EventNPC(EVENT_DEATH_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
if (controller && GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) {
std::string data_pass = fmt::format("{} {} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill), GetNPCTypeID());
parse->EventNPC(EVENT_DEATH_ZONE, controller, nullptr, data_pass.c_str(), 0);
}
}
@@ -2701,8 +2760,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
}
}
if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0))
TryTriggerOnValueAmount(false, false, false, true);
if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) {
TryTriggerOnCastRequirement();
}
if (IsClient() && !IsAIControlled())
return;
@@ -2876,10 +2936,15 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
spellid = spellbonuses.DamageShieldSpellID;
}
else {
DS = spellbonuses.SpellDamageShield;
DS = spellbonuses.SpellDamageShield + itembonuses.SpellDamageShield + aabonuses.SpellDamageShield;
rev_ds = 0;
// This ID returns "you are burned", seemed most appropriate for spell DS
spellid = 2166;
/*
Live Message - not yet used on emu
Feedback onto you "YOUR mind burns from TARGETS NAME's feedback for %i points of non-melee damage."
Feedback onto other "TARGETS NAME's mind burns from YOUR feedback for %i points of non-melee damage."
*/
}
if (DS == 0 && rev_ds == 0)
@@ -2913,6 +2978,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
DS -= DS * ds_mitigation / 100;
}
attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false);
//we can assume there is a spell now
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
@@ -3128,7 +3194,7 @@ int32 Mob::ReduceDamage(int32 damage)
if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) {
slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT];
if (slot >= 0) {
if (--buffs[slot].numhits == 0) {
if (--buffs[slot].hit_number == 0) {
if (!TryFadeEffect(slot))
BuffFadeBySlot(slot, true);
@@ -3217,7 +3283,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
if (!iBuffTic && spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) {
slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT];
if (slot >= 0) {
if (--buffs[slot].numhits == 0) {
if (--buffs[slot].hit_number == 0) {
if (!TryFadeEffect(slot))
BuffFadeBySlot(slot, true);
@@ -3343,7 +3409,7 @@ int32 Mob::ReduceAllDamage(int32 damage)
if (GetMana() >= mana_reduced) {
damage -= mana_reduced;
SetMana(GetMana() - mana_reduced);
TryTriggerOnValueAmount(false, true);
TryTriggerOnCastRequirement();
}
}
@@ -3356,7 +3422,7 @@ int32 Mob::ReduceAllDamage(int32 damage)
if (IsClient() && CastToClient()->GetEndurance() >= endurance_drain) {
damage -= damage_reduced;
CastToClient()->SetEndurance(CastToClient()->GetEndurance() - endurance_drain);
TryTriggerOnValueAmount(false, false, true);
TryTriggerOnCastRequirement();
}
}
@@ -3367,17 +3433,37 @@ int32 Mob::ReduceAllDamage(int32 damage)
bool Mob::HasProcs() const
{
for (int i = 0; i < MAX_PROCS; i++)
if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN)
for (int i = 0; i < MAX_PROCS; i++) {
if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) {
return true;
}
}
if (IsClient()) {
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (aabonuses.SpellProc[i]) {
return true;
}
}
}
return false;
}
bool Mob::HasDefensiveProcs() const
{
for (int i = 0; i < MAX_PROCS; i++)
if (DefensiveProcs[i].spellID != SPELL_UNKNOWN)
for (int i = 0; i < MAX_PROCS; i++) {
if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) {
return true;
}
}
if (IsClient()) {
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (aabonuses.DefensiveProc[i]) {
return true;
}
}
}
return false;
}
@@ -3402,9 +3488,19 @@ bool Mob::HasSkillProcSuccess() const
bool Mob::HasRangedProcs() const
{
for (int i = 0; i < MAX_PROCS; i++)
if (RangedProcs[i].spellID != SPELL_UNKNOWN)
for (int i = 0; i < MAX_PROCS; i++){
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
return true;
}
}
if (IsClient()) {
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (aabonuses.RangedProc[i]) {
return true;
}
}
}
return false;
}
@@ -3524,7 +3620,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) {
int healed = damage;
healed = attacker->GetActSpellHealing(spell_id, healed);
healed = RuleB(Spells, CompoundLifetapHeals) ? attacker->GetActSpellHealing(spell_id, healed) : healed;
LogCombat("Applying lifetap heal of [{}] to [{}]", healed, attacker->GetName());
attacker->HealDamage(healed);
@@ -3646,7 +3742,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
TryDeathSave();
}
TryTriggerOnValueAmount(true);
TryTriggerOnCastRequirement();
//fade mez if we are mezzed
if (IsMezzed() && attacker) {
@@ -4038,33 +4134,61 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
return;
}
if (!HasDefensiveProcs())
if (!HasDefensiveProcs()) {
return;
}
if (!on->HasDied() && on->GetHP() > 0) {
float ProcChance, ProcBonus;
on->GetDefensiveProcChances(ProcBonus, ProcChance, hand, this);
if (hand != EQ::invslot::slotPrimary)
if (hand == EQ::invslot::slotSecondary) {
ProcChance /= 2;
}
int level_penalty = 0;
int level_diff = GetLevel() - on->GetLevel();
if (level_diff > 6)//10% penalty per level if > 6 levels over target.
if (level_diff > 6) {//10% penalty per level if > 6 levels over target.
level_penalty = (level_diff - 6) * 10;
}
ProcChance -= ProcChance*level_penalty / 100;
if (ProcChance < 0)
if (ProcChance < 0) {
return;
}
//Spell Procs and Quest added procs
for (int i = 0; i < MAX_PROCS; i++) {
if (IsValidSpell(DefensiveProcs[i].spellID)) {
float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID);
if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc)) {
float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID);
SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc);
}
}
}
}
//AA Procs
if (IsClient()){
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
int32 aa_rank_id = aabonuses.DefensiveProc[i];
int32 aa_spell_id = aabonuses.DefensiveProc[i + 1];
int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + 2];
uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + 3];
if (aa_rank_id) {
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc)) {
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
ExecWeaponProc(nullptr, aa_spell_id, on);
SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc);
}
}
}
}
}
@@ -4109,7 +4233,9 @@ void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand)
void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand)
{
if (!on) {
return;
}
if (!weapon)
return;
uint16 skillinuse = 28;
@@ -4119,7 +4245,7 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon
ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
float ProcChance = GetProcChances(ProcBonus, hand);
if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate?
if (hand == EQ::invslot::slotSecondary)
ProcChance /= 2;
// Try innate proc on weapon
@@ -4193,12 +4319,16 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon
void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand)
{
if (!on) {
return;
}
float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
itembonuses.SpellProcChance + aabonuses.SpellProcChance);
float ProcChance = 0.0f;
ProcChance = GetProcChances(ProcBonus, hand);
if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate?
if (hand == EQ::invslot::slotSecondary)
ProcChance /= 2;
bool rangedattk = false;
@@ -4226,7 +4356,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
// Not ranged
if (!rangedattk) {
// Perma procs (AAs)
// Perma procs (Not used for AA, they are handled below)
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus?
LogCombat("Permanent proc [{}] procing spell [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
@@ -4243,32 +4373,79 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
poison_slot=i;
continue; // Process the poison proc last per @mackal
}
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
SendBeginCast(SpellProcs[i].spellID, 0);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
SpellProcs[i].base_spellID);
}
else {
LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
if (!IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc)) {
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
SendBeginCast(SpellProcs[i].spellID, 0);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID);
}
else {
LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
}
}
}
}
else if (rangedattk) { // ranged only
// ranged spell procs (buffs)
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
RangedProcs[i].base_spellID);
if (!IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc)) {
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) {
LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID);
SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc);
}
else {
LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
}
}
else {
LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
}
}
}
//AA Procs
if (IsClient()) {
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
int32 aa_rank_id = 0;
int32 aa_spell_id = SPELL_UNKNOWN;
int32 aa_proc_chance = 100;
uint32 aa_proc_reuse_timer = 0;
int proc_type = 0; //used to deterimne which timer array is used.
if (!rangedattk) {
aa_rank_id = aabonuses.SpellProc[i];
aa_spell_id = aabonuses.SpellProc[i + 1];
aa_proc_chance += aabonuses.SpellProc[i + 2];
aa_proc_reuse_timer = aabonuses.SpellProc[i + 3];
proc_type = SE_WeaponProc;
}
else {
aa_rank_id = aabonuses.RangedProc[i];
aa_spell_id = aabonuses.RangedProc[i + 1];
aa_proc_chance += aabonuses.RangedProc[i + 2];
aa_proc_reuse_timer = aabonuses.RangedProc[i + 3];
proc_type = SE_RangedProc;
}
if (aa_rank_id) {
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) {
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance);
ExecWeaponProc(nullptr, aa_spell_id, on);
SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, proc_type);
}
else {
LogCombat("AA proc [{}] failed to proc [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance);
}
}
}
}
@@ -4905,14 +5082,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base[i];
ProcMod = static_cast<float>(spells[base_spell_id].base2[i]);
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base_value[i];
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
}
else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) {
else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) {
if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) {
if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) {
float final_chance = chance * (ProcMod / 100.0f);
if (zone->random.Roll(final_chance)) {
ExecWeaponProc(nullptr, proc_spell_id, on);
@@ -4949,14 +5126,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
ProcMod = 0;
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base[i];
ProcMod = static_cast<float>(spells[base_spell_id].base2[i]);
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base_value[i];
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
}
else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) {
else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) {
if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) {
if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) {
float final_chance = chance * (ProcMod / 100.0f);
if (zone->random.Roll(final_chance)) {
ExecWeaponProc(nullptr, proc_spell_id, on);
@@ -4978,8 +5155,8 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
CanProc = true;
uint32 effect_id = 0;
int32 base1 = 0;
int32 base2 = 0;
int32 base_value = 0;
int32 limit_value = 0;
uint32 slot = 0;
for (int e = 0; e < MAX_SKILL_PROCS; e++) {
@@ -5007,17 +5184,17 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
for (auto &effect : rank->effects) {
effect_id = effect.effect_id;
base1 = effect.base1;
base2 = effect.base2;
base_value = effect.base_value;
limit_value = effect.limit_value;
slot = effect.slot;
if (effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) {
proc_spell_id = base1;
ProcMod = static_cast<float>(base2);
proc_spell_id = base_value;
ProcMod = static_cast<float>(limit_value);
}
else if (effect_id == SE_LimitToSkill && base1 <= EQ::skills::HIGHEST_SKILL) {
else if (effect_id == SE_LimitToSkill && base_value <= EQ::skills::HIGHEST_SKILL) {
if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) {
if (CanProc && base_value == skill && IsValidSpell(proc_spell_id)) {
float final_chance = chance * (ProcMod / 100.0f);
if (zone->random.Roll(final_chance)) {
@@ -5046,7 +5223,7 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) {
if (!ReuseTime && hand) {
weapon_speed = GetWeaponSpeedbyHand(hand);
ProcChance = static_cast<float>(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f);
if (hand != EQ::invslot::slotPrimary)
if (hand == EQ::invslot::slotSecondary)
ProcChance /= 2;
}
@@ -5387,6 +5564,8 @@ void Mob::SetAttackTimer()
void Client::SetAttackTimer()
{
float haste_mod = GetHaste() * 0.01f;
int primary_speed = 0;
int secondary_speed = 0;
//default value for attack timer in case they have
//an invalid weapon equipped:
@@ -5464,6 +5643,21 @@ void Client::SetAttackTimer()
speed = static_cast<int>(speed + ((hhe / 100.0f) * delay));
}
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true);
if (i == EQ::invslot::slotPrimary) {
primary_speed = speed;
}
else if (i == EQ::invslot::slotSecondary) {
secondary_speed = speed;
}
}
//To allow for duel wield animation to display correctly if both weapons have same delay
if (primary_speed == secondary_speed) {
SetDualWieldingSameDelayWeapons(1);
}
else {
SetDualWieldingSameDelayWeapons(0);
}
}
+4 -4
View File
@@ -98,7 +98,7 @@ bool Beacon::Process()
{
// NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
// I don't think any other cases that get here matter
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster;
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].target_type != ST_AECaster;
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
}
else
@@ -127,10 +127,10 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj
caster_id = caster->GetID();
spell_id = cast_spell_id;
this->resist_adjust = resist_adjust;
spell_iterations = spells[spell_id].AEDuration / 2500;
spell_iterations = spells[spell_id].aoe_duration / 2500;
spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1
if (spells[spell_id].aemaxtargets)
max_targets = spells[spell_id].aemaxtargets;
if (spells[spell_id].aoe_max_targets)
max_targets = spells[spell_id].aoe_max_targets;
spell_timer.Start(2500);
spell_timer.Trigger();
}

Some files were not shown because too many files have changed in this diff Show More