585 Commits

Author SHA1 Message Date
Filip Maj
afe79e6c5f Set version number to v0.1. 2017-08-26 14:00:40 -04:00
Joseph Mikel Keller
d757ad39fc Merged in dude22072/ffxiv-classic-server-1/develop (pull request #47)
Fixed the "pretty borders" inconsistency.

Approved-by: Tahir Akhlaq <xdemolish@hotmail.com>
2017-08-26 17:59:14 +00:00
Filip Maj
74ce5a2fe1 Merge branch 'master' into develop
# Conflicts:
#	FFXIVClassic Lobby Server/FFXIVClassic Lobby Server.csproj
#	FFXIVClassic Map Server/Database.cs
#	FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
#	FFXIVClassic Map Server/PacketProcessor.cs
#	FFXIVClassic Map Server/WorldManager.cs
#	FFXIVClassic Map Server/actors/Actor.cs
#	FFXIVClassic Map Server/actors/area/Area.cs
#	FFXIVClassic Map Server/actors/chara/player/Player.cs
#	FFXIVClassic Map Server/lua/LuaPlayer.cs
#	data/scripts/base/chara/npc/populace/PopulaceChocoboLender.lua
#	data/scripts/commands/gm/speed.lua
#	data/scripts/commands/gm/warp.lua
#	data/scripts/global.lua
#	data/scripts/player.lua
#	data/scripts/unique/fst0Town01a/PopulaceStandard/khuma_moshroca.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/ahldskyf.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/angry_river.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/ansgor.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/arnegis.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/arthurioux.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/astrid.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/audaine.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/bango_zango.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/bayard.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/bloemerl.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/bmallpa.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/bnhapla.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/chichiroon.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/drowsy-eyed_adventurer.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/eugennoix.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/fickle_beggar.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/hob.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/ivan.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/maetistym.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/mharelak.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/muscle-bound_deckhand.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/pasty-faced_adventurer.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/pearly-toothed_porter.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/ptahjha.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/suspicious-looking_traveler.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/syhrdaeg.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/syngsmyd.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/tatasako.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/tefh_moshroca.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/thata_khamazom.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/thosinbaen.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/tittering_traveler.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/totoruto.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/triaine.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/trinne.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/unconscious_adventurer.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/undsatz.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/vhynho.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/waekbyrt.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/whahtoa.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/wyra_khamazom.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/wyrstmann.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/xavalien.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/zonggo.lua
#	data/scripts/unique/sea0Town01a/PopulaceStandard/zuzule.lua
#	sql/server_zones_spawnlocations.sql
2017-08-26 13:53:23 -04:00
Filip Maj
3b271980cc Added more Ul'dah scripts. Start location fix now only happens in correct zones. 2017-08-26 13:11:25 -04:00
Filip Maj
ff93dc41ef Added casting bar functions. 2017-08-26 12:33:37 -04:00
Filip Maj
546908acde Some fixes and missing scripts. 2017-08-26 10:23:13 -04:00
Filip Maj
372d50d7eb Added closed doors to the two private areas for the ul'dah opening. CAN'T ESCAPE NOW!!!! 2017-07-09 19:13:17 -04:00
Filip Maj
bac901c437 Added warp after the ul'dah battle to the private area. 2017-07-09 18:49:12 -04:00
Filip Maj
69f7fb5e47 Ported over all the opening directors and improved the director code a bit more. Also implemented content instances for Grid/Uld. 2017-07-09 18:38:01 -04:00
Filip Maj
24c46c0480 Fixed issue where any actor < -1000 Z position would appear anyways. 2017-07-09 12:58:22 -04:00
Filip Maj
affff96837 A default return destination is now set on new character based on initialTown. 2017-07-09 11:40:28 -04:00
Filip Maj
77d6cb2e43 Fixed up how "special" items work (ie: PUG and ARC weapons). No more glitches hopefully. 2017-07-09 11:39:17 -04:00
dude22072
5a5c0a3a5f Fixed the "pretty borders" inconsistency. 2017-07-06 22:30:03 -05:00
Filip Maj
caf254fd95 More old test code removed. 2017-07-06 22:11:14 -04:00
Filip Maj
151756329c Removed oooold test code. 2017-07-06 22:10:19 -04:00
Filip Maj
ddfc71d3af GLWarpPoint shows current difficulty now too. Added some test stuff to the Orb/Sweep directors. 2017-07-06 22:09:03 -04:00
Filip Maj
a77a90578f Merge branch 'packet_actortarget_rewrite' into guildleve
# Conflicts:
#	FFXIVClassic Lobby Server/packets/send/SelectCharacterConfirmPacket.cs
2017-07-06 22:02:14 -04:00
Filip Maj
8c95543f6d Missing lobby packet???? 2017-07-06 21:59:29 -04:00
Filip Maj
5dff4cbdd3 Fixed bug with Broadcast Packet. Was adding player to the instance list while it shouldn't have been, and designed BroadcastPacket assuming so. Cleaned it all up and fixed, so now emote work after warp. Fixed Chocobo packets so correct appearance will show when player is spawned while riding gobbue. 2017-07-06 21:59:12 -04:00
Filip Maj
fc0c504c28 Merge branch 'packet_actortarget_rewrite' into guildleve 2017-06-29 11:39:02 -04:00
Filip Maj
f12ffc468d Updated lobby code for new Subpacket type. 2017-06-29 11:38:28 -04:00
Filip Maj
33e580ed38 Removed redundant queuepacket func. 2017-06-27 21:40:49 -04:00
Filip Maj
ab3e152b7d Fixed spawning being broken. 2017-06-27 21:30:32 -04:00
Filip Maj
3370309dd0 Cleaned up the code and fixed a bunch of bugs. Map server will no longer worry about base packets. 2017-06-27 21:08:30 -04:00
Filip Maj
4aa3a444d0 Messages for the Aetheryte Node appearing. 2017-06-27 20:17:24 -04:00
Filip Maj
08477780f8 Removed duplicate 0xD9 packet. Renamed 0xD8 to SetActorBGPropertiesPacket. 2017-06-27 17:37:13 -04:00
Filip Maj
516564a896 Refactored world server. 2017-06-27 17:31:17 -04:00
Filip Maj
9353f77db0 Removed duped battle packet. 2017-06-27 17:12:34 -04:00
Filip Maj
1339975ade Cleaned up inventory packets. 2017-06-27 17:07:29 -04:00
Filip Maj
7c4091a196 Finished refactoring Map Server. Clear of syntax errors. 2017-06-27 16:55:14 -04:00
Filip Maj
d8ac8216c4 More subpacket cleanup. 2017-06-27 13:52:47 -04:00
Filip Maj
a264745207 Started removing all target actor id references. 2017-06-27 13:23:05 -04:00
Filip Maj
c09471ed43 GL completion animation will only play to self now. 2017-06-26 00:14:26 -04:00
Filip Maj
00757b4ae7 Fixed dumb syntax error. 2017-06-25 23:43:02 -04:00
Filip Maj
fc96dac7c7 Fixed work values being totally wrong for guildleves. Added Add/Remove GL command. 2017-06-25 23:35:55 -04:00
Filip Maj
b5db036d71 Abandoning/Completing GLs now marks them correctly in the Journal. 2017-06-25 23:02:20 -04:00
Filip Maj
573b9a7202 Updated the GLWarpPoint actorClass to be clickable. 2017-06-25 22:44:48 -04:00
Filip Maj
e5afe8791b Implemented the ending portion of the guildleve. 2017-06-25 22:43:15 -04:00
Filip Maj
cd0bb10ef6 Forgot to push GL scripts. 2017-06-25 20:43:23 -04:00
Filip Maj
c071b9d684 Pushing new Aetheryte scripts that handle when GL is active. Finally added an error check for some lua callbacks. 2017-06-25 15:11:35 -04:00
Filip Maj
019e305525 Fixed redundant looping when removing/finishing a director. Fixed issue where packets to delete content group wouldn't send. Fixed issue where ContentGroups/Directors weren't clearing after deletion causing a growing amount of zombie groups/directors. 2017-06-25 14:52:32 -04:00
Filip Maj
875b76634a Implemented more of the Guildleve Director. Correct script is now autoloaded based on leveplate. Players are now added to the contentgroup on leve start. Moved animation and stuff to C# side of things. Cleaned up code. 2017-06-25 14:25:54 -04:00
Filip Maj
c42f1a08de Changed to Tutorial GL script as that is the correct one for our test GL. 2017-06-24 17:18:28 -04:00
Filip Maj
c78fa033af Fixed sbytes not processing correctly for work updates. Added a sync all function to guildleves. 2017-06-24 17:14:18 -04:00
Filip Maj
8fb4910320 Fixed work values not updating. GL UI now shows up though it isn't configured. 2017-06-24 16:58:02 -04:00
Filip Maj
186d5b5cc5 Had a bug where actor id was set to the wrong packet. Will have to also look at this target thing later. 2017-06-24 16:29:13 -04:00
Filip Maj
25cd75d40c Added way to instantiate a GL Director. Aetheryte parent now does it. 2017-06-24 15:57:46 -04:00
Filip Maj
8ccd3439c1 Added marker update method for guildleves. 2017-06-24 15:54:02 -04:00
Filip Maj
bfaa25cb39 Forgot the aetheryte script. 2017-06-24 15:51:06 -04:00
Filip Maj
c7acef20a9 Added missing gl scripts. 2017-06-24 15:50:03 -04:00
Filip Maj
a80c80454b Added more work values and guildleve funcs. Also realized target will always == the queuedpacket session. This will make broadcasting packets easier once cleaned up. 2017-06-24 15:47:53 -04:00
Filip Maj
eb17da1c89 Modified how directors work to allow for persistence. Implemented GuildleveDirector object and it's work values. 2017-06-24 15:31:42 -04:00
Filip Maj
2d7d10a417 Renamed Item dataobject to ItemData. Added guildleve data. 2017-06-24 14:12:52 -04:00
Filip Maj
44a76c94af Added guildleve helper funcs. 2017-06-24 12:07:27 -04:00
Filip Maj
f00e878dd0 Fixed session issue. Still a stopgap until d/cing is implemented. 2017-06-19 23:11:24 -04:00
Filip Maj
0c8642d6ab Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop 2017-06-19 22:24:45 -04:00
Filip Maj
793f5323fb DB update for inn door fixes. Also YoshiP added. 2017-06-19 22:24:31 -04:00
Filip Maj
7f6b291366 Added script for ItemStorage npc. Added scripts for both types of inn exit doors. Added BountyPresenter script (YoshiP). 2017-06-19 22:23:27 -04:00
Filip Maj
c1d67538f3 Added item storage packet for armoire. 2017-06-19 22:21:21 -04:00
Filip Maj
dbaea65c19 Fixed how login/zoning session start is handled. Should fix the bug where a player could not relogin into a server twice. 2017-06-19 16:30:04 -04:00
Filip Maj
ed5ee01ba6 Firedance description was being referenced wrong. 2017-06-15 01:21:16 -04:00
Filip Maj
b838027cba Partially figured out the SetSpecialEventWork Packet. 2017-06-15 01:18:57 -04:00
Filip Maj
1d9f57a385 Cleaned up the emotes code. 2017-06-15 00:19:18 -04:00
Filip Maj
0e8de966a9 Fixed emotes, they all work now. 2017-06-15 00:05:14 -04:00
Filip Maj
cdfa2f682e Fixed the broken elevator in Ul'dah. Finished the one in Limsa. 2017-06-14 22:41:39 -04:00
Filip Maj
866e2d3559 Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop 2017-06-14 22:24:26 -04:00
Filip Maj
d96d5b29a5 Fixed issue where spawn packets were sent on zone out. Added all npcs to Camp Bearded Rock. Rewrote how mapobjs get loaded in, no more hardcodes. Added tons mapobjs to the DB. Added ferry npcs. 2017-06-14 22:24:09 -04:00
CuriousJorge
fdf30b4615 Merged in CuriousJorge/ffxiv-classic-server/develop (pull request #45)
Applying bugfix from Master to Develop.
2017-06-15 01:59:00 +00:00
CuriousJorge
cfb3a473c1 Applying bugfix from Master to Develop.
Fixes the issue where actors are spawning in with default movement speed instead of their current.
2017-06-06 17:33:02 -04:00
Joseph Mikel Keller
5c350e4ffa Merged in dude22072/ffxiv-classic-server-1/dude22072/databasephp-edited-online-with-bitbucket-1492196780169 (pull request #42)
Tells user incorrect username/password instead of "VerifyUser failed."
2017-06-06 16:43:13 +00:00
Tahir Akhlaq
4e8ce558a7 Merged in takhlaq/ffxiv-classic-server/develop (pull request #44)
fixed some races
2017-06-06 16:42:14 +00:00
Tahir Akhlaq
72099e92bb fixed some races 2017-06-06 02:11:45 +01:00
Filip Maj
52cacb1ded Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop
# Conflicts:
#	FFXIVClassic Map Server/packets/send/Actor/SetActorSubStatPacket.cs
2017-05-16 23:44:42 -04:00
Filip Maj
c67c5f3bdf Bug fix on broadcast packet method, filled out what 0x144 does. 2017-05-16 23:43:53 -04:00
Filip Maj
5d65965ec0 Bug fix on broadcast packet method, filled out what 0x144 does. 2017-05-16 23:43:07 -04:00
Filip Maj
08b3564d7e Added things for testing mapobjs. 2017-05-05 02:37:01 -04:00
Filip Maj
9505cd71be Repositioned LaNoscea and Shroud Aetherytes. Linked them up too. Teleporting to one now causes you to appear *near* it, and facing it. 2017-05-02 21:44:44 -04:00
Filip Maj
844f21d9a4 Finished off the teleport script. Still requires all the aetheryte positions filled out. 2017-05-01 22:55:47 -04:00
Filip Maj
1516e0bc50 Added homepoint and aetheryte code. You can set the homepoint on an aetheryte or inn and the return menu will show the correct response. Added effects/msgs to teleport/return. Some sql changes I forgot. 2017-05-01 22:30:43 -04:00
Filip Maj
8c9ecebae6 Added a "silent" option for LuaEngine calls. More content instance work. Full classpath now used for zones. 2017-04-29 20:30:54 -04:00
Filip Maj
cc44d6b63c Forgot to push the sql changes for linkshells and retainers. 2017-04-15 16:41:01 -04:00
Filip Maj
9c2cdf9b5d Forgot to add script for quest abandon. 2017-04-15 16:37:14 -04:00
Filip Maj
9a2a79095c Added a spawn command for testing. Added scripts for the mole and lemming enemies. A content group is formed when you start the limsa opening (still testing). Added Vkorolon's dialog for the gridania opening echo. 2017-04-15 16:36:39 -04:00
Filip Maj
2de4934c41 Fixed battle npcs appearing strangely (no nameplate, wrong colour, etc), and implemented content groups! 2017-04-15 16:33:56 -04:00
Joseph Mikel Keller
9513389e0a database.php edited online with Bitbucket 2017-04-14 19:08:08 +00:00
Joseph Mikel Keller
b490cdf124 database.php edited online with Bitbucket 2017-04-14 19:06:25 +00:00
Filip Maj
c6307dde35 Forgot a nil in the "send game message" params, otherwise wrong overload is run. 2017-04-09 14:07:34 -04:00
Filip Maj
8c07e3f549 Added the changed schema for npc linkshells in the db. 2017-04-09 14:04:23 -04:00
Filip Maj
0d4ed1d1c8 Cleaned up the way a npc ls is set. Added an optimization, no changes are made if it is being set to the same value already set. Added the handler for npc linkshells when they are used. 2017-04-09 14:01:15 -04:00
Filip Maj
90e48f9ddd Added npc ls saving/functionality. 2017-04-09 12:48:27 -04:00
CuriousJorge
3d4bf3465b Merged in CuriousJorge/ffxiv-classic-server/develop (pull request #41)
"Seeing the Seers" quest etc3g0 scripted + minor fixes
2017-04-09 15:32:54 +00:00
CuriousJorge
3145ec5663 "Seeing the Seers" quest etc3g0 scripted, no reward handling currently. Requires actor "Nellaure" (http://ffxiv.gamerescape.com/w/index.php?title=Nellaure) to be added in. Hangs out in back-right corner of Carpenter's Guild.
Added a quest completion check to etc5g0.lua to prevent it showing "!" on quest-givers' head when you've cleared the quest already.
Fixed regression on select GM commands, !warp instant-warps within same zone again, !mypos reports to the thousandths place, !speed command works with one argument again.
2017-04-05 18:39:04 -04:00
Filip Maj
4bb222a47a Current phase wasn't being loaded in. Fixed. 2017-04-04 01:42:34 -04:00
Filip Maj
3269621dec Fixed Vkorolon not showing completion icon on login if in phase 1. 2017-04-04 01:37:27 -04:00
Filip Maj
c71aa0b45e Fixed crash if completionCheck callback wasn't there. 2017-04-04 01:25:08 -04:00
Filip Maj
e38a995574 Fixed a game message not appearing when objectives were completed. 2017-04-04 01:21:06 -04:00
Filip Maj
96c0f3444e Fixed the journal command. Wasn't sending correct phase. 2017-04-04 01:11:18 -04:00
Filip Maj
7fa4a1eb80 Added callbacks to help with quest management. Abandoning a quest now works. 2017-04-04 01:10:26 -04:00
Filip Maj
9d63be52e3 Added the Waste Not Want Not quest. 2017-04-02 23:50:51 -04:00
Filip Maj
c0f7f1b1ad Fixed quest commands not working from Lua. The uint/string overload was confusing it. 2017-04-02 18:44:47 -04:00
Filip Maj
a866688344 Added working import script for Windows (batch). 2017-04-02 18:29:07 -04:00
Rober Baker
0546b81cff Merged in reiichi001/ffxiv-classic-server/develop (pull request #40)
Typo fixes
2017-04-02 19:59:18 +00:00
Rober Baker
358afbace3 Merged in reiichi001/ffxiv-classic-server (pull request #39)
Small adjustments for new users
2017-04-02 19:58:32 +00:00
Robert Baker
be5a517aaa Typo fixes 2017-04-02 12:56:05 -07:00
Filip Maj
91820dc933 Forgot linkshell sql file. 2017-04-02 15:13:24 -04:00
Robert Baker
6642ed7e12 Fixed postbuild typos.
Automatically copy the scripts folder.
Pre-edited player.lua to disable addQuest / Directors.
2017-04-02 11:34:14 -07:00
Filip Maj
248ea0cce1 Merge branch 'develop' into lua_waiters 2017-04-02 14:29:37 -04:00
Filip Maj
be4b3b506f Pushing sql changes. 2017-04-02 14:29:04 -04:00
Filip Maj
43e1fbfab3 Pushing scripts. Also modified how private areas work. The type value is now appended to the name. 2017-04-02 14:26:36 -04:00
Filip Maj
2b780687dd Actor state from a spawn location wasn't being set. 2017-04-02 13:48:56 -04:00
Filip Maj
e0c9ca2bc2 Added break in the "setPushCircle" function. Fixed bug in the actor properties util. If a packet got full while inserting the property, it wouldn't get inserted in the next one. 2017-04-02 12:51:23 -04:00
Filip Maj
6c366110ef Added "CanAcceptQuest" and "IsQuestCompleted" helpers. 2017-03-28 18:03:16 -04:00
Tahir Akhlaq
6dd8bdda0c Merged in takhlaq/ffxiv-classic-server/null (pull request #38)
added db launch args and improved import time on gamedata_actor_class.sql
2017-03-27 16:57:02 +00:00
Tahir Akhlaq
a2a30003e0 added db launch args and improved import time on gamedata_actor_class.sql 2017-03-25 04:36:19 +00:00
Filip Maj
b345521f79 callClientFunction now returns ALL results. Fixed some scripts, finished fixing up Gridania opening script. 2017-03-21 00:43:30 -04:00
Filip Maj
6e12668176 Fixed the journal entry not clearing on removal. 2017-03-20 23:37:05 -04:00
Filip Maj
8f89eb8f23 Added quest accept/complete functions, and fixed a bug with removing quests from the DB. 2017-03-20 23:31:11 -04:00
Filip Maj
a77d2a9b48 Fixed crash. 2017-03-20 21:53:43 -04:00
Filip Maj
0a94840419 Checks and fixes for lua. 2017-03-20 21:51:43 -04:00
Filip Maj
7c25b14640 Changed "SendRequestedInfo" to "SendDataPacket" as that's the actual name of the packet. Copied all the progress with the Limsa Opening director to Gridania and Uldah. 2017-03-19 12:35:21 -04:00
Filip Maj
1bf25b2a2b Fixed a bunch of bugged scripts and cleaned up the quest flags. 2017-03-19 12:17:09 -04:00
Filip Maj
4f339709d5 Added scripts for NPCs in the Limsa echo instance and Gridania opening post-battle instance. 2017-03-19 11:41:29 -04:00
Filip Maj
7b205c7b9b Fixed syntax error in tutorial script. Finished fleshing out the opening script and related scripts for Limsa. 2017-03-19 11:40:41 -04:00
Filip Maj
77e0639410 Realized the start event packet after a zoneIn kickevent is weird and malformed, fixed for that usecase. Fixed bug with getting PrivateArea script. Missing area scripts now don't show a message (they were optional). Finished scripting out Limsa opening battle. 2017-03-19 11:39:21 -04:00
Filip Maj
545ce18f1f Merge branch 'lua_waiters' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into lua_waiters 2017-03-17 17:20:34 -04:00
Filip Maj
43c0925dee Added tutorial helper script. 2017-03-17 17:20:03 -04:00
Filip Maj
ce13827bce Fixed args being all wrong. 2017-03-14 23:13:54 -04:00
Filip Maj
b81141a158 More fixings and improvements. 2017-03-14 18:33:33 -04:00
Filip Maj
718bc28c57 Implemented "wait" functionality to the lua engine. Lost commits due to screw up. 2017-03-13 14:06:57 -04:00
Filip Maj
bc30958d37 New sql entries for limsa echo instance. Fixed PrivateAreaPastExit class entry. 2017-03-08 09:13:10 -05:00
Filip Maj
0c3f6cc9c4 Fixed bug with old "findActor" code since private areas were added. Add scripts for npcs in echo during limsa opening. 2017-03-08 09:12:14 -05:00
Filip Maj
eb324062da Phasese are now saved. 2017-03-07 21:44:57 -05:00
Filip Maj
de8877c1a4 Scripts were all broken. Fixed. 2017-03-07 17:27:33 -05:00
Joseph Mikel Keller
dc0b886691 Merged in dude22072/ffxiv-classic-server-2/dude22072/added-server_seamless_zonechange_boundcs-1483400391812 (pull request #34)
server_seamless_zonechange_bound.sql update
2017-03-07 17:55:33 +00:00
Rober Baker
8871f47066 Merged in reiichi001/ffxiv-classic-server/develop (pull request #37)
Small typo fixes
2017-03-07 17:53:37 +00:00
Franz Renatus
23ce361801 Fixed Map Server build script to copy scripts folder.
Added world_config.ini to World Server build script.

Adjusted characters.sql to make the lobby server happy again.
2017-03-07 09:16:55 -08:00
Filip Maj
d918ad3776 New scripts for the opening private areas, as well as db updates. 2017-03-07 08:32:57 -05:00
Filip Maj
e898c045f7 Overhaul of the director system and opening quests. Private Areas further implemented as well. 2017-03-07 00:09:37 -05:00
Filip Maj
dcaad5729d Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop 2017-01-29 11:08:24 -05:00
Filip Maj
f7482781e5 Rewrote directors so that they can work in the new system. Began adding content groups to the map server. 2017-01-29 11:07:28 -05:00
Filip Maj
40deaf3075 Fixed accidental hardcode. 2017-01-27 10:04:24 -05:00
Filip Maj
663c591a89 Merge branch 'launcher_editor' into develop 2017-01-26 19:42:07 -05:00
Filip Maj
031dae2c84 Added inputs, launcher editor more or less works. 2017-01-26 19:41:10 -05:00
Filip Maj
ab3ca412b3 Finished writing most of this exe patcher. 2017-01-26 19:07:47 -05:00
Filip Maj
ad88c0b28a Added a work value change function for testing. 2017-01-24 09:06:28 -05:00
Filip Maj
9372b4bc32 Added a thread and update() calls for gamestate changing. 2017-01-16 19:14:13 -05:00
Filip Maj
6a6ee67ae2 Conjurer door id. 2017-01-11 22:03:30 -05:00
Filip Maj
e6af0ef842 Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop 2017-01-11 22:02:00 -05:00
Filip Maj
b5a48b0eff Added more MapObjects. 2017-01-11 22:01:36 -05:00
Filip Maj
a8f3fe28d4 Uploaded the 1.0 launcher website as well as test patcher website. 2017-01-11 19:21:33 -05:00
Filip Maj
63b3098592 Lot of scrapped classes added. 2017-01-11 01:32:33 -05:00
Filip Maj
8b4eb8c57e Added retainer push command and fixed a bug with kickEvent (kinda???) 2017-01-10 23:59:25 -05:00
Filip Maj
b9c0084080 Fixed pushCommand load bug. Set all Aetherytes to wrong command id; fixed. 2017-01-10 23:47:37 -05:00
Filip Maj
c961ccd5bf Added all aetherytes classes and some other fixes. 2017-01-10 23:19:59 -05:00
Filip Maj
4a544a9ea3 The required sql file. 2017-01-10 16:45:38 -05:00
Filip Maj
fb1d22d731 Push command loading implemented. 2017-01-10 16:43:03 -05:00
Filip Maj
9bc3fc8dd7 Setting the active linkshell now works. 2017-01-09 23:12:56 -05:00
Filip Maj
fe1a652cd1 Removed build script to get this thing to compile. 2017-01-09 00:30:12 -05:00
Filip Maj
79de4fd1ff Merge branch 'develop' into group_work
# Conflicts:
#	FFXIVClassic Map Server/ConfigConstants.cs
2017-01-09 00:19:39 -05:00
Filip Maj
bc72147622 Added scripts needed. 2017-01-09 00:17:46 -05:00
Filip Maj
771b5b6d81 LS kicking and leaving work. 2017-01-09 00:12:11 -05:00
Filip Maj
61e4effd74 Started implementing LS management. Rank change works. 2017-01-08 23:13:15 -05:00
Filip Maj
68772a2011 Merge branch 'group_work' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into group_work 2017-01-08 21:43:04 -05:00
Filip Maj
108d8be013 Linkshell inviting completed. 2017-01-08 21:42:43 -05:00
Filip Maj
8499eeff39 Started work on the launcher/patcher editor. 2017-01-04 17:30:36 -05:00
Filip Maj
4b0ffb3882 Fixed the last actor speed. It was active mode speed. 2017-01-03 19:02:35 -05:00
dude22072
5b5bbc23b4 Added server_seamless_zonechange_bound.csv that was posted by
Jorge(@Tiam#8657) in Discord to server_seamless_zonechange_bounds.sql
2017-01-02 23:40:17 +00:00
Filip Maj
8a0ebe7ec4 Required scripts to make it work. 2017-01-02 14:38:56 -05:00
Filip Maj
5d494255ad Party invite done, as well as chat. Fixed double output of say packets. Note: Still need to implement name version of invite!!! 2017-01-02 14:35:11 -05:00
Filip Maj
5af1f6dba6 Relation group work. 2017-01-02 11:47:14 -05:00
Filip Maj
6ba1f968c3 Party leave/disband done. 2016-12-22 14:47:24 -05:00
Filip Maj
7c29a850c4 Scripts for party commands. 2016-12-21 21:50:45 -05:00
Filip Maj
2b10221a75 Party sync is done. Finished kick/promote code. Client is now always at the top of pt list. 2016-12-21 21:49:50 -05:00
Filip Maj
506bcbaf87 Added missing opcodes. 2016-12-21 20:04:33 -05:00
Filip Maj
16c9b741bf Kicked/Promote leader added but broke login. D/Cing now. 2016-12-21 18:02:50 -05:00
Filip Maj
e89b7557b3 Zone server now uses the World server's group classes. MotD now shows proper world name. Added party functions to zone server. 2016-12-21 09:27:51 -05:00
Filip Maj
ae38ee1bc1 Merge branch 'group_work' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into group_work
# Conflicts:
#	FFXIVClassic Map Server/WorldManager.cs
2016-12-21 08:28:24 -05:00
Filip Maj
bf9095822e Got party leader working. Added linkshell world/zone requests. 2016-12-21 08:27:23 -05:00
Filip Maj
2bdc238bc2 Added party sync packet which will sync member info between world/zone servers. 2016-12-20 19:17:50 -05:00
Filip Maj
a68866617f Removed a copy/paste error in the CreateLinkshell db code. 2016-12-18 10:13:04 -05:00
Filip Maj
4109f33b6e Relation code finished. 2016-12-18 10:07:06 -05:00
Filip Maj
7036ef363d Implemented MotD. Groups are now sent from world server to client, and also initialized. Retainers finished and are also sent. 2016-12-18 09:50:23 -05:00
Filip Maj
31446f37fa More cleanup (moved groups to WorldMaster class) and fixed the project file. 2016-12-17 09:37:18 -05:00
Filip Maj
2cc63960a7 Linkshell and WorkValue work. 2016-12-16 20:06:17 -05:00
Filip Maj
1148619ca5 More group work. Added packet operations to the world server so it can send global group info. 2016-12-15 12:19:44 -05:00
Filip Maj
6c409e93a9 Random work. 2016-12-13 15:02:28 -05:00
Filip Maj
fd2df829de Forgot stuff. 2016-12-12 19:05:06 -05:00
Filip Maj
31b13300ac Added all the base code for managing world-scope groups. 2016-12-12 19:03:25 -05:00
Filip Maj
09e1e31e79 Added packet 0xda 2016-12-12 10:11:27 -05:00
Filip Maj
b2c1c2895d Added new packets for controlling BG objects. Added group delete packet. Fixed crash if tried to warp to non-existent zone. Added a create LS db method. 2016-12-11 23:31:13 -05:00
Filip Maj
feb73a8444 Added subclassed groups to auto-set workvalues. 2016-12-06 08:26:16 -05:00
Filip Maj
f4e238f4be Merged in takhlaq/ffxiv-classic-server/ezpz (pull request #32)
added launch args for ip/port and default config loading
2016-12-05 15:29:50 +00:00
Tahir Akhlaq
ca44400048 cleaned up some spacing 2016-12-05 14:36:40 +00:00
Tahir Akhlaq
a2d4879cdd Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into ezpz
# Conflicts:
#	sql/characters.sql
2016-12-05 14:29:19 +00:00
Tahir Akhlaq
4a320d7096 added launch args for ip/port and default config loading
- fixed char create issues by adding default values to fields (todo: fix actual query)
- added post build command to copy scripts folder to bin
2016-12-05 14:26:20 +00:00
Filip Maj
96a29a9f04 Merged in dude22072/ffxiv-classic-server-1/dude22072/characterssql-auto_increment-1480820519818 (pull request #30)
AUTO_INCREMENT
2016-12-05 13:46:18 +00:00
Filip Maj
25e55bf6df Merged in takhlaq/ffxiv-classic-server/linux (pull request #29)
fixed linux build and import script
2016-12-05 13:46:02 +00:00
Filip Maj
c2a3641d08 Extended "Group" and created all the subgroups to cleanup code as well as make setting "work" values easier. 2016-12-04 11:59:54 -05:00
Filip Maj
f286922974 Deleted the list folder and anything in it. This was the old way before I realized how groups work. 2016-12-04 10:52:22 -05:00
Filip Maj
0317bea83b Redid the group sending using the new methods. 2016-12-04 10:51:27 -05:00
Filip Maj
18e9ee5f63 Forgot the "SynchGroupWorkValuesPacket" and some merging stuff. 2016-12-04 10:18:04 -05:00
Filip Maj
52703537e9 Forgot the "GroupCreatedPacket". 2016-12-04 10:12:33 -05:00
Filip Maj
df6e16103c Manually added the group packets and dataobjects into dev branch because the working_on_groups branch is so old it probably will break stuff. 2016-12-04 09:52:57 -05:00
dude22072
7e535a9398 users.sql AUTO_INCREMENT 2016-12-04 03:06:04 +00:00
dude22072
c04680ea38 characters.sql AUTO_INCREMENT 2016-12-04 03:02:15 +00:00
Filip Maj
101070954d Localhost Character's id was hardcoded by accident. 2016-12-03 18:56:32 -05:00
Filip Maj
b1998d5f73 Pushing sql updates. 2016-12-03 17:40:45 -05:00
Filip Maj
3864bf6d85 Merge branch 'proxy_server' into develop
# Conflicts:
#	FFXIVClassic Common Class Lib/packages.config
2016-12-03 14:03:13 -05:00
Filip Maj
4ce4647a75 If a player tries to zone to a offline server they will see a standard error message. Also a reconnection attempt will be made on a disconnected server. 2016-12-03 14:00:24 -05:00
Filip Maj
260878df38 Fixed login breaking due to destinationZone not going to 0 after switch or crash. 2016-12-03 13:33:39 -05:00
Filip Maj
ebd8ab0580 Fixed random crashes when zoning. Was due to improper write of the SessionEndPacket. For extra safety db is locked until zone in happens. Added some logs. Seems to be all working! 2016-12-03 13:23:32 -05:00
Filip Maj
e30831fdc5 Built subpackets to let the zone servers talk to the world server. Implemented cross-server zoning but the E2 packet or something isn't being sent. 2016-12-03 12:19:59 -05:00
Filip Maj
315f2e579c Merged in dude22072/ffxiv-classic-server/dude22072/marketentrancelua-syntax-error-1475561003225 (pull request #26)
MarketEntrance.lua syntax error
2016-10-05 16:18:12 -04:00
dude22072
6e7459a2f8 MarketEntrance.lua syntax error 2016-10-04 06:03:33 +00:00
Filip Maj
58fda93b45 Began working on server zone changes. 2016-09-26 16:20:01 -04:00
Filip Maj
5370f13b2b Fixed login not working, was due to packets that got queued not flushing. !!!Need to find solution!!! Added a optimization: Actors with 0 battle args are now concidered "static" and will not send their position updates. Removed some debug print statements. 2016-09-24 14:17:31 -04:00
Filip Maj
d931f71b06 Map server will only load maps it's set to load. 2016-08-29 13:39:50 -04:00
Filip Maj
62daa4db89 Removed duplicate CommandProcessor. It is held by the Server object now. GetSession by name now ignores case. 2016-08-29 12:48:23 -04:00
Filip Maj
e106eafb6c Server.cs Cleanup2 2016-08-29 12:41:33 -04:00
Filip Maj
c9feef07b7 Moved ZoneConnection to dataobjects namespace. Cleaned up Server.cs. 2016-08-29 12:40:47 -04:00
Filip Maj
cf38454c8f Cleaned up a lot of the "ConnectedPlayer" objects, turning them into "Session" objects. A lot of duplicate lists were also removed. 2016-08-29 12:37:41 -04:00
Filip Maj
06e7ea59f4 Fixed NLog config for map server. Added some debug printouts while testing. 2016-08-29 09:03:48 -04:00
Filip Maj
37b098e87a Added a sleep during debug so zone server launches after map server ready. 2016-08-29 08:54:37 -04:00
Filip Maj
6bffe69b21 More work on the world server. Modified map server to communicate with it. 2016-08-29 08:17:14 -04:00
Filip Maj
bd26a71fef Changed folder from Proxy to World. Did some nlog configing. 2016-08-28 14:25:37 -04:00
Filip Maj
0f093235f8 Default speeds were not being set, and on actor load *currentSpeed* wasn't being loaded. 2016-08-28 08:06:10 -04:00
Tahir Akhlaq
92be59d020 fixed linux build and import script 2016-08-27 03:50:02 +01:00
Filip Maj
364ab40b3f Added decompression/compression of packets. Cleaned up handshaking. 2016-08-24 15:41:54 -04:00
Filip Maj
4aae16e458 More session handling code added. 2016-08-24 14:18:37 -04:00
Filip Maj
a1ca960543 More work on the world server. 2016-08-23 16:57:24 -04:00
Filip Maj
e24a6f99cb Some more work on the world server. 2016-08-22 14:18:37 -04:00
Filip Maj
c67f74130f Moved packet structures to common. 2016-08-22 10:43:04 -04:00
Filip Maj
62cc343f51 Player chocobo issuance is now removed, and you are given a chocobo whistle if you didn't have one. 2016-08-21 20:11:50 -04:00
Filip Maj
8725377168 Changed InfoRequestResponsePacket to GenericDataPacket as that is the proper use/term. Added the city exit spawn locations. Did more work on the chocobolender script. Most of the issuing portion is done. Also can now summon through the lender. 2016-08-21 19:51:49 -04:00
Filip Maj
d5f17c01a8 Added changes to the chocobolender script and global script. Working off of "thetestgames" code. 2016-08-21 18:21:00 -04:00
Filip Maj
d6277bc722 Sql update for the last commit. 2016-08-21 18:18:11 -04:00
Filip Maj
316e326d71 GM tickets can now be in an open/closed state. Added some helper functions for currancy, and added functions to allow changing chocobo appearance. 2016-08-21 18:16:54 -04:00
Filip Maj
685fe7dd5a Fixed and improved a bunch of the recently commit Support Desk tables and database accessors. Removed a file that doesn't exist added from last commit. 2016-08-20 19:16:33 -04:00
Filip Maj
c087fb44c0 Merged in nxtstudios/ffxiv1.0-server (pull request #24)
Added DB support for Support Desk packets and fixed paths to work on Linux/OSX
2016-08-20 13:29:12 -04:00
Jordan Maxwell
00e5e4f642 Updated Chocobo Lender script 2016-08-19 17:43:04 -05:00
Jordan Maxwell
46350a0724 Fixed Bugs 2016-08-19 16:32:14 -05:00
Jordan Maxwell
fb18c1fbe4 Added DB Support for Chocobo Lenders 2016-08-19 14:40:43 -05:00
Jordan Maxwell
4f3828e594 Added MySQL tables for Support Desk 2016-08-19 00:10:07 -05:00
Jordan Maxwell
270d4ce436 Fixed MySQL Syntax issues 2016-08-18 23:36:04 -05:00
Jordan Maxwell
7c9077beec Added support for Linux/OSX Paths 2016-08-18 22:59:23 -05:00
Jordan Maxwell
d7166cadc0 Added GM Ticket support on the DB 2016-08-18 22:58:09 -05:00
Filip Maj
38014f8a16 Merged in CuriousJorge/ffxiv-classic-server/script_fixing (pull request #23)
Limsa/Gridania NPC fixed scripts.
2016-08-18 16:47:14 -04:00
CuriousJorge
4f48643c5c Limsa/Gridania NPC fixed scripts. Scripts still needed for Aubrenard & Gridania Aetheryte.
Some required changes to the database:

Task Board - Change 1200195 in gamedata_actor_class to /Chara/Npc/Object/TaskBoard
Serpent_Private_White - Change 1500324 in gamedata_actor_class to /Chara/Npc/Populace/PopulaceCompanyWarp
Beaudonet - Adjust 1001708 in gamedata_actor_class.  There is a linebreak in there causing problems
2016-08-17 23:27:31 -04:00
Filip Maj
3490f366a2 Merged in CuriousJorge/ffxiv-classic-server (pull request #21)
Updated GM Commands.
2016-08-14 10:52:52 -04:00
Filip Maj
13362d347c Merged in xdemolish/ffxiv-classic-server/develop (pull request #22)
improved import times on a few tables
2016-08-14 10:51:39 -04:00
Tahir Akhlaq
8665c1a226 improved import times on a few tables 2016-08-14 15:49:32 +01:00
Filip Maj
b6c3c7f71c Added MS Compilers package to let solution compile in VS2013. Added new sql files with Gridania NPCs. Added the new seamless boundry table. 2016-08-14 09:45:06 -04:00
Filip Maj
63ac56224e Merge branch 'develop' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into develop 2016-08-14 08:52:27 -04:00
Drajiel
09705bec4e Updated Commands. 2016-08-11 20:02:01 -04:00
Filip Maj
d5f0cbef8c Merge branch 'master' into develop 2016-08-09 14:22:27 -04:00
Filip Maj
595a53ed9e Forgot to push file for seamless zone change. 2016-08-05 18:25:24 -04:00
Filip Maj
b3920f9630 Music is faded in on seamless change instead of instant. 2016-08-05 18:25:08 -04:00
Filip Maj
c4c43421f3 Forgot ObjectItemStorage script. 2016-08-05 18:24:17 -04:00
Filip Maj
5e926bf668 Added Gridania scripts and various others. Fixed error messages popping for scripts that may not use certain functions. 2016-08-05 18:23:59 -04:00
Filip Maj
0c435d91c9 Merged in xdemolish/ffxiv-classic-server/gg (pull request #20)
readded functions to utils (note to self: read both files before merging)
2016-08-03 17:30:06 -04:00
Tahir Akhlaq
2233f48719 note to self: read both files before merging 2016-08-03 16:54:23 +01:00
Filip Maj
67928ee875 Seamless zoning implemented WOOOO! 2016-07-31 23:00:38 -04:00
Filip Maj
8eb257aba4 Seamless boundries added to DB and loaded by WorldManger. 2016-07-31 22:30:54 -04:00
Filip Maj
b43a6885c5 Added functions to allow for an improved seamless zone change. Player can now pull actors from two zones when performing a seamless zone change to give a improved feel. 2016-07-31 21:48:17 -04:00
Filip Maj
63edbe81f3 Added code to generate lua files from db. 2016-07-31 21:07:37 -04:00
Filip Maj
205136d661 Improved trigger box packet. 2016-07-31 21:07:26 -04:00
Filip Maj
a4d3e91085 Merged in xdemolish/ffxiv-classic-server/misc2 (pull request #18)
fixed import script, improved db import times, fixed logging and warp command (should be 5000% legit now)
2016-07-27 12:57:13 -04:00
Tahir Akhlaq
065fe05795 fixed import script, improved db import times, fixed logging and warp command (should be 5000% legit now)
- improved import times for gamedata tables (there's prolly other innodb tables too, i didnt check though)
- fixed issue where player wouldnt be readded to zone on warping if destination wasnt valid
- corrected typo in warp command lua description
- added logger for lua cause i forgot to last time
2016-07-27 17:23:52 +01:00
Filip Maj
2939683183 Added property flags for actors. Cleaned up NPC constructor. 2016-07-24 10:21:45 -04:00
Filip Maj
4ef62a16ec Merge branch 'master' into develop 2016-07-17 13:06:56 -04:00
Filip Maj
7f65cfcbbf Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server 2016-07-17 13:06:38 -04:00
Filip Maj
70b4fc8630 Removed some packet debug outputs. 2016-07-17 09:21:17 -04:00
Filip Maj
2c30b7dfc1 Merged in xdemolish/ffxiv-classic-server/lua_commands (pull request #17)
derped with some debug logging (thought i pushed this earlier)
2016-07-17 09:20:13 -04:00
Tahir Akhlaq
59f3cfa2f1 derped with some debug logging (thought i pushed this earlier) 2016-07-17 11:34:37 +01:00
Filip Maj
136ced3aae Added all the shop keeper scripts in Ul'dah. Fixed some broken populace scripts. Changed .ini files, wrong default. Fixed a syntax error in CommandProcessor.cs. 2016-07-16 20:12:04 -04:00
Filip Maj
5b7ad2364b Merge branch 'master' into develop 2016-07-16 20:04:49 -04:00
Filip Maj
302d7bd68a Merged in xdemolish/ffxiv-classic-server/lua_commands (pull request #16)
dynamic gm commands + packet logging fix
2016-07-16 19:52:54 -04:00
Filip Maj
d26b64b458 Fixed some minor lua related crashing when the parent file was not found for some reason. Used decorated message for error output, not normal message. 2016-07-16 19:52:13 -04:00
Tahir Akhlaq
8839e75557 missed some left over files 2016-06-24 22:14:17 +01:00
Tahir Akhlaq
5fc0e0eeca Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into lua_commands
# Conflicts:
#	FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs
#	FFXIVClassic Map Server/lua/LuaEngine.cs
#	data/scripts/global.lua
2016-06-24 22:13:17 +01:00
Tahir Akhlaq
30b0d4a97d implemented sendpacket and speed commands
- fixed data race on logging in
- todo: implement reloadzones, reloaditems, property, property2
2016-06-24 20:52:30 +01:00
Filip Maj
110cfbccc2 Fixed errors in the spawn location tables. 2016-06-21 23:00:39 -04:00
Filip Maj
3cd181f5da Ported over the Limsa/Uldah opening zone scripts. Still not 100% done. 2016-06-21 22:50:30 -04:00
deviltti
7a25c818f2 demo...srs? 2016-06-21 21:52:40 -04:00
Tahir Akhlaq
bb02f94d38 changed date formatting for logging 2016-06-22 02:31:05 +01:00
deviltti
b8a563f9d7 Lobby and game server packet ConsoleColor to NLog cleanup. 2016-06-21 21:13:01 -04:00
Filip Maj
926e9146e2 Fixed a few scripts. Jellyfish init script was built so it didn't crash boat zone. 2016-06-21 20:07:02 -04:00
Filip Maj
4c7316702d Limsa Town npcs ported over. 2016-06-21 19:54:52 -04:00
deviltti
0c197f34a1 Okay now. 2016-06-21 18:58:33 -04:00
deviltti
590ad3e002 Reimplemented ConsoleColor for packets in NLog. 2016-06-21 18:52:20 -04:00
Filip Maj
44acee7e6d Made a note that Guencen is unreachable and has no real script. 2016-06-21 18:01:12 -04:00
Filip Maj
d06bf5499f Set Ansgor to correct talk. 2016-06-21 17:56:38 -04:00
Filip Maj
3969fff64a Finished converting over all scripts. 2016-06-21 17:48:50 -04:00
Tahir Akhlaq
0aac675b30 cleaned up previous commit 2016-06-21 21:47:07 +01:00
Filip Maj
8b44d90114 Modified all wil0Town01 scripts to new way. 2016-06-21 15:48:16 -04:00
Filip Maj
ff2f587959 Modified all wil0Town01a scripts to new way. 2016-06-21 15:29:48 -04:00
Filip Maj
1242a8b00c New event conditions to take advantage of PlaceDrivenCommands. 2016-06-19 23:24:19 -04:00
Filip Maj
1546ed54fc Moved aetheryte script to new system. 2016-06-19 23:23:01 -04:00
Filip Maj
97c1fb06cf More script work. 2016-06-19 23:16:31 -04:00
Filip Maj
8743042950 Cleaned up the new scripting code. Added a helper function to do the yielding automatically. Modified all the scripts to use the new system and added a few new ones. 2016-06-19 18:52:34 -04:00
Filip Maj
51bbf4ae2e Non-npc stuff is now also using script system. Wrote new scripts for all sidemenu commands. 2016-06-18 12:26:29 -04:00
Tahir Akhlaq
37d91480f9 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into lua_commands
# Conflicts:
#	FFXIVClassic Common Class Lib/packages.config
#	FFXIVClassic Lobby Server/Program.cs
#	FFXIVClassic Map Server/CommandProcessor.cs
#	FFXIVClassic Map Server/Program.cs
#	FFXIVClassic Map Server/actors/Actor.cs
2016-06-18 05:59:42 +01:00
Tahir Akhlaq
1ad2b5d7d0 more work on commands
- moved script object to wrapper class to catch and log exceptions
- added loggers for basepacket/subpacket (todo: colour and use them in NLog.config)
- finished up most commands (todo: !property and !property2)
- todo: create and use mysql wrapper class to log exceptions
2016-06-18 05:42:14 +01:00
Filip Maj
4e69022072 New style of scripting for better complex menus. 2016-06-17 23:17:24 -04:00
Filip Maj
cdf4b3a2f2 GuildShop script done. 2016-06-16 22:44:50 -04:00
Tahir Akhlaq
57b9d5ab99 start of work moving commands to lua 2016-06-17 02:44:57 +01:00
Filip Maj
f4060e16bf Merged in xdemolish/ffxiv-classic-server/derp (pull request #15)
fixed derps causing client/server crashes
2016-06-16 14:20:40 -04:00
Tahir Akhlaq
ac01224769 fixed derps causing client/server crashes 2016-06-16 03:49:58 +01:00
Filip Maj
8eaa920751 Fixed logs on map. 2016-06-15 20:09:53 -04:00
Filip Maj
fbf6810c5c Fixed logs on lobby. 2016-06-15 20:06:08 -04:00
Filip Maj
9b3712044d Merged in xdemolish/ffxiv-classic-server/logging_and_casing (pull request #14)
moved stray console.writeline to use main logger
2016-06-15 14:22:05 -04:00
Tahir Akhlaq
1159c75ab8 moved stray console.writeline to use main logger 2016-06-15 19:15:24 +01:00
Filip Maj
1bac69c893 Fixed some startup output. 2016-06-15 10:40:30 -04:00
Filip Maj
9a29d0806a Merged in xdemolish/ffxiv-classic-server/logging_and_casing (pull request #13)
fixed logging and casing
2016-06-15 10:23:42 -04:00
Tahir Akhlaq
c413006a91 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into logging_and_casing
# Conflicts:
#	data/scripts/base/chara/npc/object/TaskBoard.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/baterich.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/claroise.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/flame_lieutenant_somber_meadow.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/gunnulf.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/heibert.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/ipaghlo.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/kokobi.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/mammet.lua
#	data/scripts/unique/wil0Town01/PopulaceStandard/roarich.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/dariustel.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/fruhybolg.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/guencen.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/hcidjaa.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/uwilsyng.lua
#	data/scripts/unique/wil0Town01a/PopulaceStandard/vavaki.lua
2016-06-15 04:41:15 +01:00
Tahir Akhlaq
2b81660dce accidentally mass replaced "target", fixed now 2016-06-15 04:27:25 +01:00
Tahir Akhlaq
884a26dc52 fixed method casing in lua 2016-06-15 03:41:23 +01:00
Filip Maj
c612b885dd Ul'dah npcs completed. 2016-06-14 21:47:54 -04:00
Tahir Akhlaq
ba13d5798d derp 2016-06-15 00:08:05 +01:00
Tahir Akhlaq
b633126568 fixed import script 2016-06-15 00:04:15 +01:00
Tahir Akhlaq
335a59204c Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server into method_casing
# Conflicts:
#	FFXIVClassic Map Server/CommandProcessor.cs
#	FFXIVClassic Map Server/WorldManager.cs
#	FFXIVClassic Map Server/actors/area/Area.cs
#	FFXIVClassic Map Server/actors/area/PrivateArea.cs
#	FFXIVClassic Map Server/actors/area/Zone.cs
#	FFXIVClassic Map Server/actors/chara/npc/Npc.cs
#	FFXIVClassic Map Server/common/Utils.cs
#	FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs
2016-06-14 22:54:02 +01:00
Tahir Akhlaq
24f55139dd mass replaced all methods to pascal case 2016-06-14 21:29:10 +01:00
Tahir Akhlaq
ed0a0a58f7 added nlog logging (todo: custom logger for packets) 2016-06-14 19:06:55 +01:00
Filip Maj
f5619ff800 All of Limsa scripted. Ul'dah scripts auto generated. 2016-06-13 22:27:12 -04:00
Tahir Akhlaq
c5516511b0 cleaned up logging, added log_level param to enable/disable types of logging 2016-06-14 03:00:57 +01:00
Filip Maj
d1c8987091 Scripted almost all of lower limsa. 2016-06-13 21:26:19 -04:00
Filip Maj
65389e3362 Added scripts for the inn keepers. Added default event conditions to almost all actors. Updated sql for actorClass, spawnLocations, and new zoneEntrances. 2016-06-12 21:30:47 -04:00
Filip Maj
5723f77b1f Added all the new scripts for the actor instancing system. 2016-06-12 19:06:03 -04:00
Filip Maj
a4ea5f024b Merge branch 'actor_instancing' into develop
# Conflicts:
#	sql/gamedata_actor_class.sql
2016-06-12 18:52:30 -04:00
Filip Maj
fec348136e Merge branch 'master' into develop
# Conflicts:
#	FFXIVClassic Map Server/CommandProcessor.cs
2016-06-12 18:51:24 -04:00
Filip Maj
957a8d89bb Null event conditions get defaulted to empty conditions to allow anything to spawn. More work on the scripts. Committing the new sql schema + imports of all 3 town zones. 2016-06-12 18:43:41 -04:00
Tahir Akhlaq
c23f9c7ca9 PROJECT: added common library to make common files actually common
- renamed sln to FFXIVClassic.sln
- threaded logging
- todo: print packets using Log.Packet
2016-06-12 20:12:59 +01:00
deviltti
16d4779970 Fixed packet ByteArrayToHex output 2016-06-10 22:40:46 -04:00
deviltti
554c8b5514 Fixed packet Console output 2016-06-10 21:14:28 -04:00
deviltti
33a52c4fa8 Fixed derp in Console output 2016-06-10 20:17:35 -04:00
deviltti
1c712c7533 Cleaned up LogFile and ByteArrayToHex utils. 2016-06-10 19:58:01 -04:00
Filip Maj
f0d4429f65 Child scripts now loaded by a uniqueId in the spawn table. 2016-06-09 22:58:03 -04:00
Tahir Akhlaq
8b93abe86e servers now log (almost) everything to file
- regex'd in mysqlexception logging
- servers can now specify server_port, log_path, log_file
- added scripts to import/export all tables (exporting will export a handful of garbage table names, open and check for structure before deleting)
- fixed packet logging (thanks deviltti)
2016-06-09 19:48:06 +01:00
Filip Maj
23b6ede128 Designed the new script system for npcs. Added missing appearance info. 2016-06-09 00:46:17 -04:00
Filip Maj
92339ba0c4 Merged in xdemolish/ffxiv-classic-server (pull request #11)
fixed character create errors and..
2016-06-08 14:04:02 -04:00
Tahir Akhlaq
d486650143 fixed character create errors
import individual tables instead of one large sql file
2016-06-07 22:58:17 +01:00
Filip Maj
62d1fec8b3 README.md edited online with Bitbucket 2016-06-06 15:26:03 +00:00
Filip Maj
abbdaf1368 README.md edited online with Bitbucket, changed IRC server to Discord. 2016-06-06 15:24:57 +00:00
Filip Maj
fc51b7f564 Modified scripts to work with new system. 2016-05-29 16:03:24 -04:00
Filip Maj
62ed9b22f1 Implemented actor instancing, as well as automatic name generation for NPCs. 2016-05-29 15:14:09 -04:00
Filip Maj
f0ec2125ac Found out the second weather packet value is a transition time. 2016-05-09 22:19:57 -04:00
Filip Maj
b45390adb6 Fixed null worldManager due to it being set as a static variable at class creation. 2016-05-09 20:57:32 -04:00
Filip Maj
8df79c9e6f Modified the SetWeatherPacket. Second value is a toggle for instant or smooth transitions. 2016-05-09 20:48:18 -04:00
Filip Maj
78f2b18641 Merged in TheManii/ffxiv-classic-server (pull request #10)
Merge ingame_help part 3
2016-05-09 20:38:28 -04:00
TheManii
320d662abe Merge ingame_help_part_3 2016-05-09 18:30:57 -04:00
TheManii
6dcdabf9a4 Added more weather types 2016-05-09 16:58:04 -04:00
TheManii
c02c09a237 Bugfixed with !test 2016-05-09 01:15:58 -04:00
TheManii
59f1ef0594 REALLY fix whitepaces 2016-05-09 00:48:58 -04:00
TheManii
c80eb623b1 Fixed whitespaces 2016-05-09 00:48:16 -04:00
TheManii
a99a348c36 Added !help entries for !test weather 2016-05-09 00:40:46 -04:00
TheManii
101a2d50ed Fixed typo in !help 2016-05-09 00:26:53 -04:00
TheManii
11a371f32b Added !set weather <weathertype>and populated weather table 2016-05-09 00:23:35 -04:00
TheManii
2c18016a29 Merged master into ingame_help 2016-05-04 23:04:03 -04:00
Filip Maj
541456bd8e Fixed bug with warp. WorldManager cannot be statically stored like that. 2016-04-24 17:59:50 -04:00
TheManii
a7959d9263 Merged Ioncannon/ffxiv-classic-server into master 2016-04-20 13:50:01 -04:00
Ioncannon
9e2960c047 Merged in TheManii/ffxiv-classic-server (pull request #9)
Make post build not fail due to modified files/silently ignore updated files
2016-04-18 16:23:55 -04:00
TheManii
01dd955376 Wrong flag 2016-04-18 15:47:57 -04:00
TheManii
426750f931 Make post build not fail due to modified files/silently ignore updated files 2016-04-18 15:42:17 -04:00
Ioncannon
c944d262b3 Merged in TheManii/ffxiv-classic-server (pull request #8)
Merge web_part_1
2016-04-18 13:49:46 -04:00
Filip Maj
7827fbf8d3 Took out some test code for the limsa opening script. 2016-04-18 13:44:51 -04:00
Filip Maj
d01c780cac Latest sql files. Also the gamedata ones were split from the main core. 2016-04-18 13:43:09 -04:00
Filip Maj
ffae2b744f Untracked the ./scripts folder since it was moved to ./data/scripts 2016-04-18 13:35:10 -04:00
Filip Maj
53064a7345 All the script changes done to get the openings working. 2016-04-18 13:33:31 -04:00
Filip Maj
3d5fa45730 Merge branch 'develop'
# Conflicts:
#	FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
#	FFXIVClassic Map Server/Server.cs
#	data/scripts/player.lua
#	scripts/zones/193/npcs/pplStd_11@0C100.lua
2016-04-18 13:31:55 -04:00
Filip Maj
79eaf5d79e Added more quest functions and completed the scripts for Gridania and most of Ul'dah. Up to the battle part that is. 2016-04-17 19:09:01 -04:00
TheManii
4a8b241e4e Merged Ioncannon/ffxiv-classic-server into master 2016-04-16 23:11:42 -04:00
TheManii
20427400ef Merge web_part_1 into master 2016-04-16 23:10:46 -04:00
Filip Maj
95849e1c1c onInit was added to all the scripts. Added code for all the Limsa opening event npcs. 2016-04-14 08:31:39 -04:00
Filip Maj
bd4e4a293c Removed a buffer being created for no reason. 2016-04-14 08:30:34 -04:00
Filip Maj
d989ec2a58 Added two callbacks to the Director; onTalked and onCommand. Split the command and event starts and keep track of them separately. 2016-04-14 08:30:21 -04:00
TheManii
14a7a4172a Added rest of fields, chocobo fields are disabled as they dont work if there is no chocobo entries for a character 2016-04-14 03:44:05 -04:00
Filip Maj
108f5aa677 Fixed crash due to bad constructor for QuestDirectorMan0L001. Unsigned integers will also write out as lua 0-datatype. 2016-04-11 22:49:05 -04:00
TheManii
863e006830 Converted initial profile + appearence to new db schema, currently only read only due to submit functions being unmodified 2016-04-11 17:00:26 -04:00
TheManii
e33bd05f09 Removed wrong files 2016-04-11 14:40:08 -04:00
TheManii
c9838254b9 Revert "Remove unused files"
This reverts commit 1ce34914bc.
2016-04-11 14:39:33 -04:00
TheManii
1ce34914bc Remove unused files 2016-04-11 14:35:17 -04:00
TheManii
73d5b9fbf1 Use intra-zone warps for !warp relative 2016-04-11 14:33:18 -04:00
Filip Maj
fe111ab6ca Quest data is now saved/loaded from the DB. 2016-04-10 13:11:12 -04:00
Ioncannon
de7484a931 Merged in TheManii/ffxiv-classic-server (pull request #7)
Merge ingame_help part 2
2016-04-10 11:17:37 -04:00
Filip Maj
b8a3d4f1e0 Added quest director class. 2016-04-10 11:14:36 -04:00
TheManii
44650bc4a5 Merge from master 2016-04-09 13:27:50 -07:00
TheManii
02e9b52a02 Fix typo 2016-04-09 13:26:46 -07:00
TheManii
10d4d7c148 Merge in Relative warps 2016-04-09 12:42:24 -07:00
TheManii
5c7ca212aa 2nd half of previous commit 2016-04-09 12:38:15 -07:00
TheManii
c38fbc0c09 *Revert ea1e16a, should be done globally and not simply here 2016-04-09 12:35:29 -07:00
TheManii
06606c5f01 *Revert changes to worldmanager.cs, not needed anymore
*Made !warp silently catch all exceptions so they dont crash server/don't change player state if invalid
*Moved rest of !warp parsing logic into parseWarp(), doWarp() now purely handles actual act of warping
2016-04-09 12:27:04 -07:00
TheManii
2eb40a0d7c Save player rotation for relative warps
Make !warp more robust with error handling
2016-04-09 11:28:21 -07:00
TheManii
b1a9ced93e Merged master into ingame_help 2016-04-08 13:07:20 -04:00
TheManii
dbbd1fa0d8 Fix merge 2016-04-08 10:03:35 -07:00
TheManii
de2ec984e4 Revert "Fix merge"
This reverts commit 29228a0c11.
2016-04-08 09:59:56 -07:00
TheManii
29228a0c11 Fix merge 2016-04-08 09:57:40 -07:00
TheManii
5920d93b19 Merged master into ingame_help 2016-04-08 12:51:28 -04:00
TheManii
3790920db3 Typo 2016-04-08 00:51:34 -07:00
TheManii
21745a6aa8 Added relative warps 2016-04-08 00:48:34 -07:00
TheManii
574b2da4f8 Merged Ioncannon/ffxiv-classic-server into master 2016-04-08 01:37:37 -04:00
TheManii
fc07d2e24d Merge ingame_help into master 2016-04-07 22:25:58 -07:00
TheManii
33be97ea9d Make CommandProcessor more robust 2016-04-07 22:22:31 -07:00
Filip Maj
c0cd7555e1 -Fixed up the quest flag functions.
-Renamed the ActorSpecialGraphicPacket to SetActorQuestGraphic as those icons all have to do with questing.
-Got onSpawn to work and implemented the quest graphics.
-Added new QuestDirector class.
2016-04-07 22:34:10 -04:00
Filip Maj
1f5788def8 Redundent zoneid was redundent. 2016-04-07 22:32:04 -04:00
TheManii
cc5d0b139a Added new !warp to help 2016-04-07 12:29:17 -07:00
TheManii
ecc61ea5f9 Surrounded command chain with region boxes
Changed doWarp() to handle in implicit, in-zone only warps

Might have issues with instanced zones, untested
2016-04-07 12:25:28 -07:00
TheManii
b17d193a59 Fix previous commit 2016-04-07 11:41:56 -07:00
TheManii
7c0594b749 Cherrypick *b16ea7b Disable login10/11 2016-04-07 10:37:14 -07:00
Ioncannon
8b55993f25 Merged in TheManii/ffxiv-classic-server (pull request #5)
Disable login10/11
2016-04-07 13:23:44 -04:00
TheManii
b16ea7bc44 Disable login10/11 2016-04-07 10:21:04 -07:00
TheManii
4cf005467a Moved most of the strings to external resource file 2016-04-06 19:55:12 -07:00
TheManii
cd8cb1acb3 Made give/removecurrency consistant with help description 2016-04-06 16:28:24 -07:00
TheManii
26f5e1be28 Fix spelling 2016-04-06 15:34:04 -07:00
TheManii
ea1e16aea7 Make Getters more case consistant 2016-04-06 15:29:24 -07:00
TheManii
cdcdd6a584 Split off ingame commands from server 2016-04-06 15:22:26 -07:00
TheManii
c74e40f7d0 Merge 23aef8c Removed need for the login2.bin packet file.
Merge 49a4bf1 Fixed the SQL schema to work with the map server as well as updated the server id in the config file to match that in the database
2016-04-06 10:51:12 -07:00
TheManii
cb86bcb3af Merged Ioncannon/ffxiv-classic-server into master 2016-04-06 13:47:59 -04:00
Ioncannon
dabbff1fda Merged in TheManii/ffxiv-classic-server (pull request #3)
Simplify post build steps
2016-04-06 13:40:45 -04:00
Ioncannon
77c12c489c Merged in VeryCrushed/ffxiv-classic-server (pull request #4)
Fixed the SQL schema to work with the map server as well as updated the server id in the config file to match that in the database
2016-04-06 13:39:52 -04:00
Andrew Babbitt
49a4bf1ab0 Fixed the SQL schema to work with the map server as well as updated the server id in the config file to match that in the database 2016-04-05 23:52:16 -06:00
TheManii
1ef3e350c0 Fix line endings 2016-04-05 16:13:16 -07:00
TheManii
7a8e53a8ac Add in ingame help system 2016-04-05 16:06:38 -07:00
TheManii
5892887e4c Merged Ioncannon/ffxiv-classic-server into master 2016-04-05 18:33:46 -04:00
Filip Maj
23aef8cc46 Removed need for the login2.bin packet file. 2016-04-05 17:34:34 -04:00
TheManii
9a2977a094 Merge branch 'master' of https://TheManii@bitbucket.org/TheManii/ffxiv-classic-server.git 2016-04-05 13:03:47 -07:00
TheManii
1a43f15800 Fix partial copies post compile 2016-04-05 13:03:46 -07:00
TheManii
aa4fc09a32 Revert "Remove unused files/docs that belong on wiki"
This reverts commit 64155dc752.
2016-04-05 11:43:12 -07:00
TheManii
64155dc752 Remove unused files/docs that belong on wiki 2016-04-05 11:42:17 -07:00
TheManii
8cb1ae99e8 README.md edited online with Bitbucket 2016-04-05 18:22:32 +00:00
TheManii
f8a2b7efc7 Update .gitignore for user provided data 2016-04-05 11:19:23 -07:00
TheManii
5d3f25f1a8 Don't Clobber existing files 2016-04-05 11:08:02 -07:00
TheManii
ddf4d0d202 Consolidated server data and automatically place them in approporate directories post build 2016-04-05 10:42:42 -07:00
Filip Maj
b68d13ea7f Cleaned up the lua calls and renamed "onInstantiation" to "init". Added a "onSpawn" callback but still working on it. Added the "ActorSpecialGraphicPacket" and functions to use it. 2016-04-02 17:56:01 -04:00
Filip Maj
e501d5b796 Merge branch 'develop' 2016-04-01 23:50:29 -04:00
Filip Maj
4c391f64bc Removed old inn code and realigned all the cutscene flags. Offsets will need to be figured out again though. 2016-04-01 23:50:17 -04:00
Filip Maj
ae9a712477 Added scripts for all the npcs (minus the door) in the Limsa Opening zone (boat). The beginning tutorial stuff is also scripted. 2016-04-01 23:32:52 -04:00
Filip Maj
91842ab08a Merge branch 'develop'
# Conflicts:
#	FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
2016-04-01 23:27:34 -04:00
Filip Maj
1dd04e2174 Merge branch 'director_and_questing' into develop 2016-04-01 23:24:28 -04:00
Filip Maj
6c3918ac2f Got the kick event packet working and implemented the director parameter in the player instantiation. The first argument of the set event status packet is an "isEnabled" flag; switched to bool rather than int. 2016-04-01 23:24:14 -04:00
Filip Maj
cf73522291 Reversed change to output folder that was done in previous pull request merge. 2016-03-30 20:49:37 -04:00
Filip Maj
8da3a6ff9c Merge branch 'develop'
# Conflicts:
#	FFXIVClassic Map Server/packets/send/Actor/events/SetEmoteEventCondition.cs
2016-03-30 20:18:28 -04:00
Filip Maj
60b4035ec5 Receive routine in the lobby server now checks if bytes read is > (not >=) than 0. This was causing connections to stay active and cause the CPU to get bogged down. 2016-03-30 20:16:59 -04:00
Filip Maj
597a800599 More work trying to get the opening cutscene to fire right away. 2016-03-30 20:15:21 -04:00
Ioncannon
48b9fa39b5 Merged in VeryCrushed/ffxiv-classic-server (pull request #1)
Small Fixes
2016-03-30 11:06:17 -04:00
Andrew Babbitt
52806d2f3d Updated the readme to be more helpful. 2016-03-29 17:52:37 -06:00
Andrew Babbitt
eaafba79e2 Updated .gitignore and changed all projects to build in a central directory at the root of the project. 2016-03-29 17:38:41 -06:00
Filip Maj
6124e6906d Uploaded a template config.ini file. 2016-03-29 17:30:51 -04:00
Filip Maj
48f1fbc8f5 Implemented the emote and notice event conditions. 2016-03-29 17:09:16 -04:00
Filip Maj
1dccc0530e Static actor file can be now loaded from the original encrypted script in the FFXIV folder. This way I do not run into issues of sharing client files. 2016-03-28 23:05:19 -04:00
Filip Maj
94db8336df Added login site. 2016-03-28 22:07:10 -04:00
Filip Maj
b33b0b1bc4 When zoning into a inn, cutscene packet will be sent as well. 2016-03-28 22:00:11 -04:00
Filip Maj
b69e18ff3c Companion name was off by 1. 2016-03-28 21:52:02 -04:00
Filip Maj
a615f29e63 Added path companion data to the cutscene book packet. Now that packet will fire if zoning into the inn. Removed some debug logs. Removed a duplicate zone packet send call. 2016-03-28 21:00:33 -04:00
Filip Maj
4d57aa72a6 Cleaned up the 0x02 packet. It fires in response to the 0x2 packet sent by the client it seems. 2016-03-28 12:14:10 -04:00
Filip Maj
a30311d12a Added director related code to player and packet processor. Cleaned up some debug messages. Added a flag when a player is "zoning in". 2016-03-28 11:31:21 -04:00
Filip Maj
f8ab0cd86d Added langauge code packet (0x6) and 0x2/0x7 temp packets to clean up the login packets sent by client. 2016-03-28 11:30:02 -04:00
Filip Maj
9c28e966f7 Added director code to player class and actor namespace. 2016-03-23 01:27:12 -04:00
Filip Maj
49a13effca Fleshed out the quest actor and added a place to store the current active quest actors in the Player actor. 2016-03-21 11:42:27 -04:00
Filip Maj
3162bedb17 Added trigger name to all the scripts that had params. Implemented the Gridania opening stoper. 2016-03-20 21:19:40 -04:00
Filip Maj
2048d8d188 Implemented a intra-zone teleport. Trigger Name is now sent to to the "eventStart" lua callback. Lua scripts can now access world manager. 2016-03-20 21:18:46 -04:00
Filip Maj
08fd862939 Implemented the emote and notice event conditions. 2016-03-20 19:34:39 -04:00
Filip Maj
0876b293e8 Started to implement private areas. Works mostly, but only one instance is created. All the push triggers work more or less except bounding box. 2016-03-20 19:29:38 -04:00
Filip Maj
67fed3dba5 Fixed bug where new characters would not have items equipped. This was due to the slotId was being saved, not the unique itemId. 2016-03-20 12:43:20 -04:00
Filip Maj
1417a0d841 Pushed the latest sql import file. 2016-03-20 12:30:29 -04:00
Filip Maj
01ba37c50d Added hair variation to the db. Turned out the hair highlight color was mis-sized (1 byte not 2) causing an invalid number when a variation was there. 2016-03-20 12:18:41 -04:00
Filip Maj
d80e9bed0e Added new DB changes to character creator. Overhauled the get character methods to include as much data as possible. Fixed an issue with undergarments not being set properly for certain race/classes. 2016-03-20 11:49:09 -04:00
Filip Maj
d9bccb5bff Fixed bug in equip script causing the classChange function to be called multiple times due to classId not being a local variable. Added some test scripts for Request Information/QuestJournal Command. 2016-03-19 19:22:28 -04:00
Filip Maj
335ec414d6 Levels are now loaded in from the database, and "mainSkillLevel" properties are also correctly set. 2016-03-19 19:21:16 -04:00
Filip Maj
1f60bcf4fc Finished code to send character skill (class) info. Added parameterdata request packet that requests this info. 2016-03-19 18:43:02 -04:00
Filip Maj
d2a5eaa2c8 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server 2016-03-19 10:23:20 -04:00
Filip Maj
a93843510e Cleaned up script packets and added the InfoResponse packet (internally the DataPacket). Added more info to the music packets. More work on class changing. 2016-03-19 10:22:20 -04:00
Filip Maj
6ef28e590c Folder rename: FFXIVClassic_Lobby_Server to FFXIV Classic Lobby Server. 2016-03-17 14:33:58 -04:00
Filip Maj
81abefcf39 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-server 2016-03-16 17:34:54 -04:00
Filip Maj
e4c296ca7e Cleaning up one more dependency. 2016-03-16 17:33:57 -04:00
Ioncannon
d4a3b601fa README.md edited online with Bitbucket 2016-03-16 21:21:42 +00:00
Filip Maj
c91e0ef146 Updated Newtonsoft JSON dependency in lobby server. 2016-03-16 17:13:33 -04:00
Filip Maj
3ece02932f Removed tracked packages. VS will restore new packages on first open. 2016-03-16 17:10:38 -04:00
Filip Maj
f92d9ea2f3 Added lobby to the solution. Cleaning up dependencies. 2016-03-16 17:07:48 -04:00
Filip Maj
978f31e64a Merged the master branch of the Lobby server repo, into the map server repo. There is no reason to have two separate repos and the lobby server project will be now under the whole FFXIV Classic VS solution. 2016-03-16 14:53:00 -04:00
Filip Maj
01eceee58f Smallclothes equipment are now saved under the "Adventurer" class as they are a separate thing from classes. 2016-03-12 10:54:08 -05:00
Filip Maj
f71b4ef031 Fixed bug where DB writing for equipment would be toggled off if gearset was nil for some reason. Added in a property change to get the bottom class icon to refresh showing the new class. 2016-03-12 10:01:41 -05:00
Filip Maj
e409d3792c Scripts for running the various commands/npcs (done so far) are pushed. 2016-03-12 02:54:03 -05:00
Filip Maj
e851c767df Finished check command as well as gearsets and switching between classes. Property changes still have to be written though. 2016-03-12 02:52:34 -05:00
Filip Maj
44e5430fdc Implemented equipment and equip graphics. Add some zone callbacks and "first start" functionality. Added playtime. 2016-03-06 17:55:42 -05:00
Filip Maj
88cb73bc09 Blowfish key is now generate from client's handshake packet. 2016-03-05 18:02:37 -05:00
Filip Maj
c9116005d6 Changed untradeable to itemType (since it's used for bazaar as well). 2016-02-22 22:33:52 -05:00
Filip Maj
83fb9badd7 Finished writing the query and editing the inventory methods to use the gamedata. 2016-02-21 21:44:11 -05:00
Filip Maj
4bebeb387a Fixed use to isUsable in utility function. 2016-02-21 20:48:54 -05:00
Filip Maj
0e4b9f4263 Finished writing final datastructure for items. Did some cleanup in server. 2016-02-21 20:48:07 -05:00
Filip Maj
e72f1d3dcf Finished the structure of the database item loader. Added the graphics portion of the reader. 2016-02-21 16:34:29 -05:00
Filip Maj
2f3f677ec4 Wrote item loaders for gamedata. 2016-02-21 16:13:35 -05:00
Filip Maj
d66d2908ec Added in all the utility functions from the Item script. 2016-02-21 15:00:49 -05:00
Filip Maj
d2ac603efa Renamed the "Item" object to InventoryItem. Began writing the Item object that will be used to store game data items. 2016-02-21 14:06:23 -05:00
Filip Maj
c2f0b9d999 Equipment now working. Still need to write database saving/loading. 2016-02-20 21:20:54 -05:00
Filip Maj
b7fd3e442c Added 0x6c to the actorIdChanger. Mount appearance is now broadcast to show Goobbue. Equipment packets implemented as well as Equipment object. 2016-02-20 16:51:35 -05:00
Filip Maj
5acc769505 The correct appearance is now set depending on what class you choose in chara creator. The actual items will be added in by the map server. The appearance is handled by the lobby in the case the user successfully makes a character, but never logs into the server (due to a disconnect or something). 2016-02-20 11:20:59 -05:00
Filip Maj
d5f07b97a8 Added a cleanup thread to delete any disconnected connections. 2016-02-20 00:38:32 -05:00
Filip Maj
1c5f8b3d0b Fixed a character appearance bug when making a character. Current zone now shown. Added a utility for setting new gear appearance. 2016-02-20 00:11:51 -05:00
Filip Maj
23dcc1dafe Reorganized the packets to follow the format the map server follows. 2016-02-18 23:55:03 -05:00
Filip Maj
a47d5f96a5 Fixed emotes not being sent to the emoter. Fixed appearance packets' gloves/legs being ordered wrong. Chat is implemented. Changed commands to start with '!'. 2016-02-18 22:38:54 -05:00
Filip Maj
8c73f6e926 Ported over the network code from the map server. A lot of backedlogged changes are here as well. 2016-02-16 23:35:21 -05:00
Filip Maj
c6ac8b2f14 Fixed an error in the netcode that would cause the client to d/c on partial packets. Set buffer size to 0xFFFF due to disconnect on really large packets (ie gm packet). Implemented support ticket packets. 2016-02-16 22:53:53 -05:00
Filip Maj
cb4171f1fd Bunch of bugfixes and final tweaking to get inventory working. 2016-02-14 12:09:18 -05:00
Filip Maj
47be08fbf7 Finished remove item code. Inventory Add/Remove should be complete. 2016-02-13 22:25:40 -05:00
Filip Maj
110a112328 Implemented remove though will need to test more. Finished tweaking addItem(itemId, quantity). 2016-02-13 21:14:49 -05:00
Filip Maj
73732ac542 Fully implemented Lua Param Data Type 9. 2016-02-13 16:39:33 -05:00
Filip Maj
1f3cd8eaf3 Added the two new datatypes to get the inventory commands to parse properly. 2016-02-13 16:33:33 -05:00
Filip Maj
69b34fdce7 Reworked the inventory system so that each inventory type is contained in it's own "Inventory" object. Also, fixed the Inventory packets being all malformed. 2016-02-13 14:12:05 -05:00
Filip Maj
df2ac1fb32 Added all the inventory packets, and implemented add and get inventory from the db to the server. Inventory at login is now connected to the db! 2016-02-11 22:14:40 -05:00
Filip Maj
c4dea467e3 Fixed missing seek, added battle packet 0x13C. 2016-02-07 15:43:32 -05:00
Filip Maj
0d880b6b75 Figured out most of the battle action packets; 0x139, 0x13a, 0x13b. 2016-02-07 15:28:08 -05:00
Filip Maj
6d8064aa6c Began work on the battle action packet. 2016-02-07 13:06:04 -05:00
Filip Maj
42ba95b69d Reworked zones to allow for isolated zones (IE Opening Zone) and private area instances. 2016-02-07 13:05:54 -05:00
Filip Maj
c83b4a12b9 Added the game message packets. Fixed "canRideChocobo" param in zone not working. Added a lot more function for lua to access. Various minor changes. 2016-02-03 00:45:11 -05:00
Filip Maj
cfb29b912f Speed is now stored in the actor. Redid actor speed packet to use float. Added GameMessagePacket which can use all 20 msg packets. Added functions for lua side to call in player obj. 2016-02-02 00:02:06 -05:00
Filip Maj
fe69f069ea Lua Parameter type 0 is now signed int. Type 1 in unsigned int. ShopSalesman script uses signed. 2016-01-31 12:23:12 -05:00
Filip Maj
45b9f9a064 Looked at pong packet once again. First 32bits are resent of incoming ping packet. Next 16bits always 0x14D. Heard from another dev this may improve movement for multiplayer. 2016-01-28 23:25:11 -05:00
Filip Maj
5c8277fbb9 Redid the database, actor_class and npclist was combined. Added commands to the lua engine. Script will default to a PopulaceStandard if no script detected to avoid crashing. Static Actors now loaded from the static actors file. 2016-01-28 23:24:20 -05:00
Filip Maj
789df97d48 More static actors loaded and can be accessed anywhere. Improved lua scripting to allow small talk. 2016-01-25 01:10:43 -05:00
Filip Maj
300748668f Removed NLua and replaced it with MoonSharp. Scripting for NPCs has been implemented, but still have to test a lot. 2016-01-24 17:11:35 -05:00
Filip Maj
74be19c51d Moved lua engine into server. 2016-01-24 10:32:37 -05:00
Filip Maj
e043be5ca4 Added a way to reset a zone and reload the NPC list. 2016-01-24 03:10:17 -05:00
Filip Maj
f1025f89d3 Rewrote the server commands. They can now accept a client connection, letting a user fire a command from within the game and only receive the response. Added actor removal for the instance system. Removed hardcoded packet 9. 2016-01-23 23:28:12 -05:00
Filip Maj
7bc3c8c2dd Added event conditions to the database. This is a JSON object that stores all types of conditions. The server also sends the correct packets to enable these conditions (minus the push packets... those aren't implemented). 2016-01-23 22:11:45 -05:00
Filip Maj
3b48ed1f74 Added actor event packets. 2016-01-23 20:12:04 -05:00
Filip Maj
ea6b1e33c7 NPCs now spawn and load template from the db. 2016-01-20 23:18:10 -05:00
Filip Maj
02b90edd3f Added two new commands; warp by zone entrance and a getpos command. Fixed character actor not appearing on zone... 0x2 packet only sent on login. Added spawnType to getSpawnPackets. 2016-01-20 00:02:57 -05:00
Filip Maj
3fcc9eea49 Moved the warp and send packet code out of packet processor and into the server object. Rewrote the warp command to use the world manager (still working on it). Add a playmusic command. 2016-01-19 22:07:29 -05:00
Filip Maj
d90dc0cb80 Redid the initial handshake code to handle the found connection type field. Server should no longer create two player objects and code has been reduced. Removed instances of connection1/2 getters... server simply sends packets down zone connection. Fixed issue with world manager not zoning in player due to missing 0x2 packet. 2016-01-19 21:47:59 -05:00
Filip Maj
093d3b7c15 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxiv-classic-map-server 2016-01-19 21:06:50 -05:00
Filip Maj
0462b790b7 Built a WorldManager to handle zones. 2016-01-19 21:06:41 -05:00
Filip Maj
5d11a0b356 Changed isEncrypted to isCompressed in basepacket. After speaking with another dev, game packets are not encrypted... just compressed. Figured out that byte 3 of basepacket is the connection type when handshaking with the map server. 2016-01-19 13:31:24 -05:00
Filip Maj
887da7b503 Moving zone/login stuff away from packetprocessor into a "WorldManager" class. 2016-01-17 23:36:34 -05:00
Filip Maj
a640e08fe1 Merge branch 'scripting' 2016-01-17 11:54:27 -05:00
Filip Maj
db62b05fdc Merge branch 'database_work' into scripting
# Conflicts:
#	FFXIVClassic Map Server/actors/chara/Character.cs
#	FFXIVClassic Map Server/actors/chara/npc/Npc.cs
#	FFXIVClassic Map Server/actors/chara/player/Player.cs
2016-01-17 11:53:52 -05:00
Filip Maj
d77344b725 Multiple zones are now stored on the server and accessed properly. 2016-01-17 11:48:55 -05:00
Filip Maj
0e85e2bddf Implementing zone settings from DB. 2016-01-17 01:51:02 -05:00
Filip Maj
9d67718060 Added utils to generate sql tables from game data. 2016-01-17 01:50:38 -05:00
Filip Maj
1c845e62e3 Implemented WeatherDirector , ZoneMaster, and WorldMaster in code. Zone objects has become the ZoneMaster actor object. 2016-01-16 23:03:04 -05:00
Filip Maj
724445a54a Achievements are now loaded from the database. Cleaned up login packets. 2016-01-16 18:38:49 -05:00
Filip Maj
f7702bca13 Added some new work parameters. Fixed GC Packet (wrong sized data). Connected player data packets to DB. Cleaned up login code. 2016-01-16 11:26:35 -05:00
Filip Maj
2bf532a6db Fixed bug in property reflection code. If array had 2 or more digit index, would truncate to 1 digit. 2016-01-12 01:20:09 -05:00
Filip Maj
e47904dc30 Got commands working. Wrong ID for one of em. 2016-01-12 00:43:52 -05:00
Filip Maj
aebaa1b70e Changed param constants. 2016-01-10 23:21:06 -05:00
Filip Maj
d9e274eb09 Quests and Leves now load from the db. 2016-01-10 14:15:46 -05:00
Filip Maj
5b5b99bb4b Got most of the properties getting set. Commands and General Parameters broken. 2016-01-10 13:36:36 -05:00
Filip Maj
d42dac543f Fixed wrong values sent for the setactorproperty packet. Initialized commandSlot_compatibility which was null. 2016-01-10 11:47:22 -05:00
Filip Maj
156f8e1e7d Added get init packets to packet processor. 2016-01-10 03:09:01 -05:00
Filip Maj
bea796ec85 Removed old db stuff. 2016-01-10 03:05:22 -05:00
Filip Maj
625ee299f7 Added more defaults. 2016-01-10 02:59:16 -05:00
Filip Maj
3205bd1c83 Added property init function which should get everything working. Added defaults to some properties. 2016-01-10 02:44:32 -05:00
Filip Maj
44c45c4ad2 Cleanup. 2016-01-09 21:35:45 -05:00
Filip Maj
524b57ed30 stop tracking this 2016-01-08 23:40:01 -05:00
Filip Maj
af4a0d5546 Changing how appearance works, pulled from a DB now. 2015-10-05 19:37:03 -04:00
Filip Maj
ad0cfca160 Added missing dapper package stuff. 2015-10-02 22:40:51 -04:00
Filip Maj
0c1d993b32 Removed old Blowfish DLL project, added server position to server research doc. 2015-10-02 22:39:38 -04:00
Filip Maj
2f98046d15 Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxivclassic-lobby-server 2015-10-02 22:37:44 -04:00
Filip Maj
6e33f39ae2 Removed packets since all lobby stuff is done and these are map server related. 2015-10-02 22:37:08 -04:00
Filip Maj
82ec583528 Removed Blowfish from solution. 2015-10-02 19:03:43 -04:00
Filip Maj
733c857c3f Blowfish reimplemented in C#, get's rid of the dll dependency allowing server to be run on Linux (Mono) as well. 2015-10-02 19:03:07 -04:00
Filip Maj
3796bd3ebe Midlander Male model id wasn't being set properly. 2015-09-14 00:19:52 -04:00
Filip Maj
0f711ab694 Fixed wrong state numbers due to removing the "Deleted" state. 2015-09-13 21:30:54 -04:00
Filip Maj
ddf1d2d1a3 Added account and select character packet creators. Fixed wrong field being read for session id. Most of the server is now NOT hardcoded and customizable from the DB. Only hardcoded packet left is the initial handshake. 2015-09-13 18:21:41 -04:00
Filip Maj
caf3968e5b All appearance data seems to be working now. Equip model ids are sent but not set, so body is invisible. Figuring out how to handle this. 2015-09-13 14:12:41 -04:00
Filip Maj
9c4220bfd2 Chara info is now generated and sent without errors. Still need to figure out what equip IDs to use, and add face and characteristics data. 2015-09-13 11:56:48 -04:00
Filip Maj
d9302db3a0 Removed uneeded packets since they have been implemented programmatically. 2015-09-13 11:31:21 -04:00
Filip Maj
b717f6aeb1 Character deletes now delete the character from the DB instead of just changing the state. DB can now get single character. Character list is sent properly as per how 1.0 did it (only 1 'NEW' entry if available). Character info is now loaded from the new character packet and stored. It is also loaded for each character, encoded, and displayed (still testing). 2015-09-13 11:30:33 -04:00
Filip Maj
aadca3968d Merge branch 'master' of https://bitbucket.org/Ioncannon/ffxivclassic-lobby-server
Conflicts:
	FFXIVClassic_Lobby_Server/packets/CharacterListPacket.cs
2015-09-11 18:59:08 -04:00
Filip Maj
d6ec5e7240 Added all Console writes to the log. Put in "Max chars" for character list. 2015-09-11 18:57:22 -04:00
Filip Maj
d96f5be7ac Fixed the "end of list" byte. Think character lists should work. 2015-09-11 18:53:32 -04:00
Filip Maj
e90a25d0ab Replace ini file reader with full .NET implementation so it can run on Mono. 2015-09-10 19:00:44 -04:00
Filip Maj
d90cf6d953 Import and Retainer lists now sent. 2015-09-10 00:52:31 -04:00
Filip Maj
443212830a Finished character creator DB calls, moved DB stuff to Dapper, started work on get characters. 2015-09-09 00:08:46 -04:00
Filip Maj
9dfd6906b9 Added log code. 2015-09-08 19:39:52 -04:00
Filip Maj
7834b737eb Added sleep to packet thread to avoid cpu consumption. 2015-09-08 00:52:19 -04:00
Filip Maj
f566c14ea0 Fixed "getServer" bug in DB class. Cleaned up chara creator function. Fixed bug in error packet. 2015-09-08 00:42:02 -04:00
Filip Maj
754f14862a Finished the character creator packets. Worked on the character list packets. 2015-09-07 23:43:23 -04:00
Filip Maj
c78414f9be Added temp character and retainer packets. Added constants for easy modding. 2015-09-07 12:12:08 -04:00
Filip Maj
6c869353cc World list packet fixes, and worked on reserve packet. 2015-09-03 23:07:56 -04:00
Filip Maj
e7e267bd44 Reimplemented server list using BInaryWriter. 2015-09-03 22:20:53 -04:00
Filip Maj
a65e81273b Server list is now sent from db rather than hard coded. 2015-09-03 01:02:55 -04:00
Filip Maj
091166b41a Error and server list packets added. Reserve will send error to client if a character name is take for a server. Code to send out server list on GetCharacters added. 2015-09-02 14:07:45 -04:00
Filip Maj
c982493d66 Added all the packet files used for testing. 2015-08-27 10:27:54 -04:00
Filip Maj
f996f727dc Server now processes session ids and stores the correct logged in user in the clientconnection object. Reserve character now creates a entry in the db. 2015-08-27 10:19:00 -04:00
Filip Maj
c1e214175f Initial commit of the FFXIV 1.0 lobby server. 2015-08-26 13:38:58 -04:00
1337 changed files with 89557 additions and 61553 deletions

220
.gitignore vendored
View File

@@ -1,4 +1,216 @@
FFXIVClassic Map Server/bin/Debug/packets/wireshark packets/
FFXIVClassic Map Server/bin/
FFXIVClassic Map Server/obj/
config.ini
# Ignore files obtained from game client
/data/staticactors.bin
/data/packets/login/login*.bin
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml

View File

@@ -0,0 +1,419 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using NLog;
using NLog.Targets;
using Ionic.Zlib;
namespace FFXIVClassic.Common
{
[StructLayout(LayoutKind.Sequential)]
public struct BasePacketHeader
{
public byte isAuthenticated;
public byte isCompressed;
public ushort connectionType;
public ushort packetSize;
public ushort numSubpackets;
public ulong timestamp; //Miliseconds
}
public class BasePacket
{
public const int TYPE_ZONE = 1;
public const int TYPE_CHAT = 2;
public const int BASEPACKET_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public byte[] data;
public BasePacketHeader header;
//Loads a sniffed packet from a file
public unsafe BasePacket(string path)
{
var bytes = File.ReadAllBytes(path);
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
if (bytes.Length < header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
int packetSize = header.packetSize;
if (packetSize - BASEPACKET_SIZE != 0)
{
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
}
else
data = new byte[0];
}
//Loads a sniffed packet from a byte array
public unsafe BasePacket(byte[] bytes)
{
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
if (bytes.Length < header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
int packetSize = header.packetSize;
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
}
public unsafe BasePacket(byte[] bytes, ref int offset)
{
if (bytes.Length < offset + BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[offset])
{
header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
int packetSize = header.packetSize;
if (bytes.Length < offset + header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, offset + BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
offset += packetSize;
}
public BasePacket(BasePacketHeader header, byte[] data)
{
this.header = header;
this.data = data;
}
public List<SubPacket> GetSubpackets()
{
var subpackets = new List<SubPacket>(header.numSubpackets);
var offset = 0;
while (offset < data.Length)
subpackets.Add(new SubPacket(data, ref offset));
return subpackets;
}
public static unsafe BasePacketHeader GetHeader(byte[] bytes)
{
BasePacketHeader header;
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
return header;
}
public byte[] GetHeaderBytes()
{
var size = Marshal.SizeOf(header);
var arr = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public byte[] GetPacketBytes()
{
var outBytes = new byte[header.packetSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE);
Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length);
return outBytes;
}
//Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint actorID)
{
using (var mem = new MemoryStream(data))
{
using (var binWriter = new BinaryWriter(mem))
{
using (var binreader = new BinaryReader(mem))
{
while (binreader.BaseStream.Position + 4 < data.Length)
{
var read = binreader.ReadUInt32();
if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 ||
read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb || read == 0x6c)
//Original ID
{
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
binWriter.Write(actorID);
}
}
}
}
}
}
//Replaces all instances of the sniffed actorID with the given one
public void ReplaceActorID(uint fromActorID, uint actorID)
{
using (var mem = new MemoryStream(data))
{
using (var binWriter = new BinaryWriter(mem))
{
using (var binreader = new BinaryReader(mem))
{
while (binreader.BaseStream.Position + 4 < data.Length)
{
var read = binreader.ReadUInt32();
if (read == fromActorID) //Original ID
{
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
binWriter.Write(actorID);
}
}
}
}
}
}
public void DebugPrintPacket()
{
#if DEBUG
logger.ColorDebug(
string.Format("IsAuth:{0} IsEncrypted:{1}, Size:0x{2:X}, NumSubpackets:{3}{4}{5}",
header.isAuthenticated, header.isCompressed, header.packetSize, header.numSubpackets,
Environment.NewLine, Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkYellow);
foreach (var sub in GetSubpackets())
{
sub.DebugPrintSubPacket();
}
#endif
}
#region Utility Functions
public static BasePacket CreatePacket(List<SubPacket> subpackets, bool isAuthed, bool isCompressed)
{
//Create Header
var header = new BasePacketHeader();
byte[] data = null;
header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = (ushort) subpackets.Count;
header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size
foreach (var subpacket in subpackets)
header.packetSize += subpacket.header.subpacketSize;
data = new byte[header.packetSize - 0x10];
//Add Subpackets
var offset = 0;
foreach (var subpacket in subpackets)
{
var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, offset, subpacketData.Length);
offset += (ushort) subpacketData.Length;
}
Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset);
var packet = new BasePacket(header, data);
return packet;
}
public static BasePacket CreatePacket(SubPacket subpacket, bool isAuthed, bool isCompressed)
{
//Create Header
var header = new BasePacketHeader();
byte[] data = null;
header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size
header.packetSize += subpacket.header.subpacketSize;
data = new byte[header.packetSize - 0x10];
//Add Subpackets
var subpacketData = subpacket.GetBytes();
Array.Copy(subpacketData, 0, data, 0, subpacketData.Length);
Debug.Assert(data != null);
var packet = new BasePacket(header, data);
return packet;
}
public static BasePacket CreatePacket(byte[] data, bool isAuthed, bool isCompressed)
{
Debug.Assert(data != null);
//Create Header
var header = new BasePacketHeader();
header.isAuthenticated = isAuthed ? (byte) 1 : (byte) 0;
header.isCompressed = isCompressed ? (byte) 1 : (byte) 0;
header.numSubpackets = 1;
header.packetSize = BASEPACKET_SIZE;
header.timestamp = Utils.MilisUnixTimeStampUTC();
//Get packet size
header.packetSize += (ushort) data.Length;
var packet = new BasePacket(header, data);
return packet;
}
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public static BasePacket CreatePacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet)
{
var data = packet.data;
int size = packet.header.packetSize;
var offset = 0;
while (offset < data.Length)
{
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Encipher(data, offset + 0x10, header.subpacketSize - 0x10);
offset += header.subpacketSize;
}
}
public static unsafe void DecryptPacket(Blowfish blowfish, ref BasePacket packet)
{
var data = packet.data;
int size = packet.header.packetSize;
var offset = 0;
while (offset < data.Length)
{
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Decipher(data, offset + 0x10, header.subpacketSize - 0x10);
offset += header.subpacketSize;
}
}
public static unsafe void DecompressPacket(ref BasePacket packet)
{
using (var compressedStream = new MemoryStream(packet.data))
using (var zipStream = new ZlibStream(compressedStream, Ionic.Zlib.CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
packet.data = resultStream.ToArray();
}
}
public static unsafe void CompressPacket(ref BasePacket packet)
{
using (var compressedStream = new MemoryStream(packet.data))
using (var zipStream = new ZlibStream(compressedStream, Ionic.Zlib.CompressionMode.Compress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
packet.data = resultStream.ToArray();
}
}
#endregion
}
public static class LoggerExtensions
{
public static void ColorDebug(this Logger logger, string message, ConsoleOutputColor color)
{
var logEvent = new LogEventInfo(LogLevel.Debug, logger.Name, message);
logEvent.Properties["color"] = (int) color;
logger.Log(logEvent);
}
}
}

View File

@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.common
namespace FFXIVClassic.Common
{
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
sealed class BitfieldLengthAttribute : Attribute
public sealed class BitfieldLengthAttribute : Attribute
{
uint length;
@@ -19,7 +15,7 @@ namespace FFXIVClassic_Lobby_Server.common
public uint Length { get { return length; } }
}
static class PrimitiveConversion
public static class PrimitiveConversion
{
public static UInt32 ToUInt32<T>(T t) where T : struct
{

View File

@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.common
namespace FFXIVClassic.Common
{
public class Blowfish
{
@@ -287,7 +283,7 @@ namespace FFXIVClassic_Lobby_Server.common
public Blowfish(byte[] key)
{
initializeBlowfish(key);
InitializeBlowfish(key);
}
public void Encipher(byte[] data, int offset, int length)
@@ -299,7 +295,7 @@ namespace FFXIVClassic_Lobby_Server.common
{
uint xl = (uint)((data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24));
uint xr = (uint)((data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24));
blowfish_encipher(ref xl, ref xr);
BlowfishEncipher(ref xl, ref xr);
data[i + 0] = (byte)(xl >> 0);
data[i + 1] = (byte)(xl >> 8);
data[i + 2] = (byte)(xl >> 16);
@@ -320,7 +316,7 @@ namespace FFXIVClassic_Lobby_Server.common
{
uint xl = (uint)((data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24));
uint xr = (uint)((data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24));
blowfish_decipher(ref xl, ref xr);
BlowfishDecipher(ref xl, ref xr);
data[i + 0] = (byte)(xl >> 0);
data[i + 1] = (byte)(xl >> 8);
data[i + 2] = (byte)(xl >> 16);
@@ -355,7 +351,7 @@ namespace FFXIVClassic_Lobby_Server.common
return y;
}
private void blowfish_encipher(ref UInt32 xl, ref UInt32 xr)
private void BlowfishEncipher(ref UInt32 xl, ref UInt32 xr)
{
UInt32 temp;
Int32 i;
@@ -378,7 +374,7 @@ namespace FFXIVClassic_Lobby_Server.common
}
private void blowfish_decipher(ref UInt32 xl, ref UInt32 xr)
private void BlowfishDecipher(ref UInt32 xl, ref UInt32 xr)
{
UInt32 temp;
Int32 i;
@@ -387,13 +383,13 @@ namespace FFXIVClassic_Lobby_Server.common
xl = xl ^ P[i];
xr = F(xl) ^ xr;
/* Exchange xl and xr */
/* ExChange xl and xr */
temp = xl;
xl = xr;
xr = temp;
}
/* Exchange xl and xr */
/* ExChange xl and xr */
temp = xl;
xl = xr;
xr = temp;
@@ -403,7 +399,7 @@ namespace FFXIVClassic_Lobby_Server.common
}
private int initializeBlowfish(byte [] key)
private int InitializeBlowfish(byte [] key)
{
Int16 i;
Int16 j;
@@ -437,7 +433,7 @@ namespace FFXIVClassic_Lobby_Server.common
for (i = 0; i < N + 2; i += 2)
{
blowfish_encipher(ref datal, ref datar);
BlowfishEncipher(ref datal, ref datar);
P[i] = datal;
P[i + 1] = datar;
@@ -447,7 +443,7 @@ namespace FFXIVClassic_Lobby_Server.common
{
for (j = 0; j < 256; j += 2)
{
blowfish_encipher(ref datal, ref datar);
BlowfishEncipher(ref datal, ref datar);
S[i,j] = datal;
S[i,j + 1] = datar;
}

View File

@@ -0,0 +1,158 @@
using System;
namespace FFXIVClassic.Common
{
namespace EfficientHashTables
{
public class Efficient64bitHashTable<T>
{
private class element
{
public ulong _key;
public T _value;
};
private element[][] _buckets;
private uint _capacity;
public Efficient64bitHashTable()
{
_capacity = 214373; // some prime number
_buckets = new element[_capacity][];
}
public Efficient64bitHashTable(uint capacity)
{
_capacity = capacity;
_buckets = new element[_capacity][];
}
public uint Hash(ulong key)
{
return (uint)(key % _capacity);
}
public void Add(ulong key, T value)
{
uint hsh = Hash(key);
element[] e;
if (_buckets[hsh] == null)
_buckets[hsh] = e = new element[1];
else
{
foreach (var elem in _buckets[hsh])
if (elem._key == key)
{
elem._value = value;
return;
}
e = new element[_buckets[hsh].Length + 1];
Array.Copy(_buckets[hsh], 0, e, 1, _buckets[hsh].Length);
_buckets[hsh] = e;
}
e[0] = new element { _key = key, _value = value };
}
public T Get(ulong key)
{
uint hsh = Hash(key);
element[] e = _buckets[hsh];
if (e == null) return default(T);
foreach (var f in e)
if (f._key == key)
return f._value;
return default(T);
}
public bool Has(ulong key)
{
uint hsh = Hash(key);
element[] e = _buckets[hsh];
if (e == null) return false;
foreach (var f in e)
if (f._key == key)
return true;
return false;
}
public int Count()
{
int r = 0;
foreach (var e in _buckets)
if (e != null)
r += e.Length;
return r;
}
}
public class Efficient32bitHashTable<T>
{
private class element
{
public uint _key;
public T _value;
};
private element[][] _buckets;
private uint _capacity;
public Efficient32bitHashTable()
{
_capacity = 463; // some prime number
_buckets = new element[_capacity][];
}
public Efficient32bitHashTable(uint capacity)
{
_capacity = capacity;
_buckets = new element[_capacity][];
}
public uint Hash(uint key)
{
return (uint)(key % _capacity);
}
public void Add(uint key, T value)
{
uint hsh = Hash(key);
element[] e;
if (_buckets[hsh] == null)
_buckets[hsh] = e = new element[1];
else
{
foreach (var elem in _buckets[hsh])
if (elem._key == key)
{
elem._value = value;
return;
}
e = new element[_buckets[hsh].Length + 1];
Array.Copy(_buckets[hsh], 0, e, 1, _buckets[hsh].Length);
_buckets[hsh] = e;
}
e[0] = new element { _key = key, _value = value };
}
public T Get(uint key)
{
uint hsh = Hash(key);
element[] e = _buckets[hsh];
if (e == null) return default(T);
foreach (var f in e)
if (f._key == key)
return f._value;
return default(T);
}
public int Count()
{
int r = 0;
foreach (var e in _buckets)
if (e != null)
r += e.Length;
return r;
}
}
}
}

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props') AND '$(OS)' == 'Windows_NT'" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props') AND '$(OS)' == 'Windows_NT'" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3A3D6626-C820-4C18-8C81-64811424F20E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FFXIVClassic.Common</RootNamespace>
<AssemblyName>FFXIVClassic.Common</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<NuGetPackageImportStamp>792e4711</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<RegisterForComInterop>false</RegisterForComInterop>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="DotNetZip">
<HintPath>..\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll</HintPath>
</Reference>
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.5\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BasePacket.cs" />
<Compile Include="Bitfield.cs" />
<Compile Include="Blowfish.cs" />
<Compile Include="EfficientHashTables.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sql.cs" />
<Compile Include="STA_INIFile.cs" />
<Compile Include="SubPacket.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FFXIVClassic.Common")]
[assembly: AssemblyDescription("Common class library for FFXIVClassic project")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ffxivclassic.fragmenterworks.com")]
[assembly: AssemblyProduct("FFXIVClassic.Common")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3a3d6626-c820-4c18-8c81-64811424f20e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -9,10 +9,9 @@ using System.Globalization;
using System.IO;
using System.Text;
namespace STA.Settings
namespace FFXIVClassic.Common
{
internal class INIFile
public class INIFile
{
#region "Declarations"
@@ -179,7 +178,7 @@ namespace STA.Settings
// *** Check if original file exists ***
bool OriginalFileExists = File.Exists(m_FileName);
// *** Get temporary file name ***
// *** get temporary file name ***
string TmpFileName = Path.ChangeExtension(m_FileName, "$n$");
// *** Copy content of original file to temporary file, replace modified values ***
@@ -199,7 +198,7 @@ namespace STA.Settings
// *** Open the original file ***
sr = new StreamReader(m_FileName);
// *** Read the file original content, replace changes with local cache values ***
// *** Read the file original content, replace Changes with local cache values ***
string s;
string SectionName;
string Key = null;
@@ -337,7 +336,7 @@ namespace STA.Settings
}
// *** Read a value from local cache ***
internal string GetValue(string SectionName, string Key, string DefaultValue)
public string GetValue(string SectionName, string Key, string DefaultValue)
{
// *** Lazy loading ***
if (m_Lazy)
@@ -380,7 +379,7 @@ namespace STA.Settings
Dictionary<string, string> Section;
if (!m_Sections.TryGetValue(SectionName, out Section))
{
// *** If it doesn't, add it ***
// *** If it Doesn't, Add it ***
Section = new Dictionary<string, string>();
m_Sections.Add(SectionName,Section);
}
@@ -474,7 +473,7 @@ namespace STA.Settings
return DefaultValue;
}
internal double GetValue(string SectionName, string Key, double DefaultValue)
internal Double GetValue(string SectionName, string Key, Double DefaultValue)
{
string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture));
double Value;
@@ -519,7 +518,7 @@ namespace STA.Settings
SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture));
}
internal void SetValue(string SectionName, string Key, double Value)
internal void SetValue(string SectionName, string Key, Double Value)
{
SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture));
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using NLog;
using System.Data;
using System.Data.Common;
namespace FFXIVClassic.Common
{
// todo:
// havent decided whether it's worth wrapping every sql class
// so i'll just leave it with logger for now
public class Sql
{
public static Logger Log = LogManager.GetCurrentClassLogger();
}
}

View File

@@ -0,0 +1,218 @@
using System;
using System.Runtime.InteropServices;
using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
namespace FFXIVClassic.Common
{
[StructLayout(LayoutKind.Sequential)]
public struct SubPacketHeader
{
public ushort subpacketSize;
public ushort type;
public uint sourceId;
public uint targetId;
public uint unknown1;
}
[StructLayout(LayoutKind.Sequential)]
public struct GameMessageHeader
{
public ushort unknown4; //Always 0x14
public ushort opcode;
public uint unknown5;
public uint timestamp;
public uint unknown6;
}
public class SubPacket
{
public const int SUBPACKET_SIZE = 0x10;
public const int GAMEMESSAGE_SIZE = 0x10;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public byte[] data;
public GameMessageHeader gameMessage;
public SubPacketHeader header;
public unsafe SubPacket(byte[] bytes, ref int offset)
{
if (bytes.Length < offset + SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
fixed (byte* pdata = &bytes[offset])
{
header = (SubPacketHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (header.type == 0x3)
{
fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE])
{
gameMessage =
(GameMessageHeader) Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader));
}
}
if (bytes.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
if (header.type == 0x3)
{
data = new byte[header.subpacketSize - SUBPACKET_SIZE - GAMEMESSAGE_SIZE];
Array.Copy(bytes, offset + SUBPACKET_SIZE + GAMEMESSAGE_SIZE, data, 0, data.Length);
}
else
{
data = new byte[header.subpacketSize - SUBPACKET_SIZE];
Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length);
}
offset += header.subpacketSize;
}
public SubPacket(ushort opcode, uint sourceId, byte[] data) : this(true, opcode, sourceId, data) { }
public SubPacket(bool isGameMessage, ushort opcode, uint sourceId, byte[] data)
{
header = new SubPacketHeader();
if (isGameMessage)
{
gameMessage = new GameMessageHeader();
gameMessage.opcode = opcode;
gameMessage.timestamp = Utils.UnixTimeStampUTC();
gameMessage.unknown4 = 0x14;
gameMessage.unknown5 = 0x00;
gameMessage.unknown6 = 0x00;
}
header.sourceId = sourceId;
header.targetId = 0;
if (isGameMessage)
header.type = 0x03;
else
header.type = opcode;
header.unknown1 = 0x00;
this.data = data;
header.subpacketSize = (ushort) (SUBPACKET_SIZE + data.Length);
if (isGameMessage)
header.subpacketSize += GAMEMESSAGE_SIZE;
}
public SubPacket(SubPacket original, uint newTargetId)
{
header = new SubPacketHeader();
gameMessage = original.gameMessage;
header.subpacketSize = original.header.subpacketSize;
header.type = original.header.type;
header.sourceId = original.header.sourceId;
header.targetId = newTargetId;
data = original.data;
}
public void SetTargetId(uint target)
{
this.header.targetId = target;
}
public byte[] GetHeaderBytes()
{
var size = Marshal.SizeOf(header);
var arr = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public byte[] GetGameMessageBytes()
{
var size = Marshal.SizeOf(gameMessage);
var arr = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(gameMessage, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public byte[] GetBytes()
{
var outBytes = new byte[header.subpacketSize];
Array.Copy(GetHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE);
if (header.type == 0x3)
Array.Copy(GetGameMessageBytes(), 0, outBytes, SUBPACKET_SIZE, GAMEMESSAGE_SIZE);
Array.Copy(data, 0, outBytes, SUBPACKET_SIZE + (header.type == 0x3 ? GAMEMESSAGE_SIZE : 0), data.Length);
return outBytes;
}
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public static SubPacket CreatePacket(ref int offset, byte[] buffer, int bytesRead)
{
SubPacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new SubPacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
public void DebugPrintSubPacket()
{
#if DEBUG
logger.ColorDebug(
string.Format("Size:0x{0:X} Opcode:0x{1:X}{2}{3}", header.subpacketSize, gameMessage.opcode,
Environment.NewLine,
Utils.ByteArrayToHex(GetHeaderBytes())), ConsoleOutputColor.DarkRed);
if (header.type == 0x03)
{
logger.ColorDebug(Utils.ByteArrayToHex(GetGameMessageBytes(), SUBPACKET_SIZE),
ConsoleOutputColor.DarkRed);
logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE),
ConsoleOutputColor.DarkMagenta);
}
else
logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE),
ConsoleOutputColor.DarkMagenta);
#endif
}
}
}

View File

@@ -0,0 +1,355 @@
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic.Common
{
public static class Utils
{
private static readonly uint[] _lookup32 = CreateLookup32();
private static uint[] CreateLookup32()
{
var result = new uint[256];
for (var i = 0; i < 256; i++)
{
var s = i.ToString("X2");
result[i] = s[0] + ((uint)s[1] << 16);
}
return result;
}
public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16)
{
if (bytes == null)
{
return string.Empty;
}
var hexChars = "0123456789ABCDEF".ToCharArray();
var offsetBlock = 8 + 3;
var byteBlock = offsetBlock + bytesPerLine * 3 + (bytesPerLine - 1) / 8 + 2;
var lineLength = byteBlock + bytesPerLine + Environment.NewLine.Length;
var line = (new string(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
var numLines = (bytes.Length + bytesPerLine - 1) / bytesPerLine;
var sb = new StringBuilder(numLines * lineLength);
for (var i = 0; i < bytes.Length; i += bytesPerLine)
{
var h = i + offset;
line[0] = hexChars[(h >> 28) & 0xF];
line[1] = hexChars[(h >> 24) & 0xF];
line[2] = hexChars[(h >> 20) & 0xF];
line[3] = hexChars[(h >> 16) & 0xF];
line[4] = hexChars[(h >> 12) & 0xF];
line[5] = hexChars[(h >> 8) & 0xF];
line[6] = hexChars[(h >> 4) & 0xF];
line[7] = hexChars[(h >> 0) & 0xF];
var hexColumn = offsetBlock;
var charColumn = byteBlock;
for (var j = 0; j < bytesPerLine; j++)
{
if (j > 0 && (j & 7) == 0)
{
hexColumn++;
}
if (i + j >= bytes.Length)
{
line[hexColumn] = ' ';
line[hexColumn + 1] = ' ';
line[charColumn] = ' ';
}
else
{
var by = bytes[i + j];
line[hexColumn] = hexChars[(by >> 4) & 0xF];
line[hexColumn + 1] = hexChars[by & 0xF];
line[charColumn] = by < 32 ? '.' : (char)by;
}
hexColumn += 3;
charColumn++;
}
sb.Append(line);
}
return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
}
public static uint UnixTimeStampUTC()
{
uint unixTimeStamp;
var currentTime = DateTime.Now;
var zuluTime = currentTime.ToUniversalTime();
var unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (uint)zuluTime.Subtract(unixEpoch).TotalSeconds;
return unixTimeStamp;
}
public static ulong MilisUnixTimeStampUTC()
{
ulong unixTimeStamp;
var currentTime = DateTime.Now;
var zuluTime = currentTime.ToUniversalTime();
var unixEpoch = new DateTime(1970, 1, 1);
unixTimeStamp = (ulong)zuluTime.Subtract(unixEpoch).TotalMilliseconds;
return unixTimeStamp;
}
public static ulong SwapEndian(ulong input)
{
return 0x00000000000000FF & (input >> 56) |
0x000000000000FF00 & (input >> 40) |
0x0000000000FF0000 & (input >> 24) |
0x00000000FF000000 & (input >> 8) |
0x000000FF00000000 & (input << 8) |
0x0000FF0000000000 & (input << 24) |
0x00FF000000000000 & (input << 40) |
0xFF00000000000000 & (input << 56);
}
public static uint SwapEndian(uint input)
{
return ((input >> 24) & 0xff) |
((input << 8) & 0xff0000) |
((input >> 8) & 0xff00) |
((input << 24) & 0xff000000);
}
public static int SwapEndian(int input)
{
var inputAsUint = (uint)input;
input = (int)
(((inputAsUint >> 24) & 0xff) |
((inputAsUint << 8) & 0xff0000) |
((inputAsUint >> 8) & 0xff00) |
((inputAsUint << 24) & 0xff000000));
return input;
}
public static uint MurmurHash2(string key, uint seed)
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
var data = Encoding.ASCII.GetBytes(key);
const uint m = 0x5bd1e995;
const int r = 24;
var len = key.Length;
var dataIndex = len - 4;
// Initialize the hash to a 'random' value
var h = seed ^ (uint)len;
// Mix 4 bytes at a time into the hash
while (len >= 4)
{
h *= m;
var k = (uint)BitConverter.ToInt32(data, dataIndex);
k = ((k >> 24) & 0xff) | // move byte 3 to byte 0
((k << 8) & 0xff0000) | // move byte 1 to byte 2
((k >> 8) & 0xff00) | // move byte 2 to byte 1
((k << 24) & 0xff000000); // byte 0 to byte 3
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
dataIndex -= 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch (len)
{
case 3:
h ^= (uint)data[0] << 16;
goto case 2;
case 2:
h ^= (uint)data[len - 2] << 8;
goto case 1;
case 1:
h ^= data[len - 1];
h *= m;
break;
}
;
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
public static byte[] ConvertBoolArrayToBinaryStream(bool[] array)
{
var data = new byte[array.Length / 8 + (array.Length % 8 != 0 ? 1 : 0)];
var dataCounter = 0;
for (var i = 0; i < array.Length; i += 8)
{
for (var bitCount = 0; bitCount < 8; bitCount++)
{
if (i + bitCount >= array.Length)
break;
data[dataCounter] = (byte)(((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]);
}
dataCounter++;
}
return data;
}
public static string ToStringBase63(int number)
{
var lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var secondDigit = lookup.Substring((int)Math.Floor(number / (double)lookup.Length), 1);
var firstDigit = lookup.Substring(number % lookup.Length, 1);
return secondDigit + firstDigit;
}
public static string FFXIVLoginStringDecodeBinary(string path)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
byte[] data = File.ReadAllBytes(path);
int offset = 0x5405a;
//int offset = 0x5425d;
//int offset = 0x53ea0;
while (true)
{
string result = "";
uint key = (uint)data[offset + 0] << 8 | data[offset + 1];
uint key2 = data[offset + 2];
key = RotateRight(key, 1) & 0xFFFF;
key -= 0x22AF;
key &= 0xFFFF;
key2 = key2 ^ key;
key = RotateRight(key, 1) & 0xFFFF;
key -= 0x22AF;
key &= 0xFFFF;
uint finalKey = key;
key = data[offset + 3];
uint count = (key2 & 0xFF) << 8;
key = key ^ finalKey;
key &= 0xFF;
count |= key;
int count2 = 0;
while (count != 0)
{
uint encrypted = data[offset + 4 + count2];
finalKey = RotateRight(finalKey, 1) & 0xFFFF;
finalKey -= 0x22AF;
finalKey &= 0xFFFF;
encrypted = encrypted ^ (finalKey & 0xFF);
result += (char)encrypted;
count--;
count2++;
}
offset += 4 + count2;
}
}
public static string FFXIVLoginStringDecode(byte[] data)
{
string result = "";
uint key = (uint)data[0] << 8 | data[1];
uint key2 = data[2];
key = RotateRight(key, 1) & 0xFFFF;
key -= 0x22AF;
key2 = key2 ^ key;
key = RotateRight(key, 1) & 0xFFFF;
key -= 0x22AF;
uint finalKey = key;
key = data[3];
uint count = (key2 & 0xFF) << 8;
key = key ^ finalKey;
key &= 0xFF;
count |= key;
int count2 = 0;
while (count != 0)
{
uint encrypted = data[4 + count2];
finalKey = RotateRight(finalKey, 1) & 0xFFFF;
finalKey -= 0x22AF;
encrypted = encrypted ^ (finalKey & 0xFF);
result += (char)encrypted;
count--;
count2++;
}
return result;
}
public static byte[] FFXIVLoginStringEncode(uint key, string text)
{
key = key & 0xFFFF;
uint count = 0;
byte[] asciiBytes = Encoding.ASCII.GetBytes(text);
byte[] result = new byte[4 + text.Length];
for (count = 0; count < text.Length; count++)
{
result[result.Length - count - 1] = (byte)(asciiBytes[asciiBytes.Length - count - 1] ^ (key & 0xFF));
key += 0x22AF;
key &= 0xFFFF;
key = RotateLeft(key, 1) & 0xFFFF;
}
count = count ^ key;
result[3] = (byte)(count & 0xFF);
key += 0x22AF & 0xFFFF;
key = RotateLeft(key, 1) & 0xFFFF;
result[2] = (byte)(key & 0xFF);
key += 0x22AF & 0xFFFF;
key = RotateLeft(key, 1) & 0xFFFF;
result[1] = (byte)(key & 0xFF);
result[0] = (byte)((key >> 8) & 0xFF);
return result;
}
public static uint RotateLeft(uint value, int bits)
{
return (value << bits) | (value >> (16 - bits));
}
public static uint RotateRight(uint value, int bits)
{
return (value >> bits) | (value << (16 - bits));
}
}
}

View File

@@ -3,7 +3,7 @@
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
</configuration>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.10.1" targetFramework="net45" />
<package id="MySql.Data" version="6.9.8" targetFramework="net45" />
<package id="NLog" version="4.3.5" targetFramework="net45" />
</packages>

View File

@@ -0,0 +1,76 @@
using System;
using System.Net.Sockets;
using FFXIVClassic.Common;
using System.Collections.Concurrent;
using Cyotek.Collections.Generic;
using System.Net;
namespace FFXIVClassic_Lobby_Server
{
class ClientConnection
{
//Connection stuff
public Blowfish blowfish;
public Socket socket;
public byte[] buffer = new byte[0xffff];
public CircularBuffer<byte> incomingStream = new CircularBuffer<byte>(1024);
public BlockingCollection<BasePacket> SendPacketQueue = new BlockingCollection<BasePacket>(100);
public int lastPartialSize = 0;
//Instance Stuff
public uint currentUserId = 0;
public uint currentAccount;
public string currentSessionToken;
//Chara Creation
public string newCharaName;
public uint newCharaPid;
public uint newCharaCid;
public ushort newCharaSlot;
public ushort newCharaWorldId;
public void ProcessIncoming(int bytesIn)
{
if (bytesIn == 0)
return;
incomingStream.Put(buffer, 0, bytesIn);
}
public void QueuePacket(BasePacket packet)
{
SendPacketQueue.Add(packet);
}
public void FlushQueuedSendPackets()
{
if (!socket.Connected)
return;
while (SendPacketQueue.Count > 0)
{
BasePacket packet = SendPacketQueue.Take();
byte[] packetBytes = packet.GetPacketBytes();
byte[] buffer = new byte[0xffff];
Array.Copy(packetBytes, buffer, packetBytes.Length);
try {
socket.Send(packetBytes);
}
catch(Exception e)
{ Program.Log.Error("Weird case, socket was d/ced: {0}", e); }
}
}
public String GetAddress()
{
return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port);
}
public void Disconnect()
{
socket.Shutdown(SocketShutdown.Both);
socket.Disconnect(false);
}
}
}

View File

@@ -0,0 +1,90 @@
using FFXIVClassic.Common;
using System;
using System.IO;
using System.Linq;
using System.Net;
namespace FFXIVClassic_Lobby_Server
{
class ConfigConstants
{
public static String OPTIONS_BINDIP;
public static String OPTIONS_PORT;
public static bool OPTIONS_TIMESTAMP = false;
public static String DATABASE_HOST;
public static String DATABASE_PORT;
public static String DATABASE_NAME;
public static String DATABASE_USERNAME;
public static String DATABASE_PASSWORD;
public static bool Load()
{
Program.Log.Info("Loading lobby_config.ini file");
if (!File.Exists("./lobby_config.ini"))
{
Program.Log.Error("FILE NOT FOUND!");
Program.Log.Error("Loading defaults...");
}
INIFile configIni = new INIFile("./lobby_config.ini");
ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1");
ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54994");
ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true");
ConfigConstants.DATABASE_HOST = configIni.GetValue("Database", "host", "");
ConfigConstants.DATABASE_PORT = configIni.GetValue("Database", "port", "");
ConfigConstants.DATABASE_NAME = configIni.GetValue("Database", "database", "");
ConfigConstants.DATABASE_USERNAME = configIni.GetValue("Database", "username", "");
ConfigConstants.DATABASE_PASSWORD = configIni.GetValue("Database", "password", "");
return true;
}
public static void ApplyLaunchArgs(string[] launchArgs)
{
var args = (from arg in launchArgs select arg.ToLower().Trim().TrimStart('-')).ToList();
for (var i = 0; i + 1 < args.Count; i += 2)
{
var arg = args[i];
var val = args[i + 1];
var legit = false;
if (arg == "ip")
{
IPAddress ip;
if (IPAddress.TryParse(val, out ip) && (legit = true))
OPTIONS_BINDIP = val;
}
else if (arg == "port")
{
UInt16 port;
if (UInt16.TryParse(val, out port) && (legit = true))
OPTIONS_PORT = val;
}
else if (arg == "user" && (legit = true))
{
DATABASE_USERNAME = val;
}
else if (arg == "p" && (legit = true))
{
DATABASE_PASSWORD = val;
}
else if (arg == "db" && (legit = true))
{
DATABASE_NAME = val;
}
else if (arg == "host" && (legit = true))
{
DATABASE_HOST = val;
}
if (!legit)
{
Program.Log.Error("Invalid parameter <{0}> for argument: <--{1}> or argument doesnt exist!", val, arg);
}
}
}
}
}

View File

@@ -0,0 +1,554 @@
using FFXIVClassic_Lobby_Server.dataobjects;
using MySql.Data.MySqlClient;
using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using FFXIVClassic_Lobby_Server.utils;
using FFXIVClassic.Common;
namespace FFXIVClassic_Lobby_Server
{
//charState: 0 - Reserved, 1 - Inactive, 2 - Active
class Database
{
public static uint GetUserIdFromSession(String sessionId)
{
uint id = 0;
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand("SELECT * FROM sessions WHERE id = @sessionId AND expiration > NOW()", conn);
cmd.Parameters.AddWithValue("@sessionId", sessionId);
using (MySqlDataReader Reader = cmd.ExecuteReader())
{
while (Reader.Read())
{
id = Reader.GetUInt32("userId");
}
}
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
}
return id;
}
public static bool ReserveCharacter(uint userId, uint slot, uint serverId, String name, out uint pid, out uint cid)
{
bool alreadyExists = false;
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
//Check if exists
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId AND state != 2 AND state != 1", conn);
cmd.Parameters.AddWithValue("@serverId", serverId);
cmd.Parameters.AddWithValue("@name", name);
using (MySqlDataReader Reader = cmd.ExecuteReader())
{
if (Reader.HasRows)
{
alreadyExists = true;
}
}
//Reserve
if (!alreadyExists)
{
MySqlCommand cmd2 = new MySqlCommand();
cmd2.Connection = conn;
cmd2.CommandText = "INSERT INTO characters(userId, slot, serverId, name, state) VALUES(@userId, @slot, @serverId, @name, 0)";
cmd2.Prepare();
cmd2.Parameters.AddWithValue("@userId", userId);
cmd2.Parameters.AddWithValue("@slot", slot);
cmd2.Parameters.AddWithValue("@serverId", serverId);
cmd2.Parameters.AddWithValue("@name", name);
cmd2.ExecuteNonQuery();
cid = (ushort)cmd2.LastInsertedId;
pid = 0xBABE;
}
else
{
pid = 0;
cid = 0;
}
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
Program.Log.Error(e.ToString());
pid = 0;
cid = 0;
}
finally
{
conn.Dispose();
}
Program.Log.Debug("[SQL] CID={0} Created on 'characters' table.", cid);
}
return alreadyExists;
}
public static void MakeCharacter(uint accountId, uint cid, CharaInfo charaInfo)
{
//Update character entry
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = @"
UPDATE characters SET
state=2,
currentZoneId=@zoneId,
positionX=@x,
positionY=@y,
positionZ=@z,
rotation=@r,
guardian=@guardian,
birthDay=@birthDay,
birthMonth=@birthMonth,
initialTown=@initialTown,
tribe=@tribe
WHERE userId=@userId AND id=@cid;
INSERT INTO characters_appearance
(characterId, baseId, size, voice, skinColor, hairStyle, hairColor, hairHighlightColor, hairVariation, eyeColor, faceType, faceEyebrows, faceEyeShape, faceIrisSize, faceNose, faceMouth, faceFeatures, ears, characteristics, characteristicsColor, mainhand, offhand, head, body, hands, legs, feet, waist)
VALUES
(@cid, 4294967295, @size, @voice, @skinColor, @hairStyle, @hairColor, @hairHighlightColor, @hairVariation, @eyeColor, @faceType, @faceEyebrows, @faceEyeShape, @faceIrisSize, @faceNose, @faceMouth, @faceFeatures, @ears, @characteristics, @characteristicsColor, @mainhand, @offhand, @head, @body, @hands, @legs, @feet, @waist)
";
cmd.Parameters.AddWithValue("@userId", accountId);
cmd.Parameters.AddWithValue("@cid", cid);
cmd.Parameters.AddWithValue("@guardian", charaInfo.guardian);
cmd.Parameters.AddWithValue("@birthDay", charaInfo.birthDay);
cmd.Parameters.AddWithValue("@birthMonth", charaInfo.birthMonth);
cmd.Parameters.AddWithValue("@initialTown", charaInfo.initialTown);
cmd.Parameters.AddWithValue("@tribe", charaInfo.tribe);
cmd.Parameters.AddWithValue("@zoneId", charaInfo.zoneId);
cmd.Parameters.AddWithValue("@x", charaInfo.x);
cmd.Parameters.AddWithValue("@y", charaInfo.y);
cmd.Parameters.AddWithValue("@z", charaInfo.z);
cmd.Parameters.AddWithValue("@r", charaInfo.rot);
cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size);
cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice);
cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor);
cmd.Parameters.AddWithValue("@hairStyle", charaInfo.appearance.hairStyle);
cmd.Parameters.AddWithValue("@hairColor", charaInfo.appearance.hairColor);
cmd.Parameters.AddWithValue("@hairHighlightColor", charaInfo.appearance.hairHighlightColor);
cmd.Parameters.AddWithValue("@hairVariation", charaInfo.appearance.hairVariation);
cmd.Parameters.AddWithValue("@eyeColor", charaInfo.appearance.eyeColor);
cmd.Parameters.AddWithValue("@faceType", charaInfo.appearance.faceType);
cmd.Parameters.AddWithValue("@faceEyebrows", charaInfo.appearance.faceEyebrows);
cmd.Parameters.AddWithValue("@faceEyeShape", charaInfo.appearance.faceEyeShape);
cmd.Parameters.AddWithValue("@faceIrisSize", charaInfo.appearance.faceIrisSize);
cmd.Parameters.AddWithValue("@faceNose", charaInfo.appearance.faceNose);
cmd.Parameters.AddWithValue("@faceMouth", charaInfo.appearance.faceMouth);
cmd.Parameters.AddWithValue("@faceFeatures", charaInfo.appearance.faceFeatures);
cmd.Parameters.AddWithValue("@ears", charaInfo.appearance.ears);
cmd.Parameters.AddWithValue("@characteristics", charaInfo.appearance.characteristics);
cmd.Parameters.AddWithValue("@characteristicsColor", charaInfo.appearance.characteristicsColor);
cmd.Parameters.AddWithValue("@mainhand", charaInfo.weapon1);
cmd.Parameters.AddWithValue("@offhand", charaInfo.weapon2);
cmd.Parameters.AddWithValue("@head", charaInfo.head);
cmd.Parameters.AddWithValue("@body", charaInfo.body);
cmd.Parameters.AddWithValue("@legs", charaInfo.legs);
cmd.Parameters.AddWithValue("@hands", charaInfo.hands);
cmd.Parameters.AddWithValue("@feet", charaInfo.feet);
cmd.Parameters.AddWithValue("@waist", charaInfo.belt);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
conn.Dispose();
return;
}
finally
{
}
//Create Level and EXP entries
try
{
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = String.Format("INSERT INTO characters_class_levels(characterId, {0}) VALUES(@characterId, 1); INSERT INTO characters_class_exp(characterId) VALUES(@characterId)", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass));
cmd.Prepare();
cmd.Parameters.AddWithValue("@characterId", cid);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
conn.Dispose();
return;
}
//Create Parameter Save
try
{
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = String.Format("INSERT INTO characters_parametersave(characterId, hp, hpMax, mp, mpMax, mainSkill, mainSkillLevel) VALUES(@characterId, 1, 1, 1, 1, @mainSkill, 1);", CharacterCreatorUtils.GetClassNameForId((short)charaInfo.currentClass));
cmd.Prepare();
cmd.Parameters.AddWithValue("@characterId", cid);
cmd.Parameters.AddWithValue("@mainSkill", charaInfo.currentClass);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
}
Program.Log.Debug("[SQL] CID={0} state updated to active(2).", cid);
}
public static bool RenameCharacter(uint userId, uint characterId, uint serverId, String newName)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
//Check if exists
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId", conn);
cmd.Parameters.AddWithValue("@serverId", serverId);
cmd.Parameters.AddWithValue("@name", newName);
using (MySqlDataReader Reader = cmd.ExecuteReader())
{
if (Reader.HasRows)
{
return true;
}
}
cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE characters SET name=@name, DoRename=0 WHERE id=@cid AND userId=@uid";
cmd.Prepare();
cmd.Parameters.AddWithValue("@uid", userId);
cmd.Parameters.AddWithValue("@cid", characterId);
cmd.Parameters.AddWithValue("@name", newName);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
Program.Log.Debug("[SQL] CID={0} name updated to \"{1}\".", characterId, newName);
return false;
}
}
public static void DeleteCharacter(uint characterId, String name)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE characters SET state=1 WHERE id=@cid AND name=@name";
cmd.Prepare();
cmd.Parameters.AddWithValue("@cid", characterId);
cmd.Parameters.AddWithValue("@name", name);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
}
Program.Log.Debug("[SQL] CID={0} deleted.", characterId);
}
public static List<World> GetServers()
{
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
List<World> worldList = null;
try
{
conn.Open();
worldList = conn.Query<World>("SELECT * FROM servers WHERE isActive=true").ToList();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
worldList = new List<World>(); }
finally
{
conn.Dispose();
}
return worldList;
}
}
public static World GetServer(uint serverId)
{
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
World world = null;
try
{
conn.Open();
world = conn.Query<World>("SELECT * FROM servers WHERE id=@ServerId", new {ServerId = serverId}).SingleOrDefault();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
return world;
}
}
public static List<Character> GetCharacters(uint userId)
{
List<Character> characters = new List<Character>();
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
conn.Open();
//Load basic info
string query = @"
SELECT
id,
slot,
serverId,
name,
isLegacy,
doRename,
currentZoneId,
guardian,
birthMonth,
birthDay,
initialTown,
tribe,
mainSkill,
mainSkillLevel
FROM characters
INNER JOIN characters_parametersave ON id = characters_parametersave.characterId
WHERE userId = @userId AND state = 2
ORDER BY slot";
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.Parameters.AddWithValue("@userId", userId);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Character chara = new Character();
chara.id = reader.GetUInt32("id");
chara.slot = reader.GetUInt16("slot");
chara.serverId = reader.GetUInt16("serverId");
chara.name = reader.GetString("name");
chara.isLegacy = reader.GetBoolean("isLegacy");
chara.doRename = reader.GetBoolean("doRename");
chara.currentZoneId = reader.GetUInt32("currentZoneId");
chara.guardian = reader.GetByte("guardian");
chara.birthMonth = reader.GetByte("birthMonth");
chara.birthDay = reader.GetByte("birthDay");
chara.initialTown = reader.GetByte("initialTown");
chara.tribe = reader.GetByte("tribe");
chara.currentClass = reader.GetByte("mainSkill");
//chara.currentJob = ???
chara.currentLevel = reader.GetInt16("mainSkillLevel");
characters.Add(chara);
}
}
}
return characters;
}
public static Character GetCharacter(uint userId, uint charId)
{
Character chara = null;
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
conn.Open();
string query = @"
SELECT
id,
slot,
serverId,
name,
isLegacy,
doRename,
currentZoneId,
guardian,
birthMonth,
birthDay,
initialTown,
tribe,
mainSkill,
mainSkillLevel
FROM characters
INNER JOIN characters_parametersave ON id = characters_parametersave.characterId
WHERE id = @charId";
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.Parameters.AddWithValue("@charId", charId);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
chara = new Character();
chara.id = reader.GetUInt32("id");
chara.slot = reader.GetUInt16("slot");
chara.serverId = reader.GetUInt16("serverId");
chara.name = reader.GetString("name");
chara.isLegacy = reader.GetBoolean("isLegacy");
chara.doRename = reader.GetBoolean("doRename");
chara.currentZoneId = reader.GetUInt32("currentZoneId");
chara.guardian = reader.GetByte("guardian");
chara.birthMonth = reader.GetByte("birthMonth");
chara.birthDay = reader.GetByte("birthDay");
chara.initialTown = reader.GetByte("initialTown");
chara.tribe = reader.GetByte("tribe");
chara.currentClass = reader.GetByte("mainSkill");
//chara.currentJob = ???
chara.currentLevel = reader.GetInt16("mainSkillLevel");
}
}
}
return chara;
}
public static Appearance GetAppearance(uint charaId)
{
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
Appearance appearance = null;
try
{
conn.Open();
appearance = conn.Query<Appearance>("SELECT * FROM characters_appearance WHERE characterId=@CharaId", new { CharaId = charaId }).SingleOrDefault();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
}
finally
{
conn.Dispose();
}
return appearance;
}
}
public static List<String> GetReservedNames(uint userId)
{
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
List<String> nameList = null;
try
{
conn.Open();
nameList = conn.Query<String>("SELECT name FROM reserved_names WHERE userId=@UserId", new { UserId = userId }).ToList();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
nameList = new List<String>(); }
finally
{
conn.Dispose();
}
return nameList;
}
}
public static List<Retainer> GetRetainers(uint userId)
{
using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
List<Retainer> retainerList = null;
try
{
conn.Open();
retainerList = conn.Query<Retainer>("SELECT * FROM retainers WHERE id=@UserId ORDER BY characterId, slot", new { UserId = userId }).ToList();
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
retainerList = new List<Retainer>(); }
finally
{
conn.Dispose();
}
return retainerList;
}
}
}
}

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{703091E0-F69C-4177-8FAE-C258AC6A65AA}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FFXIVClassic_Lobby_Server</RootNamespace>
<AssemblyName>FFXIVClassic_Lobby_Server</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<NuGetPackageImportStamp>cc1ba6f5</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Collections.Generic.CircularBuffer">
<HintPath>..\packages\Cyotek.CircularBuffer.1.0.0.0\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll</HintPath>
</Reference>
<Reference Include="Dapper">
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
</Reference>
<Reference Include="FFXIVClassic.Common">
<HintPath>..\FFXIVClassic Common Class Lib\bin\Debug\FFXIVClassic.Common.dll</HintPath>
</Reference>
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="dataobjects\Account.cs" />
<Compile Include="dataobjects\Appearance.cs" />
<Compile Include="dataobjects\CharaInfo.cs" />
<Compile Include="dataobjects\Retainer.cs" />
<Compile Include="dataobjects\Character.cs" />
<Compile Include="ClientConnection.cs" />
<Compile Include="ConfigConstants.cs" />
<Compile Include="Database.cs" />
<Compile Include="dataobjects\World.cs" />
<Compile Include="PacketProcessor.cs" />
<Compile Include="packets\receive\CharacterModifyPacket.cs" />
<Compile Include="packets\receive\SecurityHandshakePacket.cs" />
<Compile Include="packets\receive\SelectCharacterPacket.cs" />
<Compile Include="packets\receive\SessionPacket.cs" />
<Compile Include="packets\send\CharaCreatorPacket.cs" />
<Compile Include="packets\send\RetainerListPacket.cs" />
<Compile Include="packets\send\ErrorPacket.cs" />
<Compile Include="packets\HardCoded_Packets.cs" />
<Compile Include="packets\send\SelectCharacterConfirmPacket.cs" />
<Compile Include="packets\send\CharacterListPacket.cs" />
<Compile Include="packets\send\ImportListPacket.cs" />
<Compile Include="packets\send\AccountListPacket.cs" />
<Compile Include="packets\send\WorldListPacket.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server.cs" />
<Compile Include="utils\CharacterCreatorUtils.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="app.config" />
<None Include="NLog.xsd">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>xcopy "$(SolutionDir)data\lobby_config.ini" "$(SolutionDir)$(ProjectName)\$(OutDir)" /y</PostBuildEvent>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- optional, add some variabeles
https://github.com/nlog/NLog/wiki/Configuration-file#variables
-->
<variable name="myvar" value="myvalue" />
<!--
See https://github.com/nlog/nlog/wiki/Configuration-file
for information on customizing logging rules and outputs.
-->
<targets async="true">
<!--
add your targets here
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
-->
<!--
Write events to a file with the date in the filename.
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
<!--<target xsi:type="ColoredConsole" name="console" layout="[${longdate}] [${uppercase:${level}}] ${message}" />-->
<target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}/map.log"
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
<target xsi:type="ColoredConsole" name="console"
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
<target xsi:type="ColoredConsole" name="packets"
layout="${message}">
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.BasePacket') and equals('${event-context:item=color}', '6')"
backgroundColor="DarkYellow" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.SubPacket') and equals('${event-context:item=color}', '4')"
backgroundColor="DarkRed" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.SubPacket') and equals('${event-context:item=color}', '5')"
backgroundColor="DarkMagenta" foregroundColor="NoChange" />
</target>
</targets>
<rules>
<!-- add your logging rules here -->
<logger name='*' minlevel='Trace' writeTo='file' />
<logger name='FFXIVClassic_Lobby_Server.Program' minlevel='Trace' writeTo='console' />
<logger name='FFXIVClassic_Lobby_Server.packets.*' minlevel='Debug' writeTo='packets' />
<!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
<logger name="*" minlevel="Debug" writeTo="f" />
-->
</rules>
</nlog>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,390 @@
using FFXIVClassic.Common;
using FFXIVClassic_Lobby_Server.dataobjects;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Lobby_Server.packets.receive;
using FFXIVClassic_Lobby_Server.utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace FFXIVClassic_Lobby_Server
{
class PacketProcessor
{
public void ProcessPacket(ClientConnection client, BasePacket packet)
{
if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T')) //Test Ticket Data
{
packet.DebugPrintPacket();
//Crypto handshake
ProcessStartSession(client, packet);
return;
}
BasePacket.DecryptPacket(client.blowfish, ref packet);
packet.DebugPrintPacket();
List<SubPacket> subPackets = packet.GetSubpackets();
foreach (SubPacket subpacket in subPackets)
{
subpacket.DebugPrintSubPacket();
if (subpacket.header.type == 3)
{
switch (subpacket.gameMessage.opcode)
{
case 0x03:
ProcessGetCharacters(client, subpacket);
break;
case 0x04:
ProcessSelectCharacter(client, subpacket);
break;
case 0x05:
ProcessSessionAcknowledgement(client, subpacket);
break;
case 0x0B:
ProcessModifyCharacter(client, subpacket);
break;
case 0x0F:
//Mod Retainers
default:
Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode);
break;
}
}
}
}
private void ProcessStartSession(ClientConnection client, BasePacket packet)
{
SecurityHandshakePacket securityHandshake = new SecurityHandshakePacket(packet.data);
byte[] blowfishKey = GenerateKey(securityHandshake.ticketPhrase, securityHandshake.clientNumber);
client.blowfish = new Blowfish(blowfishKey);
Program.Log.Info("SecCNum: 0x{0:X}", securityHandshake.clientNumber);
//Respond with acknowledgment
BasePacket outgoingPacket = new BasePacket(HardCoded_Packets.g_secureConnectionAcknowledgment);
BasePacket.EncryptPacket(client.blowfish, outgoingPacket);
client.QueuePacket(outgoingPacket);
}
private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet)
{
packet.DebugPrintSubPacket();
SessionPacket sessionPacket = new SessionPacket(packet.data);
String clientVersion = sessionPacket.version;
Program.Log.Info("Got acknowledgment for secure session.");
Program.Log.Info("CLIENT VERSION: {0}", clientVersion);
uint userId = Database.GetUserIdFromSession(sessionPacket.session);
client.currentUserId = userId;
client.currentSessionToken = sessionPacket.session; ;
if (userId == 0)
{
ErrorPacket errorPacket = new ErrorPacket(sessionPacket.sequence, 0, 0, 13001, "Your session has expired, please login again.");
SubPacket subpacket = errorPacket.BuildPacket();
subpacket.SetTargetId(0xe0006868);
BasePacket errorBasePacket = BasePacket.CreatePacket(subpacket, true, false);
BasePacket.EncryptPacket(client.blowfish, errorBasePacket);
client.QueuePacket(errorBasePacket);
Program.Log.Info("Invalid session, kicking...");
return;
}
Program.Log.Info("USER ID: {0}", userId);
List<Account> accountList = new List<Account>();
Account defaultAccount = new Account();
defaultAccount.id = 1;
defaultAccount.name = "FINAL FANTASY XIV";
accountList.Add(defaultAccount);
AccountListPacket listPacket = new AccountListPacket(1, accountList);
BasePacket basePacket = BasePacket.CreatePacket(listPacket.BuildPackets(), true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
}
private void ProcessGetCharacters(ClientConnection client, SubPacket packet)
{
Program.Log.Info("{0} => Get characters", client.currentUserId == 0 ? client.GetAddress() : "User " + client.currentUserId);
SendWorldList(client, packet);
SendImportList(client, packet);
SendRetainerList(client, packet);
SendCharacterList(client, packet);
}
private void ProcessSelectCharacter(ClientConnection client, SubPacket packet)
{
SelectCharacterPacket selectCharRequest = new SelectCharacterPacket(packet.data);
Program.Log.Info("{0} => Select character id {1}", client.currentUserId == 0 ? client.GetAddress() : "User " + client.currentUserId, selectCharRequest.characterId);
Character chara = Database.GetCharacter(client.currentUserId, selectCharRequest.characterId);
World world = null;
if (chara != null)
world = Database.GetServer(chara.serverId);
if (world == null)
{
ErrorPacket errorPacket = new ErrorPacket(selectCharRequest.sequence, 0, 0, 13001, "World Does not exist or is inactive.");
SubPacket subpacket = errorPacket.BuildPacket();
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
return;
}
SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket(selectCharRequest.sequence, selectCharRequest.characterId, client.currentSessionToken, world.address, world.port, selectCharRequest.ticket);
BasePacket outgoingPacket = BasePacket.CreatePacket(connectCharacter.BuildPackets(), true, false);
BasePacket.EncryptPacket(client.blowfish, outgoingPacket);
client.QueuePacket(outgoingPacket);
}
private void ProcessModifyCharacter(ClientConnection client, SubPacket packet)
{
CharacterModifyPacket charaReq = new CharacterModifyPacket(packet.data);
var slot = charaReq.slot;
var name = charaReq.characterName;
var worldId = charaReq.worldId;
uint pid = 0, cid = 0;
//Get world from new char instance
if (worldId == 0)
worldId = client.newCharaWorldId;
//Check if this character exists, Get world from there
if (worldId == 0 && charaReq.characterId != 0)
{
Character chara = Database.GetCharacter(client.currentUserId, charaReq.characterId);
if (chara != null)
worldId = chara.serverId;
}
string worldName = null;
World world = Database.GetServer(worldId);
if (world != null)
worldName = world.name;
if (worldName == null)
{
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 0, 0, 13001, "World Does not exist or is inactive.");
SubPacket subpacket = errorPacket.BuildPacket();
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
Program.Log.Info("User {0} => Error; invalid server id: \"{1}\"", client.currentUserId, worldId);
return;
}
bool alreadyTaken;
switch (charaReq.command)
{
case 0x01://Reserve
alreadyTaken = Database.ReserveCharacter(client.currentUserId, slot, worldId, name, out pid, out cid);
if (alreadyTaken)
{
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
SubPacket subpacket = errorPacket.BuildPacket();
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
Program.Log.Info("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName);
return;
}
else
{
pid = 0;
client.newCharaCid = cid;
client.newCharaSlot = slot;
client.newCharaWorldId = worldId;
client.newCharaName = name;
}
Program.Log.Info("User {0} => Character reserved \"{1}\"", client.currentUserId, name);
break;
case 0x02://Make
CharaInfo info = CharaInfo.GetFromNewCharRequest(charaReq.characterInfoEncoded);
//Set Initial Appearance (items will be loaded in by map server)
uint[] classAppearance = CharacterCreatorUtils.GetEquipmentForClass(info.currentClass);
info.weapon1 = classAppearance[0];
info.weapon2 = classAppearance[1];
info.head = classAppearance[7];
if (classAppearance[8] != 0)
info.body = classAppearance[8];
else
info.body = CharacterCreatorUtils.GetUndershirtForTribe(info.tribe);
info.legs = classAppearance[9];
info.hands = classAppearance[10];
info.feet = classAppearance[11];
info.belt = classAppearance[12];
//Set Initial Position
switch (info.initialTown)
{
case 1: //ocn0Battle02 (Limsa)
info.zoneId = 193;
info.x = 0.016f;
info.y = 10.35f;
info.z = -36.91f;
info.rot = 0.025f;
break;
case 2: //fst0Battle03 (Gridania)
info.zoneId = 166;
info.x = 369.5434f;
info.y = 4.21f;
info.z = -706.1074f;
info.rot = -1.26721f;
break;
case 3: //wil0Battle01 (Ul'dah)
info.zoneId = 184;
info.x = 5.364327f;
info.y = 196.0f;
info.z = 133.6561f;
info.rot = -2.849384f;
break;
}
Database.MakeCharacter(client.currentUserId, client.newCharaCid, info);
pid = 1;
cid = client.newCharaCid;
name = client.newCharaName;
Program.Log.Info("User {0} => Character Created \"{1}\"", client.currentUserId, name);
break;
case 0x03://Rename
alreadyTaken = Database.RenameCharacter(client.currentUserId, charaReq.characterId, worldId, charaReq.characterName);
if (alreadyTaken)
{
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
SubPacket subpacket = errorPacket.BuildPacket();
BasePacket basePacket = BasePacket.CreatePacket(subpacket, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
Program.Log.Info("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName);
return;
}
Program.Log.Info("User {0} => Character renamed \"{1}\"", client.currentUserId, name);
break;
case 0x04://Delete
Database.DeleteCharacter(charaReq.characterId, charaReq.characterName);
Program.Log.Info("User {0} => Character deleted \"{1}\"", client.currentUserId, name);
break;
case 0x06://Rename Retainer
Program.Log.Info("User {0} => Retainer renamed \"{1}\"", client.currentUserId, name);
break;
}
CharaCreatorPacket charaCreator = new CharaCreatorPacket(charaReq.sequence, charaReq.command, pid, cid, 1, name, worldName);
BasePacket charaCreatorPacket = BasePacket.CreatePacket(charaCreator.BuildPacket(), true, false);
BasePacket.EncryptPacket(client.blowfish, charaCreatorPacket);
client.QueuePacket(charaCreatorPacket);
}
private void SendWorldList(ClientConnection client, SubPacket packet)
{
List<World> serverList = Database.GetServers();
WorldListPacket worldlistPacket = new WorldListPacket(0, serverList);
List<SubPacket> subPackets = worldlistPacket.BuildPackets();
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
}
private void SendImportList(ClientConnection client, SubPacket packet)
{
List<String> names = Database.GetReservedNames(client.currentUserId);
ImportListPacket importListPacket = new ImportListPacket(0, names);
List<SubPacket> subPackets = importListPacket.BuildPackets();
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
}
private void SendRetainerList(ClientConnection client, SubPacket packet)
{
List<Retainer> retainers = Database.GetRetainers(client.currentUserId);
RetainerListPacket retainerListPacket = new RetainerListPacket(0, retainers);
List<SubPacket> subPackets = retainerListPacket.BuildPackets();
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
}
private void SendCharacterList(ClientConnection client, SubPacket packet)
{
List<Character> characterList = Database.GetCharacters(client.currentUserId);
if (characterList.Count > 8)
Program.Log.Error("Warning, got more than 8 characters. List truncated, check DB for issues.");
CharacterListPacket characterlistPacket = new CharacterListPacket(0, characterList);
List<SubPacket> subPackets = characterlistPacket.BuildPackets();
BasePacket basePacket = BasePacket.CreatePacket(subPackets, true, false);
BasePacket.EncryptPacket(client.blowfish, basePacket);
client.QueuePacket(basePacket);
}
private byte[] GenerateKey(string ticketPhrase, uint clientNumber)
{
byte[] key;
using (MemoryStream memStream = new MemoryStream(0x2C))
{
using (BinaryWriter binWriter = new BinaryWriter(memStream))
{
binWriter.Write((Byte)0x78);
binWriter.Write((Byte)0x56);
binWriter.Write((Byte)0x34);
binWriter.Write((Byte)0x12);
binWriter.Write((UInt32)clientNumber);
binWriter.Write((Byte)0xE8);
binWriter.Write((Byte)0x03);
binWriter.Write((Byte)0x00);
binWriter.Write((Byte)0x00);
binWriter.Write(Encoding.ASCII.GetBytes(ticketPhrase), 0, Encoding.ASCII.GetByteCount(ticketPhrase) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(ticketPhrase));
}
byte[] nonMD5edKey = memStream.GetBuffer();
using (MD5 md5Hash = MD5.Create())
{
key = md5Hash.ComputeHash(nonMD5edKey);
}
}
return key;
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Diagnostics;
using System.Threading;
using MySql.Data.MySqlClient;
using System.Reflection;
using FFXIVClassic.Common;
using NLog;
namespace FFXIVClassic_Lobby_Server
{
class Program
{
public static Logger Log;
static void Main(string[] args)
{
// set up logging
Log = LogManager.GetCurrentClassLogger();
#if DEBUG
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter);
#endif
Log.Info("==================================");
Log.Info("FFXIV Classic Lobby Server");
Log.Info("Version: 0.1");
Log.Info("==================================");
bool startServer = true;
//Load Config
ConfigConstants.Load();
ConfigConstants.ApplyLaunchArgs(args);
//Test DB Connection
Program.Log.Info("Testing DB connection to \"{0}\"... ", ConfigConstants.DATABASE_HOST);
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
conn.Close();
Program.Log.Info("Connection ok.");
}
catch (MySqlException e)
{
Program.Log.Error(e.ToString());
startServer = false;
}
}
//Start Server if A-OK
if (startServer)
{
Server server = new Server();
server.StartServer();
while (true) Thread.Sleep(10000);
}
Program.Log.Info("Press any key to continue...");
Console.ReadKey();
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FFXIVClassic_Lobby_Server")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FFXIVClassic_Lobby_Server")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("9b87f719-1e4d-4781-89c9-28d99f579253")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,244 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using FFXIVClassic.Common;
using NLog;
namespace FFXIVClassic_Lobby_Server
{
class Server
{
public const int FFXIV_LOBBY_PORT = 54994;
public const int BUFFER_SIZE = 0xFFFF;
public const int BACKLOG = 100;
private const int CLEANUP_THREAD_SLEEP_TIME = 60;
private Socket mServerSocket;
private List<ClientConnection> mConnectionList = new List<ClientConnection>();
private PacketProcessor mProcessor;
private Thread cleanupThread;
private bool killCleanupThread = false;
private void SocketCleanup()
{
Program.Log.Info("Cleanup thread started; it will run every {0} seconds.", CLEANUP_THREAD_SLEEP_TIME);
while (!killCleanupThread)
{
int count = 0;
for (int i = mConnectionList.Count - 1; i >= 0; i--)
{
ClientConnection conn = mConnectionList[i];
if (conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)
{
conn.socket.Disconnect(false);
mConnectionList.Remove(conn);
count++;
}
}
if (count != 0)
Program.Log.Info("{0} connections were cleaned up.", count);
Thread.Sleep(CLEANUP_THREAD_SLEEP_TIME*1000);
}
}
#region Socket Handling
public bool StartServer()
{
//cleanupThread = new Thread(new ThreadStart(socketCleanup));
//cleanupThread.Name = "LobbyThread:Cleanup";
//cleanupThread.Start();
IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), int.Parse(ConfigConstants.OPTIONS_PORT));
try{
mServerSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
catch (Exception e)
{
throw new ApplicationException("Could not Create socket, check to make sure not duplicating port", e);
}
try
{
mServerSocket.Bind(serverEndPoint);
mServerSocket.Listen(BACKLOG);
}
catch (Exception e)
{
throw new ApplicationException("Error occured while binding socket, check inner exception", e);
}
try
{
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception e)
{
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
}
Console.ForegroundColor = ConsoleColor.White;
Program.Log.Info("Lobby Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Console.ForegroundColor = ConsoleColor.Gray;
mProcessor = new PacketProcessor();
return true;
}
private void AcceptCallback(IAsyncResult result)
{
ClientConnection conn = null;
try
{
System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
conn = new ClientConnection();
conn.socket = s.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList)
{
mConnectionList.Add(conn);
}
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
}
catch (SocketException)
{
if (conn.socket != null)
{
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn.socket != null)
{
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
}
private void ReceiveCallback(IAsyncResult result)
{
ClientConnection conn = (ClientConnection)result.AsyncState;
try
{
int bytesRead = conn.socket.EndReceive(result);
bytesRead += conn.lastPartialSize;
if (bytesRead > 0)
{
int offset = 0;
//Build packets until can no longer or out of data
while (true)
{
BasePacket basePacket = BuildPacket(ref offset, conn.buffer, bytesRead);
//If can't build packet, break, else process another
if (basePacket == null)
break;
else
mProcessor.ProcessPacket(conn, basePacket);
}
//Not all bytes consumed, transfer leftover to beginning
if (offset < bytesRead)
Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset);
Array.Clear(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset));
conn.lastPartialSize = bytesRead - offset;
//Build any queued subpackets into basepackets and send
conn.FlushQueuedSendPackets();
if (offset < bytesRead)
//Need offset since not all bytes consumed
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
else
//All bytes consumed, full buffer available
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
}
else
{
Program.Log.Info("{0} has disconnected.", conn.currentUserId == 0 ? conn.GetAddress() : "User " + conn.currentUserId);
lock (mConnectionList)
{
conn.Disconnect();
mConnectionList.Remove(conn);
}
}
}
catch (SocketException)
{
if (conn.socket != null)
{
Program.Log.Info("{0} has disconnected.", conn.currentUserId == 0 ? conn.GetAddress() : "User " + conn.currentUserId);
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
}
}
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public BasePacket BuildPacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
#endregion
}
}

View File

@@ -3,7 +3,7 @@
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
</configuration>

View File

@@ -0,0 +1,10 @@
using System;
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class Account
{
public UInt32 id;
public string name;
}
}

View File

@@ -0,0 +1,44 @@
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class Appearance
{
////////////
//Chara Info
public byte size = 0;
public byte voice = 0;
public ushort skinColor = 0;
public ushort hairStyle = 0;
public ushort hairColor = 0;
public ushort hairHighlightColor = 0;
public ushort hairVariation = 0;
public ushort eyeColor = 0;
public byte characteristicsColor = 0;
public byte faceType = 0;
public byte faceEyebrows = 0;
public byte faceEyeShape = 0;
public byte faceIrisSize = 0;
public byte faceNose = 0;
public byte faceMouth = 0;
public byte faceFeatures = 0;
public byte characteristics = 0;
public byte ears = 0;
public uint mainHand = 0;
public uint offHand = 0;
public uint head = 0;
public uint body = 0;
public uint legs = 0;
public uint hands = 0;
public uint feet = 0;
public uint waist = 0;
public uint rightEar = 0;
public uint leftEar = 0;
public uint rightFinger = 0;
public uint leftFinger = 0;
//Chara Info
////////////
}
}

View File

@@ -1,25 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic.Common;
using System.IO;
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class CharaInfo
{
public uint tribe = 0;
public uint size = 0;
public uint voice = 0;
public ushort skinColor = 0;
public ushort hairStyle = 0;
public ushort hairColor = 0;
public ushort hairHighlightColor = 0;
public ushort eyeColor = 0;
public ushort characteristicsColor = 0;
public Appearance appearance;
public struct FaceInfo
{
@@ -47,43 +34,34 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
public uint unknown;
}
public uint faceType = 0;
public uint faceEyebrows = 0;
public uint faceEyeShape = 0;
public uint faceIrisSize = 0;
public uint faceNose = 0;
public uint faceMouth = 0;
public uint faceFeatures = 0;
public uint characteristics = 0;
public uint ears = 0;
public uint guardian = 0;
public uint birthMonth = 0;
public uint birthDay = 0;
public uint currentClass = 0;
public uint currentJob = 0;
public uint allegiance = 0;
public uint initialTown = 0;
public uint tribe = 0;
public uint mainHand = 0;
public uint offHand = 0;
public uint headGear = 0;
public uint bodyGear = 0;
public uint legsGear = 0;
public uint handsGear = 0;
public uint feetGear = 0;
public uint waistGear = 0;
public uint rightEarGear = 0;
public uint leftEarGear = 0;
public uint rightFingerGear = 0;
public uint leftFingerGear = 0;
public ushort zoneId;
public float x, y, z, rot;
public uint currentLevel = 1;
public static CharaInfo getFromNewCharRequest(String encoded)
public uint weapon1;
public uint weapon2;
public uint head;
public uint body;
public uint hands;
public uint legs;
public uint feet;
public uint belt;
public static CharaInfo GetFromNewCharRequest(String encoded)
{
byte[] data = Convert.FromBase64String(encoded.Replace('-', '+').Replace('_', '/'));
CharaInfo info = new CharaInfo();
Appearance appearance = new Appearance();
using (MemoryStream stream = new MemoryStream(data))
{
@@ -92,30 +70,31 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
uint version = reader.ReadUInt32();
uint unknown1 = reader.ReadUInt32();
info.tribe = reader.ReadByte();
info.size = reader.ReadByte();
info.hairStyle = reader.ReadUInt16();
info.hairHighlightColor = reader.ReadUInt16();
info.faceType = reader.ReadByte();
info.characteristics = reader.ReadByte();
info.characteristicsColor = reader.ReadByte();
appearance.size = reader.ReadByte();
appearance.hairStyle = reader.ReadUInt16();
appearance.hairHighlightColor = reader.ReadByte();
appearance.hairVariation = reader.ReadByte();
appearance.faceType = reader.ReadByte();
appearance.characteristics = reader.ReadByte();
appearance.characteristicsColor = reader.ReadByte();
reader.ReadUInt32();
info.faceEyebrows = reader.ReadByte();
info.faceIrisSize = reader.ReadByte();
info.faceEyeShape = reader.ReadByte();
info.faceNose = reader.ReadByte();
info.faceFeatures = reader.ReadByte();
info.faceMouth = reader.ReadByte();
info.ears = reader.ReadByte();
info.hairColor = reader.ReadUInt16();
appearance.faceEyebrows = reader.ReadByte();
appearance.faceIrisSize = reader.ReadByte();
appearance.faceEyeShape = reader.ReadByte();
appearance.faceNose = reader.ReadByte();
appearance.faceFeatures = reader.ReadByte();
appearance.faceMouth = reader.ReadByte();
appearance.ears = reader.ReadByte();
appearance.hairColor = reader.ReadUInt16();
reader.ReadUInt32();
info.skinColor = reader.ReadUInt16();
info.eyeColor = reader.ReadUInt16();
appearance.skinColor = reader.ReadUInt16();
appearance.eyeColor = reader.ReadUInt16();
info.voice = reader.ReadByte();
appearance.voice = reader.ReadByte();
info.guardian = reader.ReadByte();
info.birthMonth = reader.ReadByte();
info.birthDay = reader.ReadByte();
@@ -127,45 +106,36 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
reader.BaseStream.Seek(0x10, SeekOrigin.Current);
info.allegiance = reader.ReadByte();
info.initialTown = reader.ReadByte();
}
}
info.appearance = appearance;
return info;
}
public String buildForCharaList(DBCharacter chara)
public static String BuildForCharaList(Character chara, Appearance appearance)
{
byte[] data;
mainHand = 79707136;
offHand = 32509954;
headGear = 43008;
bodyGear = 43008;
legsGear = 43008;
handsGear = 43008;
feetGear = 43008;
using (MemoryStream stream = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
//Build faceinfo for later
FaceInfo faceInfo = new FaceInfo();
faceInfo.characteristics = characteristics;
faceInfo.characteristicsColor = characteristicsColor;
faceInfo.type = faceType;
faceInfo.ears = ears;
faceInfo.features = faceFeatures;
faceInfo.eyebrows = faceEyebrows;
faceInfo.eyeShape = faceEyeShape;
faceInfo.irisSize = faceIrisSize;
faceInfo.mouth = faceMouth;
faceInfo.nose = faceNose;
faceInfo.characteristics = appearance.characteristics;
faceInfo.characteristicsColor = appearance.characteristicsColor;
faceInfo.type = appearance.faceType;
faceInfo.ears = appearance.ears;
faceInfo.features = appearance.faceFeatures;
faceInfo.eyebrows = appearance.faceEyebrows;
faceInfo.eyeShape = appearance.faceEyeShape;
faceInfo.irisSize = appearance.faceIrisSize;
faceInfo.mouth = appearance.faceMouth;
faceInfo.nose = appearance.faceNose;
string location1 = "prv0Inn01\0";
string location2 = "defaultTerritory\0";
@@ -176,19 +146,19 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name + '\0'));
writer.Write((UInt32)0x1c);
writer.Write((UInt32)0x04);
writer.Write((UInt32)getTribeModel());
writer.Write((UInt32)size);
uint colorVal = skinColor | (uint)(hairColor << 10) | (uint)(eyeColor << 20);
writer.Write((UInt32)GetTribeModel(chara.tribe));
writer.Write((UInt32)appearance.size);
uint colorVal = appearance.skinColor | (uint)(appearance.hairColor << 10) | (uint)(appearance.eyeColor << 20);
writer.Write((UInt32)colorVal);
var bitfield = PrimitiveConversion.ToUInt32(faceInfo);
writer.Write((UInt32)bitfield); //FACE, Figure this out!
uint hairVal = hairHighlightColor | (uint)(hairStyle << 10) | (uint)(characteristicsColor << 20);
uint hairVal = appearance.hairHighlightColor | (uint)(appearance.hairStyle << 10) | (uint)(appearance.characteristicsColor << 20);
writer.Write((UInt32)hairVal);
writer.Write((UInt32)voice);
writer.Write((UInt32)mainHand);
writer.Write((UInt32)offHand);
writer.Write((UInt32)appearance.voice);
writer.Write((UInt32)appearance.mainHand);
writer.Write((UInt32)appearance.offHand);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
@@ -196,23 +166,23 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)headGear);
writer.Write((UInt32)bodyGear);
writer.Write((UInt32)legsGear);
writer.Write((UInt32)handsGear);
writer.Write((UInt32)feetGear);
writer.Write((UInt32)waistGear);
writer.Write((UInt32)appearance.head);
writer.Write((UInt32)appearance.body);
writer.Write((UInt32)appearance.legs);
writer.Write((UInt32)appearance.hands);
writer.Write((UInt32)appearance.feet);
writer.Write((UInt32)appearance.waist);
writer.Write((UInt32)0);
writer.Write((UInt32)rightEarGear);
writer.Write((UInt32)leftEarGear);
writer.Write((UInt32)appearance.rightEar);
writer.Write((UInt32)appearance.leftEar);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)rightFingerGear);
writer.Write((UInt32)leftFingerGear);
writer.Write((UInt32)appearance.rightFinger);
writer.Write((UInt32)appearance.leftFinger);
for (int i = 0; i < 0x8; i++)
writer.Write((byte)0);
@@ -220,11 +190,11 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
writer.Write((UInt32)1);
writer.Write((UInt32)1);
writer.Write((byte)currentClass);
writer.Write((UInt16)currentLevel);
writer.Write((byte)currentJob);
writer.Write((byte)chara.currentClass);
writer.Write((UInt16)chara.currentLevel);
writer.Write((byte)chara.currentJob);
writer.Write((UInt16)1);
writer.Write((byte)tribe);
writer.Write((byte)chara.tribe);
writer.Write((UInt32)0xe22222aa);
@@ -233,9 +203,9 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(location2).Length);
writer.Write(System.Text.Encoding.UTF8.GetBytes(location2));
writer.Write((byte)guardian);
writer.Write((byte)birthMonth);
writer.Write((byte)birthDay);
writer.Write((byte)chara.guardian);
writer.Write((byte)chara.birthMonth);
writer.Write((byte)chara.birthDay);
writer.Write((UInt16)0x17);
writer.Write((UInt32)4);
@@ -243,8 +213,8 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
writer.BaseStream.Seek(0x10, SeekOrigin.Current);
writer.Write((UInt32)allegiance);
writer.Write((UInt32)allegiance);
writer.Write((UInt32)chara.initialTown);
writer.Write((UInt32)chara.initialTown);
}
data = stream.GetBuffer();
@@ -253,16 +223,16 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
}
public static String debug()
public static String Debug()
{
byte[] bytes = File.ReadAllBytes("./packets/charaInfo.bin");
byte[] bytes = File.ReadAllBytes("./packets/charaappearance.bin");
Console.WriteLine(Utils.ByteArrayToHex(bytes));
Program.Log.Debug(Utils.ByteArrayToHex(bytes));
return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_');
}
public UInt32 getTribeModel()
public static UInt32 GetTribeModel(byte tribe)
{
switch (tribe)
{

View File

@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic.Common;
using FFXIVClassic_Lobby_Server.dataobjects;
namespace FFXIVClassic_Lobby_Server
{
class DBCharacter
class Character
{
public uint id;
public ushort slot;
@@ -20,15 +16,26 @@ namespace FFXIVClassic_Lobby_Server
public bool doRename;
public uint currentZoneId;
public byte guardian;
public byte birthMonth;
public byte birthDay;
public uint currentClass = 3;
public uint currentJob = 0;
public int currentLevel = 1;
public byte initialTown;
public byte tribe;
public static CharaInfo EncodedToCharacter(String charaInfo)
{
charaInfo.Replace("+", "-");
charaInfo.Replace("/", "_");
byte[] data = System.Convert.FromBase64String(charaInfo);
Console.WriteLine("------------Base64 printout------------------");
Console.WriteLine(Utils.ByteArrayToHex(data));
Console.WriteLine("------------Base64 printout------------------");
Program.Log.Debug("------------Base64 printout------------------");
Program.Log.Debug(Utils.ByteArrayToHex(data));
Program.Log.Debug("------------Base64 printout------------------");
CharaInfo chara = new CharaInfo();

View File

@@ -0,0 +1,11 @@
namespace FFXIVClassic_Lobby_Server
{
class Retainer
{
public uint id;
public uint characterId;
public string name;
public ushort slot;
public bool doRename;
}
}

View File

@@ -0,0 +1,13 @@
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class World
{
public ushort id;
public string address;
public ushort port;
public ushort listPosition;
public ushort population;
public string name;
public bool isActive;
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
<package id="Dapper" version="1.42" targetFramework="net45" />
<package id="Microsoft.Net.Compilers" version="2.0.0-beta3" targetFramework="net45" developmentDependency="true" />
<package id="MySql.Data" version="6.9.8" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
<package id="NLog" version="4.3.4" targetFramework="net45" />
<package id="NLog.Config" version="4.3.4" targetFramework="net45" />
<package id="NLog.Schema" version="4.3.4" targetFramework="net45" />
</packages>

View File

@@ -0,0 +1,52 @@
namespace FFXIVClassic_Lobby_Server.packets
{
class HardCoded_Packets
{
public static byte[] g_secureConnectionAcknowledgment =
{
0x00, 0x00, 0x00, 0x00, 0xA0, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x90, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xED, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x10, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x42, 0x82, 0x63, 0x52, 0x01, 0x00, 0x00, 0x00, 0x10, 0xEF, 0xDF, 0xFF, 0x53, 0x61, 0x6D, 0x70,
0x6C, 0x65, 0x20, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x52, 0x75, 0x6E, 0x52, 0x75, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0xF7, 0xAF, 0xAF, 0xF7, 0x00, 0x00, 0xB8, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2C, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x4F, 0x62,
0x6A, 0x65, 0x63, 0x74, 0x2E, 0x2E, 0x2E, 0x5B, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39,
0x39, 0x2E, 0x38, 0x32, 0x3A, 0x36, 0x33, 0x34, 0x30, 0x37, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x6C, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
0x32, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xAF, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xFE, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x00, 0x01, 0xCC, 0xCC, 0x0C, 0x69, 0x00, 0xE0, 0xD0, 0x58, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
0xF0, 0xEE, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x34, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x18, 0xBE, 0x34, 0x01, 0x00, 0x00, 0x00, 0x00,
0xD8, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0xF7, 0xAF, 0x42, 0x82, 0x63, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0x00,
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0xFF,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x24, 0xCF, 0x76, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x70, 0x7A, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD1, 0xF3, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xE8, 0x3E, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0x70, 0x99, 0xAA, 0x01, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x58, 0x59, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0xE0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x05, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x69, 0x00, 0xE0, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF0, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x23, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x5A, 0x33, 0x02, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
};
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets.receive
{
class CharacterModifyPacket
{
public UInt64 sequence;
public uint characterId;
public uint personType;
public byte slot;
public byte command;
public ushort worldId;
public String characterName;
public String characterInfoEncoded;
public bool invalidPacket = false;
public CharacterModifyPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
sequence = binReader.ReadUInt64();
characterId = binReader.ReadUInt32();
personType = binReader.ReadUInt32();
slot = binReader.ReadByte();
command = binReader.ReadByte();
worldId = binReader.ReadUInt16();
characterName = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
characterInfoEncoded = Encoding.ASCII.GetString(binReader.ReadBytes(0x190)).Trim(new[] { '\0' });
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets.receive
{
class SecurityHandshakePacket
{
public string ticketPhrase;
public uint clientNumber;
public bool invalidPacket = false;
public SecurityHandshakePacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
binReader.BaseStream.Seek(0x34, SeekOrigin.Begin);
ticketPhrase = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
clientNumber = binReader.ReadUInt32();
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.IO;
namespace FFXIVClassic_Lobby_Server.packets.receive
{
class SelectCharacterPacket
{
public UInt64 sequence;
public uint characterId;
public uint unknownId;
public UInt64 ticket;
public bool invalidPacket = false;
public SelectCharacterPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
sequence = binReader.ReadUInt64();
characterId = binReader.ReadUInt32();
unknownId = binReader.ReadUInt32();
ticket = binReader.ReadUInt64();
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets.receive
{
class SessionPacket
{
public bool invalidPacket = false;
public UInt64 sequence;
public String session;
public String version;
public SessionPacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
sequence = binReader.ReadUInt64();
binReader.ReadUInt32();
binReader.ReadUInt32();
session = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
version = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
}
catch (Exception){
invalidPacket = true;
}
}
}
}
}
}

View File

@@ -0,0 +1,100 @@
using FFXIVClassic.Common;
using FFXIVClassic_Lobby_Server.dataobjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class AccountListPacket
{
public const ushort OPCODE = 0x0C;
public const ushort MAXPERPACKET = 8;
private UInt64 sequence;
private List<Account> accountList;
public AccountListPacket(UInt64 sequence, List<Account> accountList)
{
this.sequence = sequence;
this.accountList = accountList;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int accountCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (Account account in accountList)
{
if (totalCount == 0 || accountCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x280);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (UInt32)(accountList.Count - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
//Write Entries
binWriter.Write((UInt32)account.id);
binWriter.Write((UInt32)0);
binWriter.Write(Encoding.ASCII.GetBytes(account.name.PadRight(0x40, '\0')));
accountCount++;
totalCount++;
//Send this chunk of world list
if (accountCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
accountCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (accountCount > 0 || accountList.Count == 0)
{
if (accountList.Count == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@@ -0,0 +1,65 @@
using FFXIVClassic.Common;
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class CharaCreatorPacket
{
public const ushort OPCODE = 0x0E;
public const ushort RESERVE = 0x01;
public const ushort MAKE = 0x02;
public const ushort RENAME = 0x03;
public const ushort DELETE = 0x04;
public const ushort RENAME_RETAINER = 0x06;
private UInt64 sequence;
private ushort command;
private uint pid;
private uint cid;
private uint type;
private uint ticket;
private string charaName;
private string worldName;
public CharaCreatorPacket(UInt64 sequence, ushort command, uint pid, uint cid, uint ticket, string charaName, string worldName)
{
this.sequence = sequence;
this.command = command;
this.pid = pid;
this.cid = cid;
this.type = 0x400017;
this.ticket = ticket;
this.charaName = charaName;
this.worldName = worldName;
}
public SubPacket BuildPacket()
{
MemoryStream memStream = new MemoryStream(0x1F0);
BinaryWriter binWriter = new BinaryWriter(memStream);
binWriter.Write((UInt64)sequence);
binWriter.Write((byte)1);
binWriter.Write((byte)1);
binWriter.Write((UInt16)command);
binWriter.Write((UInt32)0);
binWriter.Write((UInt32)pid); //PID
binWriter.Write((UInt32)cid); //CID
binWriter.Write((UInt32)type); //Type?
binWriter.Write((UInt32)ticket); //Ticket
binWriter.Write(Encoding.ASCII.GetBytes(charaName.PadRight(0x20, '\0'))); //Name
binWriter.Write(Encoding.ASCII.GetBytes(worldName.PadRight(0x20, '\0'))); //World Name
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
return new SubPacket(OPCODE, 0xe0006868, data);
}
}
}

View File

@@ -0,0 +1,158 @@
using FFXIVClassic.Common;
using FFXIVClassic_Lobby_Server.dataobjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class CharacterListPacket
{
public const ushort OPCODE = 0x0D;
public const ushort MAXPERPACKET = 2;
private ulong sequence;
private List<Character> characterList;
public CharacterListPacket(ulong sequence, List<Character> characterList)
{
this.sequence = sequence;
this.characterList = characterList;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int numCharacters = characterList.Count >= 8 ? 8 : characterList.Count + 1;
int characterCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (Character chara in characterList)
{
Appearance appearance = Database.GetAppearance(chara.id);
if (totalCount == 0 || characterCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x3B0);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
//Write Entries
World world = Database.GetServer(chara.serverId);
string worldname = world == null ? "Unknown" : world.name;
binWriter.Write((uint)0); //???
binWriter.Write((uint)chara.id); //Character Id
binWriter.Write((byte)(totalCount)); //Slot
byte options = 0;
if (chara.state == 1)
options |= 0x01;
if (chara.doRename)
options |= 0x02;
if (chara.isLegacy)
options |= 0x08;
binWriter.Write((byte)options); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
binWriter.Write((ushort)0);
binWriter.Write((uint)chara.currentZoneId); //Logged out zone
binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name
binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name
binWriter.Write(CharaInfo.BuildForCharaList(chara, appearance)); //Appearance Data
//binWriter.Write(CharaInfo.Debug()); //Appearance Data
characterCount++;
totalCount++;
//Send this chunk of character list
if (characterCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
characterCount = 0;
}
//Incase DB came back with more than max
if (totalCount >= 8)
break;
}
//Add a 'NEW' slot if there is space
if (characterList.Count < 8)
{
if (characterCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x3B0);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters-totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
//Write Entries
binWriter.Write((uint)0); //???
binWriter.Write((uint)0); //Character Id
binWriter.Write((byte)(totalCount)); //Slot
binWriter.Write((byte)0); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
binWriter.Write((ushort)0);
binWriter.Write((uint)0); //Logged out zone
characterCount++;
totalCount++;
//Send this chunk of character list
if (characterCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
characterCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (characterCount > 0 || numCharacters == 0)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@@ -0,0 +1,46 @@
using FFXIVClassic.Common;
using System;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class ErrorPacket
{
private const ushort OPCODE = 0x02;
private UInt64 sequence;
private uint errorCode;
private uint statusCode;
private uint textId;
private string message;
public ErrorPacket(UInt64 sequence, uint errorCode, uint statusCode, uint textId, string message)
{
this.sequence = sequence;
this.errorCode = errorCode;
this.statusCode = statusCode;
this.textId = textId;
this.message = message;
}
public SubPacket BuildPacket()
{
MemoryStream memStream = new MemoryStream(0x210);
BinaryWriter binWriter = new BinaryWriter(memStream);
binWriter.Write(sequence);
binWriter.Write(errorCode);
binWriter.Write(statusCode);
binWriter.Write(textId);
binWriter.Write(Encoding.ASCII.GetBytes(message));
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
return subpacket;
}
}
}

View File

@@ -0,0 +1,103 @@
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class ImportListPacket
{
public const ushort OPCODE = 0x16;
public const ushort MAXPERPACKET = 12;
private UInt64 sequence;
private List<String> namesList;
public ImportListPacket(UInt64 sequence, List<String> names)
{
this.sequence = sequence;
this.namesList = names;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int namesCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (String name in namesList)
{
if (totalCount == 0 || namesCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (UInt32)(namesList.Count - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
//Write Entries
binWriter.Write((uint)0);
binWriter.Write((uint)totalCount);
if (!name.Contains(" "))
binWriter.Write(Encoding.ASCII.GetBytes((name+" Last").PadRight(0x20, '\0')));
else
binWriter.Write(Encoding.ASCII.GetBytes(name.PadRight(0x20, '\0')));
namesCount++;
totalCount++;
//Send this chunk of world list
if (namesCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
namesCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (namesCount > 0 || namesList.Count == 0)
{
if (namesList.Count == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@@ -0,0 +1,104 @@
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class RetainerListPacket
{
public const ushort OPCODE = 0x17;
public const ushort MAXPERPACKET = 9;
private UInt64 sequence;
private List<Retainer> retainerList;
public RetainerListPacket(UInt64 sequence, List<Retainer> retainerList)
{
this.sequence = sequence;
this.retainerList = retainerList;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int retainerCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (Retainer retainer in retainerList)
{
if (totalCount == 0 || retainerCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (UInt32)(retainerList.Count - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
binWriter.Write((UInt64)0);
binWriter.Write((UInt32)0);
}
//Write Entries
binWriter.Write((uint)retainer.id);
binWriter.Write((uint)retainer.characterId);
binWriter.Write((ushort)retainer.slot);
binWriter.Write((ushort)(retainer.doRename ? 0x04 : 0x00));
binWriter.Write((uint)0);
binWriter.Write(Encoding.ASCII.GetBytes(retainer.name.PadRight(0x20, '\0')));
retainerCount++;
totalCount++;
//Send this chunk of character list
if (retainerCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
retainerCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (retainerCount > 0 || retainerList.Count == 0)
{
if (retainerList.Count == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@@ -0,0 +1,59 @@
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class SelectCharacterConfirmPacket
{
public const ushort OPCODE = 0x0F;
private UInt64 sequence;
private UInt32 characterId;
private string sessionToken;
private string worldIp;
private UInt16 worldPort;
private UInt64 selectCharTicket;
public SelectCharacterConfirmPacket(UInt64 sequence, UInt32 characterId, string sessionToken, string worldIp, ushort worldPort, UInt64 selectCharTicket)
{
this.sequence = sequence;
this.characterId = characterId;
this.sessionToken = sessionToken;
this.worldIp = worldIp;
this.worldPort = worldPort;
this.selectCharTicket = selectCharTicket;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
byte[] data;
using (MemoryStream memStream = new MemoryStream(0x98))
{
using (BinaryWriter binWriter = new BinaryWriter(memStream))
{
binWriter.Write((UInt64)sequence);
binWriter.Write((UInt32)characterId); //ActorId
binWriter.Write((UInt32)characterId); //CharacterId
binWriter.Write((UInt32)0);
binWriter.Write(Encoding.ASCII.GetBytes(sessionToken.PadRight(0x42, '\0'))); //Session Token
binWriter.Write((UInt16)worldPort); //World Port
binWriter.Write(Encoding.ASCII.GetBytes(worldIp.PadRight(0x38, '\0'))); //World Hostname/IP
binWriter.Write((UInt64)selectCharTicket); //Ticket or Handshake of somekind
}
data = memStream.GetBuffer();
}
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
return subPackets;
}
}
}

View File

@@ -0,0 +1,102 @@
using FFXIVClassic.Common;
using FFXIVClassic_Lobby_Server.dataobjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace FFXIVClassic_Lobby_Server.packets
{
class WorldListPacket
{
public const ushort OPCODE = 0x15;
public const ushort MAXPERPACKET = 6;
private UInt64 sequence;
private List<World> worldList;
public WorldListPacket(UInt64 sequence, List<World> serverList)
{
this.sequence = sequence;
this.worldList = serverList;
}
public List<SubPacket> BuildPackets()
{
List<SubPacket> subPackets = new List<SubPacket>();
int serverCount = 0;
int totalCount = 0;
MemoryStream memStream = null;
BinaryWriter binWriter = null;
foreach (World world in worldList)
{
if (totalCount == 0 || serverCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (UInt32)(worldList.Count - totalCount) : (UInt32)MAXPERPACKET);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
//Write Entries
binWriter.Write((ushort)world.id);
binWriter.Write((ushort)world.listPosition);
binWriter.Write((uint)world.population);
binWriter.Write((UInt64)0);
binWriter.Write(Encoding.ASCII.GetBytes(world.name.PadRight(64, '\0')));
serverCount++;
totalCount++;
//Send this chunk of world list
if (serverCount >= MAXPERPACKET)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
serverCount = 0;
}
}
//If there is anything left that was missed or the list is empty
if (serverCount > 0 || worldList.Count == 0)
{
if (worldList.Count == 0)
{
memStream = new MemoryStream(0x210);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, data);
subpacket.SetTargetId(0xe0006868);
subPackets.Add(subpacket);
}
return subPackets;
}
}
}

View File

@@ -0,0 +1,133 @@
using System.Collections.Generic;
namespace FFXIVClassic_Lobby_Server.utils
{
class CharacterCreatorUtils
{
private static readonly Dictionary<uint, uint[]> equipmentAppearance = new Dictionary<uint, uint[]>
{
{ 2, new uint[]{60818432,60818432,0,0,0,0,0,0,10656,10560,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //PUG
{ 3, new uint[]{79692890,0,0,0,0,0,0,0,31776,4448,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //GLA
{ 4, new uint[]{147850310,0,0,0,0,0,0,23713,0,10016,5472,1152,6144,0,0,0,0,0,0,0,0,0} }, //MRD
{ 7, new uint[]{210764860,236979210,0,0,0,231736320,0,0,9888,9984,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //ARC
{ 8, new uint[]{168823858,0,0,0,0,0,0,0,13920,7200,1024,10656,6144,0,0,0,0,0,0,0,0,0} }, //LNC
{ 22, new uint[]{294650980,0,0,0,0,0,0,0,7744,5472,1024,5504,4096,0,0,0,0,0,0,0,0,0} }, //THM
{ 23, new uint[]{347079700,0,0,0,0,0,0,0,4448,2240,1024,4416,4096,0,0,0,0,0,0,0,0,0} }, //CNJ
{ 29, new uint[]{705692672,0,0,0,0,0,0,0,0,10016,10656,9632,2048,0,0,0,0,0,0,0,0,0} }, //CRP
{ 30, new uint[]{721421372,0,0,0,0,0,0,0,0,2241,2336,2304,2048,0,0,0,0,0,0,0,0,0} }, //BSM
{ 31, new uint[]{737149962,0,0,0,0,0,0,0,32992,2240,1024,2272,2048,0,0,0,0,0,0,0,0,0} }, //ARM
{ 32, new uint[]{752878592,0,0,0,0,0,0,0,2368,3424,1024,10656,2048,0,0,0,0,0,0,0,0,0} }, //GSM
{ 33, new uint[]{768607252,0,0,0,0,0,0,4448,4449,1792,1024,21888,2048,0,0,0,0,0,0,0,0,0} }, //LTW
{ 34, new uint[]{784335922,0,0,0,0,0,0,0,5505,5473,1024,5505,2048,0,0,0,0,0,0,0,0,0} }, //WVR
{ 35, new uint[]{800064522,0,0,0,0,0,0,20509,5504,2241,1024,1152,2048,0,0,0,0,0,0,0,0,0} }, //ALC
{ 36, new uint[]{815793192,0,0,0,0,0,0,5632,34848,1792,1024,25825,2048,0,0,0,0,0,0,0,0,0} }, //CUL
{ 39, new uint[]{862979092,0,0,0,0,0,0,0,1184,2242,6464,6528,14336,0,0,0,0,0,0,0,0,0} }, //MIN
{ 40, new uint[]{878707732,0,0,0,0,0,0,6304,6624,6560,1024,1152,14336,0,0,0,0,0,0,0,0,0} }, //BOT
{ 41, new uint[]{894436372,0,0,0,0,0,0,6400,1184,9984,1024,6529,14336,0,0,0,0,0,0,0,0,0} }, //FSH
};
public static uint[] GetEquipmentForClass(uint charClass)
{
if (equipmentAppearance.ContainsKey(charClass))
return equipmentAppearance[charClass];
else
return null;
}
public static string GetClassNameForId(short id)
{
switch (id)
{
case 2: return "pug";
case 3: return "gla";
case 4: return "mrd";
case 7: return "arc";
case 8: return "lnc";
case 22: return "thm";
case 23: return "cnj";
case 29: return "crp";
case 30: return "bsm";
case 31: return "arm";
case 32: return "gsm";
case 33: return "ltw";
case 34: return "wvr";
case 35: return "alc";
case 36: return "cul";
case 39: return "min";
case 40: return "btn";
case 41: return "fsh";
default: return "undefined";
}
}
public static uint GetUndershirtForTribe(uint tribe)
{
uint graphicId;
switch (tribe)
{
case 1:
graphicId = 1184;
break;
case 2:
graphicId = 1186;
break;
case 3:
graphicId = 1187;
break;
case 4:
graphicId = 1184;
break;
case 5:
graphicId = 1024;
break;
case 6:
graphicId = 1187;
break;
case 7:
graphicId = 1505;
break;
case 8:
graphicId = 1184;
break;
case 9:
graphicId = 1185;
break;
case 10:
graphicId = 1504;
break;
case 11:
graphicId = 1505;
break;
case 12:
graphicId = 1216;
break;
case 13:
graphicId = 1186;
break;
case 14:
graphicId = 1184;
break;
case 15:
graphicId = 1186;
break;
default:
graphicId = 0;
break;
}
return graphicId;
}
}
}

View File

@@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFXIVClassic Map Server", "FFXIVClassic Map Server\FFXIVClassic Map Server.csproj", "{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -6,6 +6,6 @@
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data></configuration>

View File

@@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using FFXIVClassic_Lobby_Server.packets;
using System.Diagnostics;
using FFXIVClassic_Lobby_Server.common;
using System.Collections.Concurrent;
using System.IO;
using Cyotek.Collections.Generic;
using System.Net;
namespace FFXIVClassic_Lobby_Server
{
class ClientConnection
{
//Connection stuff
public Blowfish blowfish;
public Socket socket;
public byte[] buffer = new byte[0xffff];
private BlockingCollection<BasePacket> sendPacketQueue = new BlockingCollection<BasePacket>(1000);
//Instance Stuff
public uint owner = 0;
public uint connType = 0;
public void queuePacket(BasePacket packet)
{
sendPacketQueue.Add(packet);
}
public void queuePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted)
{
sendPacketQueue.Add(BasePacket.createPacket(subpacket, isAuthed, isEncrypted));
}
public void flushQueuedSendPackets()
{
if (!socket.Connected)
return;
while (sendPacketQueue.Count > 0)
{
BasePacket packet = sendPacketQueue.Take();
byte[] packetBytes = packet.getPacketBytes();
byte[] buffer = new byte[0xffff];
Array.Copy(packetBytes, buffer, packetBytes.Length);
try
{
socket.Send(packetBytes);
}
catch (Exception e)
{ Log.error(String.Format("Weird case, socket was d/ced: {0}", e)); }
}
}
public String getAddress()
{
return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port);
}
public void disconnect()
{
if (socket.Connected)
socket.Disconnect(false);
}
}
}

View File

@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.dataobjects;
using System.IO;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.Actors;
namespace FFXIVClassic_Map_Server
{
class CommandProcessor
{
private static Dictionary<uint, ItemData> gamedataItems = Server.GetGamedataItems();
const UInt32 ITEM_GIL = 1000001;
/// <summary>
/// We only use the default options for SendMessagePacket.
/// May as well make it less unwieldly to view
/// </summary>
/// <param name="client"></param>
/// <param name="message"></param>
private void SendMessage(Session session, String message)
{
if (session != null)
session.GetActor().QueuePacket(SendMessagePacket.BuildPacket(session.id, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", message));
}
internal bool DoCommand(string input, Session session)
{
if (!input.Any() || input.Equals(""))
return false;
input.Trim();
input = input.StartsWith("!") ? input.Substring(1) : input;
var split = input.Split('"')
.Select((str, index) => index % 2 == 0
? str.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
: new String[] { str }
)
.SelectMany(str => str).ToArray();
split = split.ToArray(); // Ignore case on commands
var cmd = split[0];
if (cmd.Any())
{
// if client isnt null, take player to be the player actor
Player player = null;
if (session != null)
player = session.GetActor();
if (cmd.Equals("help"))
{
// if there's another string after this, take it as the command we want the description for
if (split.Length > 1)
{
LuaEngine.RunGMCommand(player, split[1], null, true);
return true;
}
// print out all commands
foreach (var str in Directory.GetFiles("./scripts/commands/gm/"))
{
var c = str.Replace(".lua", "");
c = c.Replace("./scripts/commands/gm/", "");
LuaEngine.RunGMCommand(player, c, null, true);
}
return true;
}
LuaEngine.RunGMCommand(player, cmd.ToString(), split.ToArray());
return true;
}
// Debug
//SendMessage(client, string.Join(",", split));
if (split.Length >= 1)
{
// TODO: reloadzones
#region !reloaditems
if (split[0].Equals("reloaditems"))
{
Program.Log.Info(String.Format("Got request to reload item gamedata"));
SendMessage(session, "Reloading Item Gamedata...");
gamedataItems.Clear();
gamedataItems = Database.GetItemGamedata();
Program.Log.Info(String.Format("Loaded {0} items.", gamedataItems.Count));
SendMessage(session, String.Format("Loaded {0} items.", gamedataItems.Count));
return true;
}
#endregion
#region !property
else if (split[0].Equals("property"))
{
if (split.Length == 4)
{
// ChangeProperty(Utils.MurmurHash2(split[1], 0), Convert.ToUInt32(split[2], 16), split[3]);
}
return true;
}
#endregion
#region !property2
else if (split[0].Equals("property2"))
{
if (split.Length == 4)
{
//ChangeProperty(Convert.ToUInt32(split[1], 16), Convert.ToUInt32(split[2], 16), split[3]);
}
return true;
}
#endregion
}
return false;
}
}
}

View File

@@ -1,17 +1,18 @@
using FFXIVClassic_Lobby_Server.common;
using STA.Settings;
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using MoonSharp.Interpreter.Interop;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
namespace FFXIVClassic_Lobby_Server
namespace FFXIVClassic_Map_Server
{
class ConfigConstants
{
public static String OPTIONS_BINDIP;
public static String OPTIONS_PORT;
public static bool OPTIONS_TIMESTAMP = false;
public static uint DATABASE_WORLDID;
@@ -21,35 +22,75 @@ namespace FFXIVClassic_Lobby_Server
public static String DATABASE_USERNAME;
public static String DATABASE_PASSWORD;
public static bool load()
public static bool Load()
{
Console.Write("Loading config.ini file... ");
Program.Log.Info("Loading map_config.ini file... ");
if (!File.Exists("./config.ini"))
if (!File.Exists("./map_config.ini"))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[FILE NOT FOUND]");
Console.ForegroundColor = ConsoleColor.Gray;
return false;
Program.Log.Error("FILE NOT FOUND");
Program.Log.Error("Loading defaults... ");
}
INIFile configIni = new INIFile("./config.ini");
INIFile configIni = new INIFile("./map_config.ini");
ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1");
ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "1989");
ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true");
ConfigConstants.DATABASE_WORLDID = configIni.GetValue("Database", "worldid", (uint)0);
ConfigConstants.DATABASE_WORLDID = UInt32.Parse(configIni.GetValue("Database", "worldid", "0"));
ConfigConstants.DATABASE_HOST = configIni.GetValue("Database", "host", "");
ConfigConstants.DATABASE_PORT = configIni.GetValue("Database", "port", "");
ConfigConstants.DATABASE_NAME = configIni.GetValue("Database", "database", "");
ConfigConstants.DATABASE_USERNAME = configIni.GetValue("Database", "username", "");
ConfigConstants.DATABASE_PASSWORD = configIni.GetValue("Database", "password", "");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("[OK]");
Console.ForegroundColor = ConsoleColor.Gray;
return true;
}
public static void ApplyLaunchArgs(string[] launchArgs)
{
var args = (from arg in launchArgs select arg.ToLower().Trim().TrimStart('-')).ToList();
for (var i = 0; i + 1 < args.Count; i += 2)
{
var arg = args[i];
var val = args[i + 1];
var legit = false;
if (arg == "ip")
{
IPAddress ip;
if (IPAddress.TryParse(val, out ip) && (legit = true))
OPTIONS_BINDIP = val;
}
else if (arg == "port")
{
UInt16 port;
if (UInt16.TryParse(val, out port) && (legit = true))
OPTIONS_PORT = val;
}
else if (arg == "user" && (legit = true))
{
DATABASE_USERNAME = val;
}
else if (arg == "p" && (legit = true))
{
DATABASE_PASSWORD = val;
}
else if (arg == "db" && (legit = true))
{
DATABASE_NAME = val;
}
else if (arg == "host" && (legit = true))
{
DATABASE_HOST = val;
}
if (!legit)
{
Program.Log.Error("Invalid parameter <{0}> for argument: <--{1}> or argument doesnt exist!", val, arg);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props') AND '$(OS)' == 'Windows_NT'" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props') AND '$(OS)' == 'Windows_NT'" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -11,6 +12,7 @@
<AssemblyName>FFXIVClassic Map Server</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>1d22ec4a</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -31,6 +33,10 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Collections.Generic.CircularBuffer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
@@ -41,19 +47,24 @@
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="KeraLua">
<HintPath>..\packages\NLua.1.3.2.1\lib\net45\KeraLua.dll</HintPath>
<Reference Include="FFXIVClassic.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\FFXIVClassic Common Class Lib\bin\Debug\FFXIVClassic.Common.dll</HintPath>
</Reference>
<Reference Include="MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll</HintPath>
<Reference Include="MoonSharp.Interpreter">
<HintPath>..\packages\MoonSharp.1.2.1.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
</Reference>
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLua">
<HintPath>..\packages\NLua.1.3.2.1\lib\net45\NLua.dll</HintPath>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.5\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -64,15 +75,41 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="actors\area\PrivateArea.cs" />
<Compile Include="actors\area\PrivateAreaContent.cs" />
<Compile Include="actors\area\SpawnLocation.cs" />
<Compile Include="actors\area\Zone.cs" />
<Compile Include="actors\chara\npc\ActorClass.cs" />
<Compile Include="actors\chara\npc\NpcWork.cs" />
<Compile Include="actors\chara\AetheryteWork.cs" />
<Compile Include="actors\chara\player\Equipment.cs" />
<Compile Include="actors\chara\player\Inventory.cs" />
<Compile Include="actors\chara\Work.cs" />
<Compile Include="actors\debug\Debug.cs" />
<Compile Include="actors\director\Director.cs" />
<Compile Include="actors\director\GuildleveDirector.cs" />
<Compile Include="actors\director\Work\GuildleveWork.cs" />
<Compile Include="actors\EventList.cs" />
<Compile Include="actors\group\GLContentGroup.cs" />
<Compile Include="actors\group\ContentGroup.cs" />
<Compile Include="actors\group\Work\ContentGroupWork.cs" />
<Compile Include="actors\group\Work\GlobalTemp.cs" />
<Compile Include="actors\group\Group.cs" />
<Compile Include="actors\group\MonsterParty.cs" />
<Compile Include="actors\group\Party.cs" />
<Compile Include="actors\group\Relation.cs" />
<Compile Include="actors\group\Work\GroupGlobalSave.cs" />
<Compile Include="actors\group\Work\GroupGlobalTemp.cs" />
<Compile Include="actors\group\Work\GroupMemberSave.cs" />
<Compile Include="actors\group\Work\PartyWork.cs" />
<Compile Include="actors\group\Work\RelationWork.cs" />
<Compile Include="actors\judge\Judge.cs" />
<Compile Include="actors\quest\Quest.cs" />
<Compile Include="actors\StaticActors.cs" />
<Compile Include="ClientConnection.cs" />
<Compile Include="common\Bitfield.cs" />
<Compile Include="common\Blowfish.cs" />
<Compile Include="common\Log.cs" />
<Compile Include="common\STA_INIFile.cs" />
<Compile Include="common\Utils.cs" />
<Compile Include="actors\world\WorldMaster.cs" />
<Compile Include="dataobjects\GuildleveData.cs" />
<Compile Include="dataobjects\ZoneConnection.cs" />
<Compile Include="CommandProcessor.cs" />
<Compile Include="ConfigConstants.cs" />
<Compile Include="Database.cs" />
<Compile Include="actors\Actor.cs" />
@@ -85,31 +122,27 @@
<Compile Include="actors\chara\ParameterTemp.cs" />
<Compile Include="actors\chara\player\Player.cs" />
<Compile Include="actors\command\Command.cs" />
<Compile Include="dataobjects\CharaInfo.cs" />
<Compile Include="actors\chara\CharaWork.cs" />
<Compile Include="actors\chara\ParameterSave.cs" />
<Compile Include="actors\chara\player\PlayerWork.cs" />
<Compile Include="dataobjects\database\DBAppearance.cs" />
<Compile Include="dataobjects\database\DBCharacter.cs" />
<Compile Include="dataobjects\database\DBCommands.cs" />
<Compile Include="dataobjects\database\DBJournal.cs" />
<Compile Include="dataobjects\database\DBPlayerData.cs" />
<Compile Include="dataobjects\database\DBStats.cs" />
<Compile Include="dataobjects\database\DBWorld.cs" />
<Compile Include="dataobjects\Item.cs" />
<Compile Include="dataobjects\ConnectedPlayer.cs" />
<Compile Include="dataobjects\InventoryItem.cs" />
<Compile Include="dataobjects\Session.cs" />
<Compile Include="dataobjects\ItemData.cs" />
<Compile Include="dataobjects\RecruitmentDetails.cs" />
<Compile Include="dataobjects\SeamlessBoundry.cs" />
<Compile Include="dataobjects\SearchEntry.cs" />
<Compile Include="lua\LuaEngine.cs" />
<Compile Include="lua\LuaEvent.cs" />
<Compile Include="lua\LuaParam.cs" />
<Compile Include="lua\LuaPlayer.cs" />
<Compile Include="lua\LuaScript.cs" />
<Compile Include="lua\LuaUtils.cs" />
<Compile Include="PacketProcessor.cs" />
<Compile Include="packets\BasePacket.cs" />
<Compile Include="packets\receive\ChatMessagePacket.cs" />
<Compile Include="packets\receive\events\EventUpdatePacket.cs" />
<Compile Include="packets\receive\events\EventStartPacket.cs" />
<Compile Include="packets\receive\GroupCreatedPacket.cs" />
<Compile Include="packets\receive\HandshakePacket.cs" />
<Compile Include="packets\receive\LangaugeCodePacket.cs" />
<Compile Include="packets\receive\ParameterDataRequestPacket.cs" />
<Compile Include="packets\receive\recruitment\RecruitmentDetailsRequestPacket.cs" />
<Compile Include="packets\receive\recruitment\RecruitmentSearchRequestPacket.cs" />
<Compile Include="packets\receive\recruitment\StartRecruitingRequestPacket.cs" />
@@ -119,53 +152,95 @@
<Compile Include="packets\receive\social\FriendlistRequestPacket.cs" />
<Compile Include="packets\receive\supportdesk\FaqBodyRequestPacket.cs" />
<Compile Include="packets\receive\supportdesk\FaqListRequestPacket.cs" />
<Compile Include="packets\receive\supportdesk\GMSupportTicketPacket.cs" />
<Compile Include="packets\receive\supportdesk\GMTicketIssuesRequestPacket.cs" />
<Compile Include="packets\receive\_0x02ReceivePacket.cs" />
<Compile Include="packets\receive\ZoneInCompletePacket.cs" />
<Compile Include="packets\send\actor\ActorDoEmotePacket.cs" />
<Compile Include="packets\send\actor\ActorInstantiatePacket.cs" />
<Compile Include="packets\send\actor\ActorSpecialGraphicPacket.cs" />
<Compile Include="packets\send\actor\battle\BattleAction.cs" />
<Compile Include="packets\send\actor\battle\BattleActionX00Packet.cs" />
<Compile Include="packets\send\actor\battle\BattleActionX18Packet.cs" />
<Compile Include="packets\send\actor\battle\BattleActionX10Packet.cs" />
<Compile Include="packets\send\actor\DeleteAllActorsPacket.cs" />
<Compile Include="packets\send\actor\events\SetEventStatus.cs" />
<Compile Include="packets\send\actor\events\SetNoticeEventCondition.cs" />
<Compile Include="packets\send\actor\events\SetPushEventConditionWithTriggerBox.cs" />
<Compile Include="packets\send\actor\events\SetPushEventConditionWithFan.cs" />
<Compile Include="packets\send\actor\events\SetEmoteEventCondition.cs" />
<Compile Include="packets\send\actor\events\SetTalkEventCondition.cs" />
<Compile Include="packets\send\actor\events\SetPushEventConditionWithCircle.cs" />
<Compile Include="packets\send\actor\inventory\InventoryRemoveX01Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryRemoveX08Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryRemoveX16Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryRemoveX32Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryRemoveX64Packet.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentListX64Packet.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentListX32Packet.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentListX16Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryListX01Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryListX08Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryListX16Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryListX64Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryListX32Packet.cs" />
<Compile Include="packets\send\actor\PlayAnimationOnActorPacket.cs" />
<Compile Include="packets\send\actor\PlayBGAnimation.cs" />
<Compile Include="packets\send\actor\_0x132Packet.cs" />
<Compile Include="packets\send\actor\SetActorIsZoningPacket.cs" />
<Compile Include="packets\send\actor\DoBattleActionPacket.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentChangePacket.cs" />
<Compile Include="packets\send\actor\battle\BattleActionX01Packet.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentListX01Packet.cs" />
<Compile Include="packets\send\actor\inventory\InventoryBeginChangePacket.cs" />
<Compile Include="packets\send\actor\inventory\InventoryEndChangePacket.cs" />
<Compile Include="packets\send\actor\inventory\InventoryItemEndPacket.cs" />
<Compile Include="packets\send\actor\inventory\InventoryItemPacket.cs" />
<Compile Include="packets\send\actor\inventory\InventorySetBeginPacket.cs" />
<Compile Include="packets\send\actor\inventory\InventorySetEndPacket.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentSetupPacket.cs" />
<Compile Include="packets\send\actor\inventory\EquipmentListX08Packet.cs" />
<Compile Include="packets\send\actor\RemoveActorPacket.cs" />
<Compile Include="packets\send\actor\SetActorIconPacket.cs" />
<Compile Include="packets\send\actor\SetActorIdleAnimationPacket.cs" />
<Compile Include="packets\send\actor\SetActorSubStatPacket.cs" />
<Compile Include="packets\send\actor\SetActorStatusPacket.cs" />
<Compile Include="packets\send\actor\SetActorBGPropertiesPacket.cs" />
<Compile Include="packets\send\actor\_0xFPacket.cs" />
<Compile Include="packets\send\groups\CreateNamedGroup.cs" />
<Compile Include="packets\send\groups\CreateNamedGroupMultiple.cs" />
<Compile Include="packets\send\events\EndEventPacket.cs" />
<Compile Include="packets\send\events\KickEventPacket.cs" />
<Compile Include="packets\send\events\RunEventFunctionPacket.cs" />
<Compile Include="packets\send\list\ListEntry.cs" />
<Compile Include="packets\send\list\ListUtils.cs" />
<Compile Include="packets\send\list\SetListPropertyPacket.cs" />
<Compile Include="packets\send\list\ListBeginPacket.cs" />
<Compile Include="packets\send\list\ListEndPacket.cs" />
<Compile Include="packets\send\list\ListEntriesEndPacket.cs" />
<Compile Include="packets\send\list\ListStartPacket.cs" />
<Compile Include="packets\send\GameMessagePacket.cs" />
<Compile Include="packets\send\groups\DeleteGroupPacket.cs" />
<Compile Include="packets\send\groups\GroupHeaderPacket.cs" />
<Compile Include="packets\send\groups\GroupMember.cs" />
<Compile Include="packets\send\groups\GroupMembersBeginPacket.cs" />
<Compile Include="packets\send\groups\GroupMembersEndPacket.cs" />
<Compile Include="packets\send\groups\ContentMembersX08Packet.cs" />
<Compile Include="packets\send\groups\GroupMembersX08Packet.cs" />
<Compile Include="packets\send\groups\ContentMembersX16Packet.cs" />
<Compile Include="packets\send\groups\GroupMembersX16Packet.cs" />
<Compile Include="packets\send\groups\ContentMembersX32Packet.cs" />
<Compile Include="packets\send\groups\GroupMembersX32Packet.cs" />
<Compile Include="packets\send\groups\ContentMembersX64Packet.cs" />
<Compile Include="packets\send\groups\GroupMembersX64Packet.cs" />
<Compile Include="packets\send\groups\SynchGroupWorkValuesPacket.cs" />
<Compile Include="packets\send\player\GenericDataPacket.cs" />
<Compile Include="packets\send\player\SendAchievementRatePacket.cs" />
<Compile Include="packets\send\player\SetCurrentJobPacket.cs" />
<Compile Include="packets\send\player\SetCurrentMountGoobbuePacket.cs" />
<Compile Include="packets\send\player\SetCurrentMountChocoboPacket.cs" />
<Compile Include="packets\send\player\SetGrandCompanyPacket.cs" />
<Compile Include="packets\send\actor\SetActorNamePacket.cs" />
<Compile Include="packets\send\actor\SetActorPropetyPacket.cs" />
<Compile Include="packets\send\actor\SetActorSpeedPacket.cs" />
<Compile Include="packets\send\actor\SetActorStatePacket.cs" />
<Compile Include="packets\send\actor\SetActorTargetAnimatedPacket.cs" />
<Compile Include="packets\send\actor\SetActorTargetPacket.cs" />
<Compile Include="packets\send\actor\SetActorStatusAllPacket.cs" />
<Compile Include="packets\send\Actor\SetActorNamePacket.cs" />
<Compile Include="packets\send\Actor\SetActorPropetyPacket.cs" />
<Compile Include="packets\send\Actor\SetActorSpeedPacket.cs" />
<Compile Include="packets\send\Actor\SetActorStatePacket.cs" />
<Compile Include="packets\send\Actor\SetActorTargetAnimatedPacket.cs" />
<Compile Include="packets\send\Actor\SetActorTargetPacket.cs" />
<Compile Include="packets\send\Actor\SetActorStatusAllPacket.cs" />
<Compile Include="packets\send\login\0x2Packet.cs" />
<Compile Include="packets\send\actor\AddActorPacket.cs" />
<Compile Include="packets\send\actor\MoveActorToPositionPacket.cs" />
<Compile Include="packets\send\actor\SetActorAppearancePacket.cs" />
<Compile Include="packets\send\actor\SetActorPositionPacket.cs" />
<Compile Include="packets\send\login\0x7ResponsePacket.cs" />
<Compile Include="packets\send\LogoutPacket.cs" />
<Compile Include="packets\send\player\SetCompletedAchievementsPacket.cs" />
<Compile Include="packets\send\player\AchievementEarnedPacket.cs" />
@@ -175,9 +250,10 @@
<Compile Include="packets\send\player\SetHasGoobbuePacket.cs" />
<Compile Include="packets\send\player\SetHasChocoboPacket.cs" />
<Compile Include="packets\send\player\SetLatestAchievementsPacket.cs" />
<Compile Include="packets\send\player\SetPlayerItemStoragePacket.cs" />
<Compile Include="packets\send\player\SetPlayerDreamPacket.cs" />
<Compile Include="packets\send\player\SetPlayerTitlePacket.cs" />
<Compile Include="packets\send\player\_0x196Packet.cs" />
<Compile Include="packets\send\player\SetSpecialEventWorkPacket.cs" />
<Compile Include="packets\send\PongPacket.cs" />
<Compile Include="packets\send\QuitPacket.cs" />
<Compile Include="packets\send\recruitment\CurrentRecruitmentDetailsPacket.cs" />
@@ -202,30 +278,76 @@
<Compile Include="packets\send\supportdesk\FaqListResponsePacket.cs" />
<Compile Include="packets\send\supportdesk\GMTicketPacket.cs" />
<Compile Include="packets\send\supportdesk\GMTicketSentResponsePacket.cs" />
<Compile Include="packets\send\_0x02Packet.cs" />
<Compile Include="packets\send\_0x10Packet.cs" />
<Compile Include="packets\send\_0xE2Packet.cs" />
<Compile Include="packets\SubPacket.cs" />
<Compile Include="packets\receive\PingPacket.cs" />
<Compile Include="packets\receive\UpdatePlayerPositionPacket.cs" />
<Compile Include="packets\WorldPackets\Receive\ErrorPacket.cs" />
<Compile Include="packets\WorldPackets\Receive\PartySyncPacket.cs" />
<Compile Include="packets\WorldPackets\Receive\SessionEndPacket.cs" />
<Compile Include="packets\WorldPackets\Receive\SessionBeginPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\CreateLinkshellPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\DeleteLinkshellPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\LinkshellInviteCancelPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\LinkshellChangePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\LinkshellRankChangePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\ModifyLinkshellPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\GroupInviteResultPacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\LinkshellInvitePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\PartyInvitePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\LinkshellLeavePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\PartyLeavePacket.cs" />
<Compile Include="packets\WorldPackets\Send\Group\PartyModifyPacket.cs" />
<Compile Include="packets\WorldPackets\Send\SessionBeginConfirmPacket.cs" />
<Compile Include="packets\WorldPackets\Send\SessionEndConfirmPacket.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="lua\LuaUtils.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Server.cs" />
<Compile Include="utils\ActorPropertyPacketUtil.cs" />
<Compile Include="utils\CharacterUtils.cs" />
<Compile Include="Zone.cs" />
<Compile Include="utils\SQLGeneration.cs" />
<Compile Include="actors\area\Area.cs" />
<Compile Include="WorldManager.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<Content Include="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SubType>Designer</SubType>
</Content>
<None Include="NLog.xsd">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
<Compile Include="packets\WorldPackets\Send\WorldRequestZoneChangePacket.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="actors\area\" />
<Folder Include="actors\director\" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
xcopy /s /y "D:\Coding\FFXIV Related\ffxiv-classic-map-server\packages\NLua.1.3.2.1\lib\native\*.*" "$(TargetDir)"</PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.0.0-beta3\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- optional, add some variabeles
https://github.com/nlog/NLog/wiki/Configuration-file#variables
-->
<variable name="myvar" value="myvalue" />
<!--
See https://github.com/nlog/nlog/wiki/Configuration-file
for information on customizing logging rules and outputs.
-->
<targets async="true">
<!--
add your targets here
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
-->
<!--
Write events to a file with the date in the filename.
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
<!--<target xsi:type="ColoredConsole" name="console" layout="[${longdate}] [${uppercase:${level}}] ${message}" />-->
<target xsi:type="File" name="file" fileName="${basedir}/logs/${shortdate}/map.log"
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
<target xsi:type="ColoredConsole" name="console"
layout="[${date:format=dd MMM yyyy HH\:mm\:ss.fff}] [${uppercase:${level}}] ${message}" />
<target xsi:type="ColoredConsole" name="packets"
layout="${message}">
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.BasePacket') and equals('${event-context:item=color}', '6')"
backgroundColor="DarkYellow" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.SubPacket') and equals('${event-context:item=color}', '4')"
backgroundColor="DarkRed" foregroundColor="NoChange" />
<highlight-row
condition="equals('${logger}', 'FFXIVClassic.Common.SubPacket') and equals('${event-context:item=color}', '5')"
backgroundColor="DarkMagenta" foregroundColor="NoChange" />
</target>
</targets>
<rules>
<!-- add your logging rules here -->
<logger name='*' minlevel='Trace' writeTo='file' />
<logger name='FFXIVClassic_Map_Server.Program' minlevel='Trace' writeTo='console' />
<logger name='FFXIVClassic_Map_Server.lua.*' minlevel='Trace' writeTo='console' />
<logger name='FFXIVClassic_Map_Server.packets.*' minlevel='Debug' writeTo='packets' />
<logger name='FFXIVClassic.Common.*' minlevel='Debug' writeTo='packets' />
<!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
<logger name="*" minlevel="Debug" writeTo="f" />
-->
</rules>
</nlog>

File diff suppressed because it is too large Load Diff

View File

@@ -1,439 +1,259 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.packets;
using MySql.Data.MySqlClient;
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets;
using FFXIVClassic_Map_Server.packets.receive;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.packets.send.login;
using FFXIVClassic_Map_Server.packets.send.Actor.inventory;
using FFXIVClassic_Map_Server.packets.send.Actor;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server;
using FFXIVClassic_Map_Server.packets.send.player;
using FFXIVClassic_Map_Server.dataobjects.chara;
using FFXIVClassic_Map_Server.packets.send.supportdesk;
using FFXIVClassic_Map_Server.packets.receive.social;
using FFXIVClassic_Map_Server.packets.send.social;
using FFXIVClassic_Map_Server.packets.receive.supportdesk;
using FFXIVClassic_Map_Server.packets.receive.recruitment;
using FFXIVClassic_Map_Server.packets.send.recruitment;
using FFXIVClassic_Map_Server.packets.send.list;
using FFXIVClassic_Map_Server.packets.receive.events;
using FFXIVClassic_Map_Server.packets.send.events;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.dataobjects.actors;
using FFXIVClassic_Map_Server.dataobjects.chara.npc;
using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.packets.WorldPackets.Send;
using FFXIVClassic_Map_Server.packets.WorldPackets.Receive;
using FFXIVClassic_Map_Server.actors.director;
namespace FFXIVClassic_Lobby_Server
namespace FFXIVClassic_Map_Server
{
class PacketProcessor
{
LuaEngine luaEngine = new LuaEngine();
Dictionary<uint, ConnectedPlayer> mPlayers;
List<ClientConnection> mConnections;
Server mServer;
StaticActors mStaticActors = new StaticActors();
Zone inn = new Zone();
public PacketProcessor(Dictionary<uint, ConnectedPlayer> playerList, List<ClientConnection> connectionList)
public PacketProcessor(Server server)
{
mPlayers = playerList;
mConnections = connectionList;
// initNpcs();
mServer = server;
}
public void processPacket(ClientConnection client, BasePacket packet)
public void ProcessPacket(ZoneConnection client, SubPacket subpacket)
{
ConnectedPlayer player = null;
if (client.owner != 0 && mPlayers.ContainsKey(client.owner))
player = mPlayers[client.owner];
Session session = mServer.GetSession(subpacket.header.sourceId);
if (packet.header.isEncrypted == 0x01)
BasePacket.decryptPacket(client.blowfish, ref packet);
if (session == null && subpacket.gameMessage.opcode != 0x1000)
return;
// packet.debugPrintPacket();
List<SubPacket> subPackets = packet.getSubpackets();
foreach (SubPacket subpacket in subPackets)
{
if (subpacket.header.type == 0x01)
{
packet.debugPrintPacket();
byte[] reply1Data = {
0x01, 0x00, 0x00, 0x00, 0x28, 0x0, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x07, 0x00, 0x00, 0x0, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0xFD, 0xFF, 0xFF,
0x43, 0xEC, 0x00, 0xE0, 0x00, 0x0, 0x00, 0x0
};
BasePacket reply1 = new BasePacket(reply1Data);
BasePacket reply2 = new BasePacket("./packets/login/login2.bin");
using (MemoryStream mem = new MemoryStream(reply1.data))
{
using (BinaryWriter binReader = new BinaryWriter(mem))
{
binReader.BaseStream.Seek(0x14, SeekOrigin.Begin);
binReader.Write((UInt32)Utils.UnixTimeStampUTC());
}
}
//Already Handshaked
if (client.owner != 0)
{
using (MemoryStream mem = new MemoryStream(reply2.data))
{
using (BinaryWriter binReader = new BinaryWriter(mem))
{
binReader.BaseStream.Seek(0x10, SeekOrigin.Begin);
binReader.Write(player.actorID);
}
}
client.queuePacket(reply1);
client.queuePacket(reply2);
break;
}
uint actorID = 0;
using (MemoryStream mem = new MemoryStream(packet.data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
byte[] readIn = new byte[12];
binReader.BaseStream.Seek(0x14, SeekOrigin.Begin);
binReader.Read(readIn, 0, 12);
actorID = UInt32.Parse(Encoding.ASCII.GetString(readIn));
}
catch (Exception)
{ }
}
}
if (actorID == 0)
break;
//Second connection
if (mPlayers.ContainsKey(actorID))
player = mPlayers[actorID];
using (MemoryStream mem = new MemoryStream(reply2.data))
{
using (BinaryWriter binReader = new BinaryWriter(mem))
{
binReader.BaseStream.Seek(0x10, SeekOrigin.Begin);
binReader.Write(actorID);
}
}
if (player == null)
{
player = new ConnectedPlayer(actorID);
mPlayers[actorID] = player;
client.owner = actorID;
client.connType = 0;
player.setConnection1(client);
Log.debug(String.Format("Got actorID {0} for conn {1}.", actorID, client.getAddress()));
}
else
{
client.owner = actorID;
client.connType = 1;
player.setConnection2(client);
}
//Get Character info
//Create player actor
reply1.debugPrintPacket();
client.queuePacket(reply1);
client.queuePacket(reply2);
break;
}
else if (subpacket.header.type == 0x07)
{
//Ping?
//packet.debugPrintPacket();
BasePacket init = Login0x7ResponsePacket.buildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC());
//client.queuePacket(init);
}
else if (subpacket.header.type == 0x08)
{
//Response, client's current [actorID][time]
packet.debugPrintPacket();
}
else if (subpacket.header.type == 0x03)
{
//Normal Game Opcode
switch (subpacket.gameMessage.opcode)
{
//World Server - Error
case 0x100A:
ErrorPacket worldError = new ErrorPacket(subpacket.data);
switch (worldError.errorCode)
{
case 0x01:
session.GetActor().SendGameMessage(Server.GetWorldManager().GetActor(), 60005, 0x20);
break;
}
break;
//World Server - Session Begin
case 0x1000:
subpacket.DebugPrintSubPacket();
SessionBeginPacket beginSessionPacket = new SessionBeginPacket(subpacket.data);
session = mServer.AddSession(subpacket.header.sourceId);
if (!beginSessionPacket.isLogin)
Server.GetWorldManager().DoZoneIn(session.GetActor(), false, session.GetActor().destinationSpawnType);
Program.Log.Info("{0} has been added to the session list.", session.GetActor().customDisplayName);
client.FlushQueuedSendPackets();
break;
//World Server - Session End
case 0x1001:
SessionEndPacket endSessionPacket = new SessionEndPacket(subpacket.data);
if (endSessionPacket.destinationZoneId == 0)
session.GetActor().CleanupAndSave();
else
session.GetActor().CleanupAndSave(endSessionPacket.destinationZoneId, endSessionPacket.destinationSpawnType, endSessionPacket.destinationX, endSessionPacket.destinationY, endSessionPacket.destinationZ, endSessionPacket.destinationRot);
Server.GetServer().RemoveSession(session.id);
Program.Log.Info("{0} has been removed from the session list.", session.GetActor().customDisplayName);
session.QueuePacket(SessionEndConfirmPacket.BuildPacket(session, endSessionPacket.destinationZoneId));
client.FlushQueuedSendPackets();
break;
//World Server - Party Synch
case 0x1020:
PartySyncPacket partySyncPacket = new PartySyncPacket(subpacket.data);
Server.GetWorldManager().PartyMemberListRecieved(partySyncPacket);
break;
//Ping
case 0x0001:
//subpacket.debugPrintSubPacket();
//subpacket.DebugPrintSubPacket();
PingPacket pingPacket = new PingPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(PongPacket.buildPacket(player.actorID, pingPacket.time), true, false));
session.QueuePacket(PongPacket.BuildPacket(session.id, pingPacket.time));
session.Ping();
break;
//Unknown
case 0x0002:
BasePacket packet196 = new BasePacket("./packets/196");
BasePacket reply7 = new BasePacket("./packets/login/login7_data.bin");
BasePacket reply8 = new BasePacket("./packets/login/login8_data.bin");
BasePacket reply9 = new BasePacket("./packets/login/login9_zonesetup.bin");
BasePacket reply10 = new BasePacket("./packets/login/login10.bin");
BasePacket reply11 = new BasePacket("./packets/login/login11.bin");
BasePacket reply12 = new BasePacket("./packets/login/login12.bin");
// BasePacket keyitems = new BasePacket("./packets/login/keyitems.bin");
// BasePacket currancy = new BasePacket("./packets/login/currancy.bin");
#region replaceid
//currancy.replaceActorID(player.actorID);
//keyitems.replaceActorID(player.actorID);
packet196.replaceActorID(player.actorID);
reply7.replaceActorID(player.actorID);
reply8.replaceActorID(player.actorID);
reply9.replaceActorID(player.actorID);
reply10.replaceActorID(player.actorID);
reply11.replaceActorID(player.actorID);
reply12.replaceActorID(player.actorID);
#endregion
client.queuePacket(SetMapPacket.buildPacket(player.actorID, 0xD1, 0xF4), true, false);
// client.queuePacket(SetMapPacket.buildPacket(player.actorID, 0x68, 0xF4), true, false);
client.queuePacket(_0x2Packet.buildPacket(player.actorID), true, false);
client.queuePacket(SendMessagePacket.buildPacket(player.actorID, player.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", "-------- Login Message --------\nWelcome to the 1.0 Dev Server"), true, false);
client.queuePacket(SetMusicPacket.buildPacket(player.actorID, 0x3D, 0x01), true, false);
client.queuePacket(SetWeatherPacket.buildPacket(player.actorID, SetWeatherPacket.WEATHER_CLEAR), true, false);
BasePacket actorPacket = player.getActor().getInitPackets(player.actorID);
client.queuePacket(actorPacket);
//Retainers
List<ListEntry> retainerListEntries = new List<ListEntry>();
retainerListEntries.Add(new ListEntry(player.actorID, 0xFFFFFFFF, 0x139E, false, true, player.getActor().customDisplayName));
retainerListEntries.Add(new ListEntry(0x23, 0x0, 0xFFFFFFFF, false, false, "TEST1"));
retainerListEntries.Add(new ListEntry(0x24, 0x0, 0xFFFFFFFF, false, false, "TEST2"));
retainerListEntries.Add(new ListEntry(0x25, 0x0, 0xFFFFFFFF, false, false, "TEST3"));
BasePacket retainerListPacket = BasePacket.createPacket(ListUtils.createRetainerList(player.actorID, 0xF4, 1, 0x800000000004e639, retainerListEntries), true, false);
client.queuePacket(retainerListPacket);
//Party
List<ListEntry> partyListEntries = new List<ListEntry>();
partyListEntries.Add(new ListEntry(player.actorID, 0xFFFFFFFF, 0xFFFFFFFF, false, true, player.getActor().customDisplayName));
partyListEntries.Add(new ListEntry(0x029B27D3, 0xFFFFFFFF, 0x195, false, true, "Valentine Bluefeather"));
BasePacket partyListPacket = BasePacket.createPacket(ListUtils.createPartyList(player.actorID, 0xF4, 1, 0x8000000000696df2, partyListEntries), true, false);
client.queuePacket(partyListPacket);
////////ITEMS////////
client.queuePacket(InventoryBeginChangePacket.buildPacket(player.actorID), true, false);
#region itemsetup
//TEST
List<Item> items = new List<Item>();
items.Add(new Item(1337, 8030920, 5)); //Leather Jacket
items.Add(new Item(1338, 8013626, 1)); //Chocobo Mask
items.Add(new Item(1339, 5030402, 2)); //Thyrus
items.Add(new Item(1340, 8013635, 3)); //Dalamud Horn
items.Add(new Item(1341, 10100132, 4)); //Savage Might 4
items.Add(new Item(1342, 8032407, 6)); //Green Summer Halter (Female)
items.Add(new Item(1343, 8051307, 7)); //Green Summer Tanga (Female)
items.Add(new Item(1344, 8050766, 8)); //Flame Private's Saroul
int count = 0;
items[2].isHighQuality = true;
items[0].durability = 9999;
items[0].spiritbind = 10000;
items[0].materia1 = 6;
items[0].materia2 = 7;
items[0].materia3 = 8;
items[0].materia4 = 9;
items[0].materia5 = 10;
items[1].durability = 9999;
items[2].durability = 0xFFFFFFF;
items[3].durability = 9999;
items[4].quantity = 99;
//Reused
SubPacket endInventory = InventorySetEndPacket.buildPacket(player.actorID);
SubPacket beginInventory = InventorySetBeginPacket.buildPacket(player.actorID, 200, 00);
SubPacket setInventory = InventoryItemPacket.buildPacket(player.actorID, items, ref count);
List<SubPacket> setinvPackets = new List<SubPacket>();
setinvPackets.Add(beginInventory);
setinvPackets.Add(setInventory);
setinvPackets.Add(endInventory);
#endregion
client.queuePacket(BasePacket.createPacket(setinvPackets, true, false));
//client.queuePacket(currancy);
//client.queuePacket(keyitems);
#region equipsetup
EquipmentSetupPacket initialEqupmentPacket = new EquipmentSetupPacket();
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_BODY, 5);
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_HEAD, 3);
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERSHIRT, 6);
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_UNDERGARMENT, 7);
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_MAINHAND, 2);
initialEqupmentPacket.setItem(EquipmentSetupPacket.SLOT_LEGS, 8);
#endregion
//Equip Init
client.queuePacket(InventorySetBeginPacket.buildPacket(player.actorID, 0x23, InventorySetBeginPacket.CODE_EQUIPMENT), true, false);
client.queuePacket(BasePacket.createPacket(initialEqupmentPacket.buildPackets(player.actorID), true, false));
client.queuePacket(InventorySetEndPacket.buildPacket(player.actorID), true, false);
client.queuePacket(InventoryEndChangePacket.buildPacket(player.actorID), true, false);
////////ITEMS////////
client.queuePacket(SetGrandCompanyPacket.buildPacket(player.actorID, player.actorID, 0x01, 0x1B, 0x1B, 0x1B), true, false);
client.queuePacket(SetPlayerTitlePacket.buildPacket(player.actorID, player.actorID, 0x00), true, false);
client.queuePacket(SetCurrentJobPacket.buildPacket(player.actorID, player.actorID, 0x13), true, false);
//client.queuePacket(packet196);//client.queuePacket(_0x196Packet.buildPacket(player.actorID, player.actorID), true, false);
client.queuePacket(SetChocoboNamePacket.buildPacket(player.actorID, player.actorID, "Boco"), true, false);
client.queuePacket(SetHasChocoboPacket.buildPacket(player.actorID, true), true, false);
client.queuePacket(SetHasGoobbuePacket.buildPacket(player.actorID, true), true, false);
SetCompletedAchievementsPacket cheevos = new SetCompletedAchievementsPacket();
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_BATTLE] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_CHARACTER] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_CURRENCY] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_DUNGEONS] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_EXPLORATION] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_GATHERING] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_GRAND_COMPANY] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_ITEMS] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_MATERIA] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_QUESTS] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_SEASONAL_EVENTS] = true;
cheevos.achievementFlags[SetCompletedAchievementsPacket.CATEGORY_SYNTHESIS] = true;
client.queuePacket(cheevos.buildPacket(player.actorID), true, false);
client.queuePacket(SetLatestAchievementsPacket.buildPacket(player.actorID, new uint[5]), true, false);
client.queuePacket(SetAchievementPointsPacket.buildPacket(player.actorID, 0x00), true, false);
SetCutsceneBookPacket book = new SetCutsceneBookPacket();
for (int i = 0; i < book.cutsceneFlags.Length; i++)
book.cutsceneFlags[i] = true;
client.queuePacket(book.buildPacket(player.actorID), true, false);
//client.queuePacket(SetPlayerDreamPacket.buildPacket(player.actorID, 11), true, false);
//BasePacket packet1a5 = new BasePacket("./packets/1ax/1a5");
//packet1a5.replaceActorID(player.actorID);
//client.queuePacket(packet1a5);
//loadTest(client, player);
//return;
inn.addActorToZone(player.getActor());
// BasePacket tpacket = BasePacket.createPacket(player.getActor().createInitSubpackets(player.actorID), true, false);
// client.queuePacket(tpacket);
client.queuePacket(reply7);
client.queuePacket(reply8);
client.queuePacket(reply9);
client.queuePacket(reply10);
// client.queuePacket(reply11);
client.queuePacket(reply12);
subpacket.DebugPrintSubPacket();
session.QueuePacket(_0x2Packet.BuildPacket(session.id));
client.FlushQueuedSendPackets();
break;
//Chat Received
case 0x0003:
ChatMessagePacket chatMessage = new ChatMessagePacket(subpacket.data);
Log.info(String.Format("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType));
subpacket.debugPrintSubPacket();
//Program.Log.Info("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType);
if (chatMessage.message.StartsWith("!"))
{
if (Server.GetCommandProcessor().DoCommand(chatMessage.message, session))
return; ;
}
if (chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SAY || chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SHOUT)
session.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(session.id, chatMessage.logType, session.GetActor().customDisplayName, chatMessage.message), false);
break;
//Unknown
//Langauge Code (Client safe to send packets to now)
case 0x0006:
LangaugeCodePacket langCode = new LangaugeCodePacket(subpacket.data);
LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onBeginLogin", true);
Server.GetWorldManager().DoZoneIn(session.GetActor(), true, 0x1);
LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onLogin", true);
session.languageCode = langCode.languageCode;
break;
//Unknown - Happens a lot at login, then once every time player zones
case 0x0007:
//subpacket.DebugPrintSubPacket();
ZoneInCompletePacket zoneInCompletePacket = new ZoneInCompletePacket(subpacket.data);
break;
//Update Position
case 0x00CA:
//Update Position
UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data);
player.updatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState);
session.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState);
session.GetActor().SendInstanceUpdate();
//Update Instance
List<BasePacket> instanceUpdatePackets = player.updateInstance(inn.getActorsAroundActor(player.getActor(), 50));
foreach (BasePacket bp in instanceUpdatePackets)
client.queuePacket(bp);
if (session.GetActor().IsInZoneChange())
session.GetActor().SetZoneChanging(false);
break;
//Set Target
case 0x00CD:
//subpacket.debugPrintSubPacket();
//subpacket.DebugPrintSubPacket();
SetTargetPacket setTarget = new SetTargetPacket(subpacket.data);
player.getActor().currentTarget = setTarget.actorID;
client.queuePacket(BasePacket.createPacket(SetActorTargetAnimatedPacket.buildPacket(player.actorID, player.actorID, setTarget.actorID), true, false));
session.GetActor().currentTarget = setTarget.actorID;
session.GetActor().BroadcastPacket(SetActorTargetAnimatedPacket.BuildPacket(session.id, setTarget.actorID), true);
break;
//Lock Target
case 0x00CC:
LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data);
player.getActor().currentLockedTarget = lockTarget.actorID;
session.GetActor().currentLockedTarget = lockTarget.actorID;
break;
//Start Event
case 0x012D:
subpacket.debugPrintSubPacket();
subpacket.DebugPrintSubPacket();
EventStartPacket eventStart = new EventStartPacket(subpacket.data);
player.eventCurrentOwner = eventStart.scriptOwnerActorID;
player.eventCurrentStarter = eventStart.eventStarter;
//Is it a static actor? If not look in the player's instance
//Actor ownerActor = findActor(player, player.eventCurrentOwner);
/*
if (eventStart.error != null)
{
player.errorMessage += eventStart.error;
//if (ownerActor == null)
// break;
if (eventStart.errorIndex == eventStart.errorNum - 1)
Program.Log.Error("\n"+player.errorMessage);
//luaEngine.doEventStart(player, ownerActor, eventStart);
//Log.debug(String.Format("\n===Event START===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nEvent Starter: {4}\nParams: {5}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.val1, eventStart.val2, eventStart.eventStarter, LuaParamReader.dumpParams(eventStart.luaParams)));
break;
}
*/
Actor ownerActor = Server.GetStaticActors(eventStart.scriptOwnerActorID);
session.GetActor().currentEventOwner = eventStart.scriptOwnerActorID;
session.GetActor().currentEventName = eventStart.triggerName;
if (ownerActor == null)
{
//Is it a instance actor?
ownerActor = session.GetActor().zone.FindActorInArea(session.GetActor().currentEventOwner);
if (ownerActor == null)
{
//Is it a Director?
Director director = session.GetActor().GetDirector(eventStart.scriptOwnerActorID);
if (director != null)
ownerActor = director;
else
{
Program.Log.Debug("\n===Event START===\nCould not find actor 0x{0:X} for event started by caller: 0x{1:X}\nEvent Starter: {2}\nParams: {3}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams));
break;
}
}
}
session.GetActor().StartEvent(ownerActor, eventStart);
Program.Log.Debug("\n===Event START===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nEvent Starter: {4}\nParams: {5}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.val1, eventStart.val2, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams));
break;
//Unknown, happens at npc spawn and cutscene play????
case 0x00CE:
subpacket.DebugPrintSubPacket();
break;
//Event Result
case 0x012E:
subpacket.debugPrintSubPacket();
subpacket.DebugPrintSubPacket();
EventUpdatePacket eventUpdate = new EventUpdatePacket(subpacket.data);
Log.debug(String.Format("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nFunction ID: 0x{4:X}\nParams: {5}", eventUpdate.actorID, eventUpdate.scriptOwnerActorID, eventUpdate.val1, eventUpdate.val2, eventUpdate.step, LuaUtils.dumpParams(eventUpdate.luaParams)));
Program.Log.Debug("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nStep: 0x{4:X}\nParams: {5}", eventUpdate.actorID, eventUpdate.scriptOwnerActorID, eventUpdate.val1, eventUpdate.val2, eventUpdate.step, LuaUtils.DumpParams(eventUpdate.luaParams));
/*
//Is it a static actor? If not look in the player's instance
Actor updateOwnerActor = Server.GetStaticActors(session.GetActor().currentEventOwner);
if (updateOwnerActor == null)
{
updateOwnerActor = Server.GetWorldManager().GetActorInWorld(session.GetActor().currentEventOwner);
if (session.GetActor().currentDirector != null && session.GetActor().currentEventOwner == session.GetActor().currentDirector.actorId)
updateOwnerActor = session.GetActor().currentDirector;
/*Actor updateOwnerActor = findActor(player, player.eventCurrentOwner);
if (updateOwnerActor == null)
break;
luaEngine.doEventUpdated(player, updateOwnerActor, eventUpdate);
}
*/
session.GetActor().UpdateEvent(eventUpdate);
//LuaEngine.DoActorOnEventUpdated(session.GetActor(), updateOwnerActor, eventUpdate);
break;
case 0x012F:
subpacket.debugPrintSubPacket();
subpacket.DebugPrintSubPacket();
ParameterDataRequestPacket paramRequest = new ParameterDataRequestPacket(subpacket.data);
if (paramRequest.paramName.Equals("charaWork/exp"))
session.GetActor().SendCharaExpInfo();
break;
//Group Created Confirm
case 0x0133:
GroupCreatedPacket groupCreated = new GroupCreatedPacket(subpacket.data);
Server.GetWorldManager().SendGroupInit(session, groupCreated.groupId);
break;
/* RECRUITMENT */
//Start Recruiting
case 0x01C3:
StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(StartRecruitingResponse.buildPacket(player.actorID, true), true, false));
session.QueuePacket(StartRecruitingResponse.BuildPacket(session.id, true));
break;
//End Recruiting
case 0x01C4:
client.queuePacket(BasePacket.createPacket(EndRecruitmentPacket.buildPacket(player.actorID), true, false));
session.QueuePacket(EndRecruitmentPacket.BuildPacket(session.id));
break;
//Party Window Opened, Request State
case 0x01C5:
client.queuePacket(BasePacket.createPacket(RecruiterStatePacket.buildPacket(player.actorID, true, true, 1), true, false));
session.QueuePacket(RecruiterStatePacket.BuildPacket(session.id, false, false, 0));
break;
//Search Recruiting
case 0x01C7:
@@ -447,166 +267,84 @@ namespace FFXIVClassic_Lobby_Server
details.purposeId = 2;
details.locationId = 1;
details.subTaskId = 1;
details.comment = "This is a test details packet sent by the server. No implementation has been created yet...";
details.comment = "This is a test details packet sent by the server. No implementation has been Created yet...";
details.num[0] = 1;
client.queuePacket(BasePacket.createPacket(CurrentRecruitmentDetailsPacket.buildPacket(player.actorID, details), true, false));
session.QueuePacket(CurrentRecruitmentDetailsPacket.BuildPacket(session.id, details));
break;
//Accepted Recruiting
case 0x01C6:
subpacket.debugPrintSubPacket();
subpacket.DebugPrintSubPacket();
break;
/* SOCIAL STUFF */
case 0x01C9:
AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(BlacklistAddedPacket.buildPacket(player.actorID, true, addBlackList.name), true, false));
session.QueuePacket(BlacklistAddedPacket.BuildPacket(session.id, true, addBlackList.name));
break;
case 0x01CA:
AddRemoveSocialPacket removeBlackList = new AddRemoveSocialPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(BlacklistRemovedPacket.buildPacket(player.actorID, true, removeBlackList.name), true, false));
AddRemoveSocialPacket RemoveBlackList = new AddRemoveSocialPacket(subpacket.data);
session.QueuePacket(BlacklistRemovedPacket.BuildPacket(session.id, true, RemoveBlackList.name));
break;
case 0x01CB:
int offset1 = 0;
client.queuePacket(BasePacket.createPacket(SendBlacklistPacket.buildPacket(player.actorID, new String[] { "Test" }, ref offset1), true, false));
session.QueuePacket(SendBlacklistPacket.BuildPacket(session.id, new String[] { "Test" }, ref offset1));
break;
case 0x01CC:
AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(FriendlistAddedPacket.buildPacket(player.actorID, true, (uint)addFriendList.name.GetHashCode(), true, addFriendList.name), true, false));
session.QueuePacket(FriendlistAddedPacket.BuildPacket(session.id, true, (uint)addFriendList.name.GetHashCode(), true, addFriendList.name));
break;
case 0x01CD:
AddRemoveSocialPacket removeFriendList = new AddRemoveSocialPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(FriendlistRemovedPacket.buildPacket(player.actorID, true, removeFriendList.name), true, false));
AddRemoveSocialPacket RemoveFriendList = new AddRemoveSocialPacket(subpacket.data);
session.QueuePacket(FriendlistRemovedPacket.BuildPacket(session.id, true, RemoveFriendList.name));
break;
case 0x01CE:
int offset2 = 0;
client.queuePacket(BasePacket.createPacket(SendFriendlistPacket.buildPacket(player.actorID, new Tuple<long, string>[] { new Tuple<long, string>(01, "Test2") }, ref offset2), true, false));
session.QueuePacket(SendFriendlistPacket.BuildPacket(session.id, new Tuple<long, string>[] { new Tuple<long, string>(01, "Test2") }, ref offset2));
break;
case 0x01CF:
client.queuePacket(BasePacket.createPacket(FriendStatusPacket.buildPacket(player.actorID, null), true, false));
session.QueuePacket(FriendStatusPacket.BuildPacket(session.id, null));
break;
/* SUPPORT DESK STUFF */
//Request for FAQ/Info List
case 0x01D0:
FaqListRequestPacket faqRequest = new FaqListRequestPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(FaqListResponsePacket.buildPacket(player.actorID, new string[] { "Testing FAQ1", "Coded style!" }), true, false));
session.QueuePacket(FaqListResponsePacket.BuildPacket(session.id, new string[] { "Testing FAQ1", "Coded style!" }));
break;
//Request for body of a faq/info selection
case 0x01D1:
FaqBodyRequestPacket faqBodyRequest = new FaqBodyRequestPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(FaqBodyResponsePacket.buildPacket(player.actorID, "HERE IS A GIANT BODY. Nothing else to say!"), true, false));
session.QueuePacket(FaqBodyResponsePacket.BuildPacket(session.id, "HERE IS A GIANT BODY. Nothing else to say!"));
break;
//Request issue list
case 0x01D2:
GMTicketIssuesRequestPacket issuesRequest = new GMTicketIssuesRequestPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(IssueListResponsePacket.buildPacket(player.actorID, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5" }), true, false));
session.QueuePacket(IssueListResponsePacket.BuildPacket(session.id, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5" }));
break;
//Request if GM ticket exists
case 0x01D3:
client.queuePacket(BasePacket.createPacket(StartGMTicketPacket.buildPacket(player.actorID, false), true, false));
session.QueuePacket(StartGMTicketPacket.BuildPacket(session.id, false));
break;
//Request for GM response message
case 0x01D4:
client.queuePacket(BasePacket.createPacket(GMTicketPacket.buildPacket(player.actorID, "This is a GM Ticket Title", "This is a GM Ticket Body."), true, false));
session.QueuePacket(GMTicketPacket.BuildPacket(session.id, "This is a GM Ticket Title", "This is a GM Ticket Body."));
break;
//GM Ticket Sent
case 0x01D5:
GMSupportTicketPacket gmTicket = new GMSupportTicketPacket(subpacket.data);
Program.Log.Info("Got GM Ticket: \n" + gmTicket.ticketTitle + "\n" + gmTicket.ticketBody);
session.QueuePacket(GMTicketSentResponsePacket.BuildPacket(session.id, true));
break;
//Request to end ticket
case 0x01D6:
client.queuePacket(BasePacket.createPacket(EndGMTicketPacket.buildPacket(player.actorID), true, false));
session.QueuePacket(EndGMTicketPacket.BuildPacket(session.id));
break;
default:
Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode));
subpacket.debugPrintSubPacket();
Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode);
subpacket.DebugPrintSubPacket();
break;
}
}
else
packet.debugPrintPacket();
}
}
public void sendPacket(string path, int conn)
{
BasePacket packet = new BasePacket(path);
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mPlayers)
{
packet.replaceActorID(entry.Value.actorID);
if (conn == 1 || conn == 3)
entry.Value.getConnection1().queuePacket(packet);
if (conn == 2 || conn == 3)
entry.Value.getConnection2().queuePacket(packet);
}
}
/*
public Actor findActor(ConnectedPlayer player, uint id)
{
Actor ownerActor = mStaticActors[id];
if (ownerActor == null)
{
foreach (Actor a in player.actorInstanceList)
{
if (a.actorID == player.eventCurrentOwner)
{
ownerActor = a;
break;
}
}
if (ownerActor == null)
return null;
}
return ownerActor;
}
*/
private void initNpcs()
{
List<Npc> npcList = Database.getNpcList();
foreach (Npc npc in npcList)
inn.addActorToZone(npc);
Log.info(String.Format("Loaded {0} npcs...", npcList.Count));
}
private void loadTest(ClientConnection client, ConnectedPlayer player)
{
string sequence = "6789abcdefghijklmnopqrsuvwxy";
//10 for just login
for (int i = 7; i < sequence.Length; i++)
{
BasePacket packet = new BasePacket("./packets/tt2/" + sequence[i]);
packet.replaceActorID(player.actorID);
client.queuePacket(packet);
}
}
public void doWarp(uint mapID, float x, float y, float z)
{
List<SubPacket> pList = new List<SubPacket>();
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mPlayers)
{
pList.Clear();
entry.Value.getActor().positionX = x;
entry.Value.getActor().positionY = y;
entry.Value.getActor().positionZ = z;
pList.Add(_0xE2Packet.buildPacket(0x6c, 0xF));
pList.Add(SetMapPacket.buildPacket(0x6c, mapID, 0));
BasePacket packet = BasePacket.createPacket(pList, true, false);
BasePacket actorPacket = entry.Value.getActor().getInitPackets(entry.Value.actorID);
packet.replaceActorID(entry.Value.actorID);
actorPacket.replaceActorID(entry.Value.actorID);
entry.Value.getConnection2().queuePacket(packet);
entry.Value.getConnection2().queuePacket(actorPacket);
}
}
}
}

View File

@@ -1,109 +1,75 @@
using FFXIVClassic_Lobby_Server.packets;
using System;
using System;
using System.Diagnostics;
using System.Threading;
using FFXIVClassic_Lobby_Server.common;
using System.Runtime.InteropServices;
using System.Text;
using MySql.Data.MySqlClient;
using System.Reflection;
using FFXIVClassic_Lobby_Server.dataobjects;
using System.IO;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic.Common;
using NLog;
using NLog.Targets;
using NLog.Targets.Wrappers;
using NLog.Config;
namespace FFXIVClassic_Lobby_Server
namespace FFXIVClassic_Map_Server
{
class Program
{
public static Logger Log;
static void Main(string[] args)
{
// set up logging
Log = LogManager.GetCurrentClassLogger();
#if DEBUG
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter);
#endif
bool startServer = true;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("---------FFXIV 1.0 Map Server---------");
Console.ForegroundColor = ConsoleColor.Gray;
Assembly assem = Assembly.GetExecutingAssembly();
Version vers = assem.GetName().Version;
Console.WriteLine("Version: " + vers.ToString());
Log.Info("==================================");
Log.Info("FFXIV Classic Map Server");
Log.Info("Version: 0.1");
Log.Info("==================================");
//Load Config
if (!ConfigConstants.load())
startServer = false;
ConfigConstants.Load();
ConfigConstants.ApplyLaunchArgs(args);
//Test DB Connection
Console.Write("Testing DB connection... ");
Program.Log.Info("Testing DB connection... ");
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
conn.Close();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("[OK]");
Console.ForegroundColor = ConsoleColor.Gray;
Program.Log.Info("Connection ok.");
}
catch (MySqlException e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[FAILED]");
Console.ForegroundColor = ConsoleColor.Gray;
Program.Log.Error(e.ToString());
startServer = false;
}
}
//Check World ID
DBWorld thisWorld = Database.getServer(ConfigConstants.DATABASE_WORLDID);
if (thisWorld != null)
Console.WriteLine("Successfully pulled world info from DB. Server name is {0}.", thisWorld.name);
else
Console.WriteLine("World info could not be retrieved from the DB. Welcome and MOTD will not be displayed.");
//Start server if A-OK
if (startServer)
{
Server server = new Server();
server.startServer();
while (true)
server.StartServer();
while (startServer)
{
String input = Console.ReadLine();
String[] split = input.Split(' ');
if (split.Length >= 3)
{
if (split[0].Equals("sendpacket"))
{
try{
server.sendPacket("./packets/" + split[1], Int32.Parse(split[2]));
}
catch (Exception e)
{
Log.error("Could not load packet: " + e);
}
}
else if (split[0].Equals("warp"))
{
server.doWarp(split[1], split[2], split[3], split[4]);
}
else if (split[0].Equals("property"))
{
server.testCodePacket(Utils.MurmurHash2(split[1], 0), Convert.ToUInt32(split[2], 16), split[3]);
}
else if (split[0].Equals("test"))
{
server.testCodePacket2(split[1], split[2]);
}
}
Log.Info("[Console Input] " + input);
Server.GetCommandProcessor().DoCommand(input, null);
}
}
Console.WriteLine("Press any key to continue...");
Program.Log.Info("Press any key to continue...");
Console.ReadKey();
}

View File

@@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following

View File

@@ -0,0 +1,270 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace FFXIVClassic_Map_Server.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or Remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FFXIVClassic_Map_Server.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Adds the specified currency to the current player&apos;s inventory
///
///*Syntax: givecurrency &lt;quantity&gt;
/// givecurrency &lt;type&gt; &lt;quantity&gt;
///&lt;type&gt; is the specific type of currency desired, defaults to gil if no type specified.
/// </summary>
public static string CPgivecurrency {
get {
return ResourceManager.GetString("CPgivecurrency", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the specified items to the current player&apos;s inventory
///
///*Syntax: giveitem &lt;item id&gt;
/// giveitem &lt;item id&gt; &lt;quantity&gt;
/// giveitem &lt;item id&gt; &lt;quantity&gt; &lt;type&gt;
///&lt;item id&gt; is the item&apos;s specific id as defined in the server database
///&lt;type&gt; is the type as defined in the server database (defaults to standard item if not specified).
/// </summary>
public static string CPgiveitem {
get {
return ResourceManager.GetString("CPgiveitem", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adds the specified key item to the current player&apos;s inventory
///
///*Syntax: givekeyitem &lt;item id&gt;
///&lt;item id&gt; is the key item&apos;s specific id as defined in the server database.
/// </summary>
public static string CPgivekeyitem {
get {
return ResourceManager.GetString("CPgivekeyitem", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use !help(command) for details
///
///Available commands:
///Standard: mypos, music, warp
///Server Administration: givecurrency, giveitem, givekeyitem, Removecurrency, Removekeyitem, reloaditems, reloadzones
///Test: test weather.
/// </summary>
public static string CPhelp {
get {
return ResourceManager.GetString("CPhelp", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Changes the currently playing background music
///
///*Syntax: music &lt;music id&gt;
///&lt;music id&gt; is the music&apos;s specific id as defined in the client.
/// </summary>
public static string CPmusic {
get {
return ResourceManager.GetString("CPmusic", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prints out your current location
///
///*Note: The X/Y/Z coordinates Do not correspond to the coordinates listed in the in-game map, they are based on the underlying game data.
/// </summary>
public static string CPmypos {
get {
return ResourceManager.GetString("CPmypos", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to *Syntax: property &lt;value 1&gt; &lt;value 2&gt; &lt;value 3&gt;.
/// </summary>
public static string CPproperty {
get {
return ResourceManager.GetString("CPproperty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to *Syntax: property2 &lt;value 1&gt; &lt;value 2&gt; &lt;value 3&gt;.
/// </summary>
public static string CPproperty2 {
get {
return ResourceManager.GetString("CPproperty2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reloads the current item data from the database.
/// </summary>
public static string CPreloaditems {
get {
return ResourceManager.GetString("CPreloaditems", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reloads the current zone data from the database.
/// </summary>
public static string CPreloadzones {
get {
return ResourceManager.GetString("CPreloadzones", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Removes the specified currency from the current player&apos;s inventory
///
///*Syntax: Removecurrency &lt;quantity&gt;
/// Removecurrency &lt;type&gt; &lt;quantity&gt;
///&lt;type&gt; is the specific type of currency desired, defaults to gil if no type specified.
/// </summary>
public static string CPRemovecurrency {
get {
return ResourceManager.GetString("CPRemovecurrency", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Removes the specified items to the current player&apos;s inventory
///
///*Syntax: Removeitem &lt;itemid&gt;
/// Removeitem &lt;itemid&gt; &lt;quantity&gt;
///&lt;item id&gt; is the item&apos;s specific id as defined in the server database.
/// </summary>
public static string CPRemoveitem {
get {
return ResourceManager.GetString("CPRemoveitem", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Removes the specified key item to the current player&apos;s inventory
///
///*Syntax: Removekeyitem &lt;itemid&gt;
///&lt;item id&gt; is the key item&apos;s specific id as defined in the server database.
/// </summary>
public static string CPRemovekeyitem {
get {
return ResourceManager.GetString("CPRemovekeyitem", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Server sends a special packet to the client
///
///*Syntax: sendpacket &lt;path to packet&gt;
///&lt;Path to packet&gt; is the path to the packet, starting in &lt;map server install location&gt;\packet.
/// </summary>
public static string CPsendpacket {
get {
return ResourceManager.GetString("CPsendpacket", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Overrides the currently displayed character equipment in a specific slot
///
///*Note: Similar to Glamours in FFXIV:ARR, the overridden graphics are purely cosmetic, they Do not affect the underlying stats of whatever is equipped on that slot
///
///*Syntax: sendpacket &lt;slot&gt; &lt;wid&gt; &lt;eid&gt; &lt;vid&gt; &lt;cid&gt;
///&lt;w/e/v/c id&gt; are as defined in the client game data.
/// </summary>
public static string CPsetgraphic {
get {
return ResourceManager.GetString("CPsetgraphic", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Changes the current weather
///
///*Syntax: test weather &lt;weather id&gt;
///&lt;weather id&gt; is the weather&apos;s specific id as defined in the client.
/// </summary>
public static string CPtestweather {
get {
return ResourceManager.GetString("CPtestweather", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Teleports the player to the specified location
///
///*Note: You can teleport relative to your current position by putting a @ in front of a value, cannot be combined with a zone id or instance name
///
///*Syntax: warp &lt;location list&gt;
/// warp &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
/// warp &lt;zone id&gt; &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
/// warp &lt;zone id&gt; &lt;instance&gt; &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
///&lt;location list&gt; is a pre-defined list of locations from the server database
///&lt;zone id&gt; is the [rest of string was truncated]&quot;;.
/// </summary>
public static string CPwarp {
get {
return ResourceManager.GetString("CPwarp", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are Done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that Don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CPgivecurrency" xml:space="preserve">
<value>Adds the specified currency to the current player's inventory
*Syntax: givecurrency &lt;quantity&gt;
givecurrency &lt;type&gt; &lt;quantity&gt;
&lt;type&gt; is the specific type of currency desired, defaults to gil if no type specified</value>
</data>
<data name="CPgiveitem" xml:space="preserve">
<value>Adds the specified items to the current player's inventory
*Syntax: giveitem &lt;item id&gt;
giveitem &lt;item id&gt; &lt;quantity&gt;
giveitem &lt;item id&gt; &lt;quantity&gt; &lt;type&gt;
&lt;item id&gt; is the item's specific id as defined in the server database
&lt;type&gt; is the type as defined in the server database (defaults to standard item if not specified)</value>
</data>
<data name="CPgivekeyitem" xml:space="preserve">
<value>Adds the specified key item to the current player's inventory
*Syntax: givekeyitem &lt;item id&gt;
&lt;item id&gt; is the key item's specific id as defined in the server database</value>
</data>
<data name="CPhelp" xml:space="preserve">
<value>Use !help(command) for details
Available commands:
Standard: mypos, music, warp
Server Administration: givecurrency, giveitem, givekeyitem, removecurrency, removekeyitem, reloaditems, reloadzones
Test: test weather</value>
</data>
<data name="CPmusic" xml:space="preserve">
<value>Changes the currently playing background music
*Syntax: music &lt;music id&gt;
&lt;music id&gt; is the music's specific id as defined in the client</value>
</data>
<data name="CPmypos" xml:space="preserve">
<value>Prints out your current location
*Note: The X/Y/Z coordinates Do not correspond to the coordinates listed in the in-game map, they are based on the underlying game data</value>
</data>
<data name="CPproperty" xml:space="preserve">
<value>*Syntax: property &lt;value 1&gt; &lt;value 2&gt; &lt;value 3&gt;</value>
</data>
<data name="CPproperty2" xml:space="preserve">
<value>*Syntax: property2 &lt;value 1&gt; &lt;value 2&gt; &lt;value 3&gt;</value>
</data>
<data name="CPreloaditems" xml:space="preserve">
<value>Reloads the current item data from the database</value>
</data>
<data name="CPreloadzones" xml:space="preserve">
<value>Reloads the current zone data from the database</value>
</data>
<data name="CPremovecurrency" xml:space="preserve">
<value>Removes the specified currency from the current player's inventory
*Syntax: removecurrency &lt;quantity&gt;
removecurrency &lt;type&gt; &lt;quantity&gt;
&lt;type&gt; is the specific type of currency desired, defaults to gil if no type specified</value>
</data>
<data name="CPremoveitem" xml:space="preserve">
<value>Removes the specified items to the current player's inventory
*Syntax: removeitem &lt;itemid&gt;
removeitem &lt;itemid&gt; &lt;quantity&gt;
&lt;item id&gt; is the item's specific id as defined in the server database</value>
</data>
<data name="CPremovekeyitem" xml:space="preserve">
<value>Removes the specified key item to the current player's inventory
*Syntax: removekeyitem &lt;itemid&gt;
&lt;item id&gt; is the key item's specific id as defined in the server database</value>
</data>
<data name="CPsendpacket" xml:space="preserve">
<value>Server sends a special packet to the client
*Syntax: sendpacket &lt;path to packet&gt;
&lt;Path to packet&gt; is the path to the packet, starting in &lt;map server install location&gt;\packet</value>
</data>
<data name="CPsetgraphic" xml:space="preserve">
<value>Overrides the currently displayed character equipment in a specific slot
*Note: Similar to Glamours in FFXIV:ARR, the overridden graphics are purely cosmetic, they Do not affect the underlying stats of whatever is equipped on that slot
*Syntax: sendpacket &lt;slot&gt; &lt;wid&gt; &lt;eid&gt; &lt;vid&gt; &lt;cid&gt;
&lt;w/e/v/c id&gt; are as defined in the client game data</value>
</data>
<data name="CPtestweather" xml:space="preserve">
<value>Changes the current weather
*Syntax: test weather &lt;weather id&gt;
&lt;weather id&gt; is the weather's specific id as defined in the client</value>
</data>
<data name="CPwarp" xml:space="preserve">
<value>Teleports the player to the specified location
*Note: You can teleport relative to your current position by putting a @ in front of a value, cannot be combined with a zone id or instance name
*Syntax: warp &lt;location list&gt;
warp &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
warp &lt;zone id&gt; &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
warp &lt;zone id&gt; &lt;instance&gt; &lt;X coordinate&gt; &lt;Y coordinate&gt; &lt;Z coordinate&gt;
&lt;location list&gt; is a pre-defined list of locations from the server database
&lt;zone id&gt; is the zone's id as defined in the server database
&lt;instance&gt; is an instanced copy of the desired zone that's only visible to the current player</value>
</data>
</root>

View File

@@ -1,44 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Lobby_Server.packets;
using System.IO;
using FFXIVClassic_Map_Server.packets.send.actor;
namespace FFXIVClassic_Lobby_Server
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
namespace FFXIVClassic_Map_Server
{
class Server
{
public const int FFXIV_MAP_PORT = 54992;
public const int BUFFER_SIZE = 0x400;
public const int BUFFER_SIZE = 0xFFFF; //Max basepacket size is 0xFFFF
public const int BACKLOG = 100;
public const string STATIC_ACTORS_PATH = "./staticactors.bin";
private static Server mSelf;
private Socket mServerSocket;
private Dictionary<uint,ConnectedPlayer> mConnectedPlayerList = new Dictionary<uint,ConnectedPlayer>();
private List<ClientConnection> mConnectionList = new List<ClientConnection>();
private Dictionary<uint, Session> mSessionList = new Dictionary<uint, Session>();
private static CommandProcessor mCommandProcessor = new CommandProcessor();
private static ZoneConnection mWorldConnection = new ZoneConnection();
private static WorldManager mWorldManager;
private static Dictionary<uint, ItemData> mGamedataItems;
private static Dictionary<uint, GuildleveData> mGamedataGuildleves;
private static StaticActors mStaticActors;
private PacketProcessor mProcessor;
private Thread mProcessorThread;
private Thread mGameThread;
#region Socket Handling
public bool startServer()
public Server()
{
IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), FFXIV_MAP_PORT);
mSelf = this;
}
try{
mServerSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
public bool StartServer()
{
mStaticActors = new StaticActors(STATIC_ACTORS_PATH);
mGamedataItems = Database.GetItemGamedata();
Program.Log.Info("Loaded {0} items.", mGamedataItems.Count);
mGamedataGuildleves = Database.GetGuildleveGamedata();
Program.Log.Info("Loaded {0} guildleves.", mGamedataGuildleves.Count);
mWorldManager = new WorldManager(this);
mWorldManager.LoadZoneList();
mWorldManager.LoadZoneEntranceList();
mWorldManager.LoadSeamlessBoundryList();
mWorldManager.LoadActorClasses();
mWorldManager.LoadSpawnLocations();
mWorldManager.SpawnAllActors();
mWorldManager.StartZoneThread();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), int.Parse(ConfigConstants.OPTIONS_PORT));
try
{
mServerSocket = new Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
catch (Exception e)
{
throw new ApplicationException("Could not create socket, check to make sure not duplicating port", e);
throw new ApplicationException("Could not Create socket, check to make sure not duplicating port", e);
}
try
{
@@ -51,70 +77,105 @@ namespace FFXIVClassic_Lobby_Server
}
try
{
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception e)
{
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
}
Console.Write("Game server has started @ ");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Program.Log.Info("Map Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Console.ForegroundColor = ConsoleColor.Gray;
mProcessor = new PacketProcessor(mConnectedPlayerList, mConnectionList);
mProcessor = new PacketProcessor(this);
//mGameThread = new Thread(new ThreadStart(mProcessor.update));
//mGameThread.Start();
return true;
}
private void acceptCallback(IAsyncResult result)
#region Session Handling
public Session AddSession(uint id)
{
ClientConnection conn = null;
if (mSessionList.ContainsKey(id))
return mSessionList[id];
Session session = new Session(id);
mSessionList.Add(id, session);
return session;
}
public void RemoveSession(uint id)
{
if (mSessionList.ContainsKey(id))
{
mSessionList.Remove(id);
}
}
public Session GetSession(uint id)
{
if (mSessionList.ContainsKey(id))
return mSessionList[id];
else
return null;
}
public Session GetSession(string name)
{
foreach (Session s in mSessionList.Values)
{
if (s.GetActor().customDisplayName.ToLower().Equals(name.ToLower()))
return s;
}
return null;
}
public Dictionary<uint, Session> GetSessionList()
{
return mSessionList;
}
#endregion
#region Socket Handling
private void AcceptCallback(IAsyncResult result)
{
ZoneConnection conn = null;
Socket socket = (System.Net.Sockets.Socket)result.AsyncState;
try
{
conn = new ClientConnection();
conn = new ZoneConnection();
conn.socket = socket.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList)
{
mConnectionList.Add(conn);
}
mWorldConnection = conn;
Log.conn(String.Format("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port));
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn);
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (SocketException)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
mWorldConnection = null;
}
}
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
mWorldConnection = null;
}
}
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
}
@@ -122,153 +183,122 @@ namespace FFXIVClassic_Lobby_Server
/// Receive Callback. Reads in incoming data, converting them to base packets. Base packets are sent to be parsed. If not enough data at the end to build a basepacket, move to the beginning and prepend.
/// </summary>
/// <param name="result"></param>
private void receiveCallback(IAsyncResult result)
private void ReceiveCallback(IAsyncResult result)
{
ClientConnection conn = (ClientConnection)result.AsyncState;
ZoneConnection conn = (ZoneConnection)result.AsyncState;
//Check if disconnected
if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0))
{
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
try
{
int bytesRead = conn.socket.EndReceive(result);
if (bytesRead > 0)
bytesRead += conn.lastPartialSize;
if (bytesRead >= 0)
{
int offset = 0;
//Build packets until can no longer or out of data
while (true)
{
BasePacket basePacket = buildPacket(ref offset, conn.buffer, bytesRead);
SubPacket subPacket = SubPacket.CreatePacket(ref offset, conn.buffer, bytesRead);
//If can't build packet, break, else process another
if (basePacket == null)
if (subPacket == null)
break;
else
mProcessor.processPacket(conn, basePacket);
mProcessor.ProcessPacket(conn, subPacket);
}
//Not all bytes consumed, transfer leftover to beginning
if (offset < bytesRead)
Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset);
conn.lastPartialSize = bytesRead - offset;
//Build any queued subpackets into basepackets and send
conn.flushQueuedSendPackets();
conn.FlushQueuedSendPackets();
if (offset < bytesRead)
//Need offset since not all bytes consumed
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(receiveCallback), conn);
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
else
//All bytes consumed, full buffer available
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn);
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
}
else
{
Log.conn(String.Format("{0} has disconnected.", conn.owner == 0 ? conn.getAddress() : "User " + conn.owner));
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
}
catch (SocketException)
{
if (conn.socket != null)
{
Log.conn(String.Format("{0} has disconnected.", conn.owner == 0 ? conn.getAddress() : "User " + conn.owner));
lock (mConnectionList)
{
mConnectionList.Remove(conn);
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
}
}
}
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public BasePacket buildPacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
#endregion
public void sendPacket(string path, int conn)
public static ZoneConnection GetWorldConnection()
{
mProcessor.sendPacket(path, conn);
return mWorldConnection;
}
public void testCodePacket(uint id, uint value, string target)
public static Server GetServer()
{
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket();
changeProperty.addInt(id, value);
changeProperty.setTarget(target);
return mSelf;
}
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
public static CommandProcessor GetCommandProcessor()
{
SubPacket changePropertyPacket = changeProperty.buildPacket((entry.Value.actorID), (entry.Value.actorID));
BasePacket packet = BasePacket.createPacket(changePropertyPacket, true, false);
packet.debugPrintPacket();
if (entry.Value.getConnection1() != null)
entry.Value.getConnection1().queuePacket(packet);
return mCommandProcessor;
}
public static WorldManager GetWorldManager()
{
return mWorldManager;
}
public static Dictionary<uint, ItemData> GetGamedataItems()
{
return mGamedataItems;
}
public static Actor GetStaticActors(uint id)
{
return mStaticActors.GetActor(id);
}
public static Actor GetStaticActors(string name)
{
return mStaticActors.FindStaticActor(name);
}
public static ItemData GetItemGamedata(uint id)
{
if (mGamedataItems.ContainsKey(id))
return mGamedataItems[id];
else
Log.error("Connection was null");
if (entry.Value.getConnection2() != null)
entry.Value.getConnection2().queuePacket(packet);
return null;
}
public static GuildleveData GetGuildleveGamedata(uint id)
{
if (mGamedataGuildleves.ContainsKey(id))
return mGamedataGuildleves[id];
else
Log.error("Connection was null");
}
}
public void testCodePacket2(string name, string target)
{
foreach (KeyValuePair<uint, ConnectedPlayer> entry in mConnectedPlayerList)
{
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket();
changeProperty.addProperty(entry.Value.getActor(), name);
changeProperty.addProperty(entry.Value.getActor(), "charaWork.parameterSave.hpMax[0]");
changeProperty.setTarget(target);
SubPacket changePropertyPacket = changeProperty.buildPacket((entry.Value.actorID), (entry.Value.actorID));
BasePacket packet = BasePacket.createPacket(changePropertyPacket, true, false);
packet.debugPrintPacket();
entry.Value.getConnection1().queuePacket(packet);
entry.Value.getConnection2().queuePacket(packet);
}
}
public void doWarp(String map, String x, String y, String z)
{
if (map.ToLower().StartsWith("0x"))
mProcessor.doWarp(Convert.ToUInt32(map, 16), Single.Parse(x), Single.Parse(y), Single.Parse(z));
else
mProcessor.doWarp(Convert.ToUInt32(map), Single.Parse(x), Single.Parse(y), Single.Parse(z));
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,186 +0,0 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Map_Server.dataobjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server
{
class Zone
{
public uint mapId;
public ushort weatherNormal, weatherCommon, weatherRare;
public ushort bgmDay, bgmNight, bgmBattle;
public int boundingGridSize = 50;
public int minX = -100, minY = -100, maxX = 100, maxY = 100;
private int numXBlocks, numYBlocks;
private int halfWidth, halfHeight;
private List<Actor>[,] actorBlock;
public Zone()
{
numXBlocks = (maxX - minX) / boundingGridSize;
numYBlocks = (maxY - minY) / boundingGridSize;
actorBlock = new List<Actor>[numXBlocks, numYBlocks];
halfWidth = numXBlocks / 2;
halfHeight = numYBlocks / 2;
for (int y = 0; y < numYBlocks; y++)
{
for (int x = 0; x < numXBlocks; x++ )
{
actorBlock[x, y] = new List<Actor>();
}
}
}
#region Actor Management
public void addActorToZone(Actor actor)
{
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
lock (actorBlock)
actorBlock[gridX, gridY].Add(actor);
}
public void removeActorToZone(Actor actor)
{
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
lock (actorBlock)
actorBlock[gridX, gridY].Remove(actor);
}
public void updateActorPosition(Actor actor)
{
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
int gridOldX = (int)actor.oldPositionX / boundingGridSize;
int gridOldY = (int)actor.oldPositionZ / boundingGridSize;
gridOldX += halfWidth;
gridOldY += halfHeight;
//Boundries
if (gridOldX < 0)
gridOldX = 0;
if (gridOldX >= numXBlocks)
gridOldX = numXBlocks - 1;
if (gridOldY < 0)
gridOldY = 0;
if (gridOldY >= numYBlocks)
gridOldY = numYBlocks - 1;
//Still in same block
if (gridX == gridOldX && gridY == gridOldY)
return;
lock (actorBlock)
actorBlock[gridOldX, gridOldY].Remove(actor);
lock (actorBlock)
actorBlock[gridX, gridY].Add(actor);
}
public List<Actor> getActorsAroundPoint(float x, float y, int checkDistance)
{
int gridX = (int)x/boundingGridSize;
int gridY = (int)y/boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
List<Actor> result = new List<Actor>();
for (int gx = gridX - checkDistance; gx <= gridX + checkDistance; gx++)
{
for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++)
{
result.AddRange(actorBlock[gx, gy]);
}
}
return result;
}
public List<Actor> getActorsAroundActor(Actor actor, int checkDistance)
{
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
List<Actor> result = new List<Actor>();
for (int gy = ((gridY - checkDistance) < 0 ? 0 : (gridY - checkDistance)); gy <= ((gridY + checkDistance) >= numYBlocks ? numYBlocks - 1 : (gridY + checkDistance)); gy++)
{
for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++)
{
result.AddRange(actorBlock[gx, gy]);
}
}
return result;
}
#endregion
}
}

View File

@@ -1,20 +1,17 @@
using FFXIVClassic_Lobby_Server;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.dataobjects;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.dataobjects.chara;

using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.packets.send.Actor;
using FFXIVClassic_Map_Server.packets.send.actor.events;
using FFXIVClassic.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FFXIVClassic_Map_Server.actors.area;
using System.Reflection;
using System.Text;
using System.ComponentModel;
using FFXIVClassic_Map_Server.packets.send.actor.battle;
namespace FFXIVClassic_Map_Server.dataobjects
namespace FFXIVClassic_Map_Server.Actors
{
class Actor
{
@@ -26,84 +23,277 @@ namespace FFXIVClassic_Map_Server.dataobjects
public ushort currentMainState = SetActorStatePacket.MAIN_STATE_PASSIVE;
public ushort currentSubState = SetActorStatePacket.SUB_STATE_NONE;
public float positionX = SetActorPositionPacket.INNPOS_X, positionY = SetActorPositionPacket.INNPOS_Y, positionZ = SetActorPositionPacket.INNPOS_Z, rotation = SetActorPositionPacket.INNPOS_ROT;
public float positionX, positionY, positionZ, rotation;
public float oldPositionX, oldPositionY, oldPositionZ, oldRotation;
public ushort moveState, oldMoveState;
public float[] moveSpeeds = new float[4];
public uint currentZoneId;
public uint zoneId, zoneId2;
public string privateArea;
public uint privateAreaType;
public Area zone = null;
public Area zone2 = null;
public bool isZoning = false;
public bool spawnedFirstTime = false;
public string classPath;
public string className;
public List<LuaParam> classParams;
public Actor(uint Id)
public EventList eventConditions;
public Actor(uint actorId)
{
actorId = Id;
this.actorId = actorId;
}
public SubPacket createAddActorPacket(uint playerActorId)
public Actor(uint actorId, string actorName, string className, List<LuaParam> classParams)
{
return AddActorPacket.buildPacket(actorId, playerActorId, 0);
this.actorId = actorId;
this.actorName = actorName;
this.className = className;
this.classParams = classParams;
this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP;
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
}
public SubPacket createNamePacket(uint playerActorId)
public void SetPushCircleRange(string triggerName, float size)
{
return SetActorNamePacket.buildPacket(actorId, playerActorId, displayNameId, displayNameId == 0xFFFFFFFF ? customDisplayName : "");
if (eventConditions == null || eventConditions.pushWithCircleEventConditions == null)
return;
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
{
if (condition.conditionName.Equals(triggerName))
{
condition.radius = size;
break;
}
}
}
public SubPacket createSpeedPacket(uint playerActorId)
public SubPacket CreateAddActorPacket(byte val)
{
return SetActorSpeedPacket.buildPacket(actorId, playerActorId);
return AddActorPacket.BuildPacket(actorId, val);
}
public SubPacket createSpawnPositonPacket(uint playerActorId, uint spawnType)
public SubPacket CreateNamePacket()
{
return SetActorNamePacket.BuildPacket(actorId, displayNameId, displayNameId == 0xFFFFFFFF | displayNameId == 0x0 ? customDisplayName : "");
}
public SubPacket CreateSpeedPacket()
{
return SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]);
}
public SubPacket CreateSpawnPositonPacket(ushort spawnType)
{
return CreateSpawnPositonPacket(null, spawnType);
}
public SubPacket CreateSpawnPositonPacket(Player player, ushort spawnType)
{
//TODO: FIX THIS IF
uint playerActorId = player == null ? 0 : player.actorId; //Get Rid
SubPacket spawnPacket;
if (!spawnedFirstTime && playerActorId == actorId)
spawnPacket = SetActorPositionPacket.buildPacket(actorId, playerActorId, 0, positionX, positionY, positionZ, rotation, spawnType, false);
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, 0x1, false);
else if (playerActorId == actorId)
spawnPacket = SetActorPositionPacket.buildPacket(actorId, playerActorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, true);
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, true);
else
spawnPacket = SetActorPositionPacket.buildPacket(actorId, playerActorId, actorId, positionX, positionY, positionZ, rotation, spawnType, false);
{
if (this is Player)
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, spawnType, false);
else
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, actorId, positionX, positionY, positionZ, rotation, spawnType, false);
}
//return SetActorPositionPacket.buildPacket(actorId, playerActorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
spawnedFirstTime = true;
return spawnPacket;
}
public SubPacket createPositionUpdatePacket(uint playerActorId)
public SubPacket CreateSpawnTeleportPacket(ushort spawnType)
{
return MoveActorToPositionPacket.buildPacket(actorId, playerActorId, positionX, positionY, positionZ, rotation, moveState);
SubPacket spawnPacket;
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, false);
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
//spawnPacket.DebugPrintSubPacket();
return spawnPacket;
}
public SubPacket createStatePacket(uint playerActorID)
public SubPacket CreatePositionUpdatePacket()
{
return SetActorStatePacket.buildPacket(actorId, playerActorID, currentMainState, currentSubState);
return MoveActorToPositionPacket.BuildPacket(actorId, positionX, positionY, positionZ, rotation, moveState);
}
public SubPacket createIsZoneingPacket(uint playerActorId)
public SubPacket CreateStatePacket()
{
return SetActorIsZoningPacket.buildPacket(actorId, playerActorId, false);
return SetActorStatePacket.BuildPacket(actorId, currentMainState, currentSubState);
}
public virtual SubPacket createScriptBindPacket(uint playerActorId)
{
return null;
}
public virtual BasePacket getInitPackets(uint playerActorId)
public List<SubPacket> GetEventConditionPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(createAddActorPacket(playerActorId));
subpackets.Add(createSpeedPacket(playerActorId));
subpackets.Add(createSpawnPositonPacket(playerActorId, 0x1));
subpackets.Add(createNamePacket(playerActorId));
subpackets.Add(createStatePacket(playerActorId));
subpackets.Add(createIsZoneingPacket(playerActorId));
return BasePacket.createPacket(subpackets, true, false);
//Return empty list
if (eventConditions == null)
return subpackets;
if (eventConditions.talkEventConditions != null)
{
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetTalkEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.noticeEventConditions != null)
{
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
subpackets.Add(SetNoticeEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.emoteEventConditions != null)
{
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
subpackets.Add(SetEmoteEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithCircleEventConditions != null)
{
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
subpackets.Add(SetPushEventConditionWithCircle.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithFanEventConditions != null)
{
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
subpackets.Add(SetPushEventConditionWithFan.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithBoxEventConditions != null)
{
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
subpackets.Add(SetPushEventConditionWithTriggerBox.BuildPacket(actorId, condition));
}
return subpackets;
}
public List<SubPacket> GetSetEventStatusPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
//Return empty list
if (eventConditions == null)
return subpackets;
if (eventConditions.talkEventConditions != null)
{
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
}
if (eventConditions.noticeEventConditions != null)
{
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
}
if (eventConditions.emoteEventConditions != null)
{
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 3, condition.conditionName));
}
if (eventConditions.pushWithCircleEventConditions != null)
{
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
if (eventConditions.pushWithFanEventConditions != null)
{
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
if (eventConditions.pushWithBoxEventConditions != null)
{
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
return subpackets;
}
public SubPacket CreateIsZoneingPacket()
{
return SetActorIsZoningPacket.BuildPacket(actorId, false);
}
public virtual SubPacket CreateScriptBindPacket(Player player)
{
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
}
public virtual SubPacket CreateScriptBindPacket()
{
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
}
public virtual List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(8));
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(player, spawnType));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player));
return subpackets;
}
public virtual List<SubPacket> GetSpawnPackets()
{
return GetSpawnPackets(0x1);
}
public virtual List<SubPacket> GetSpawnPackets(ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(8));
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(null, spawnType));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket());
return subpackets;
}
public virtual List<SubPacket> GetInitPackets()
{
List<SubPacket> packets = new List<SubPacket>();
SetActorPropetyPacket initProperties = new SetActorPropetyPacket("/_init");
initProperties.AddByte(0xE14B0CA8, 1);
initProperties.AddByte(0x2138FD71, 1);
initProperties.AddByte(0xFBFBCFB1, 1);
initProperties.AddTarget();
packets.Add(initProperties.BuildPacket(actorId));
return packets;
}
public override bool Equals(Object obj)
@@ -115,21 +305,222 @@ namespace FFXIVClassic_Map_Server.dataobjects
return actorId == actorObj.actorId;
}
public string getName()
public string GetName()
{
return actorName;
}
public string getClassName()
public string GetClassName()
{
return className;
}
public List<LuaParam> getLuaParams()
public ushort GetState()
{
return currentMainState;
}
public List<LuaParam> GetLuaParams()
{
return classParams;
}
public void ChangeState(ushort newState)
{
currentMainState = newState;
SubPacket ChangeStatePacket = SetActorStatePacket.BuildPacket(actorId, newState, currentSubState);
SubPacket battleActionPacket = BattleActionX01Packet.BuildPacket(actorId, actorId, actorId, 0x72000062, 1, 0, 0x05209, 0, 0);
zone.BroadcastPacketAroundActor(this, ChangeStatePacket);
zone.BroadcastPacketAroundActor(this, battleActionPacket);
}
public void ChangeSpeed(int type, float value)
{
moveSpeeds[type] = value;
SubPacket ChangeSpeedPacket = SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]);
zone.BroadcastPacketAroundActor(this, ChangeSpeedPacket);
}
public void ChangeSpeed(float speedStop, float speedWalk, float speedRun, float speedActive)
{
moveSpeeds[0] = speedStop;
moveSpeeds[1] = speedWalk;
moveSpeeds[2] = speedRun;
moveSpeeds[3] = speedActive;
SubPacket ChangeSpeedPacket = SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]);
zone.BroadcastPacketAroundActor(this, ChangeSpeedPacket);
}
public void Update(double deltaTime)
{
}
public void GenerateActorName(int actorNumber)
{
//Format Class Name
string className = this.className.Replace("Populace", "Ppl")
.Replace("Monster", "Mon")
.Replace("Crowd", "Crd")
.Replace("MapObj", "Map")
.Replace("Object", "Obj")
.Replace("Retainer", "Rtn")
.Replace("Standard", "Std");
className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
//Format Zone Name
string zoneName = zone.zoneName.Replace("Field", "Fld")
.Replace("Dungeon", "Dgn")
.Replace("Town", "Twn")
.Replace("Battle", "Btl")
.Replace("Test", "Tes")
.Replace("Event", "Evt")
.Replace("Ship", "Shp")
.Replace("Office", "Ofc");
if (zone is PrivateArea)
{
//Check if "normal"
zoneName = zoneName.Remove(zoneName.Length - 1, 1) + "P";
}
zoneName = Char.ToLowerInvariant(zoneName[0]) + zoneName.Substring(1);
try
{
className = className.Substring(0, 20 - zoneName.Length);
}
catch (ArgumentOutOfRangeException e)
{ }
//Convert actor number to base 63
string classNumber = Utils.ToStringBase63(actorNumber);
//Get stuff after @
uint zoneId = zone.actorId;
uint privLevel = 0;
if (zone is PrivateArea)
privLevel = ((PrivateArea)zone).GetPrivateAreaType();
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
}
public bool SetWorkValue(Player player, string name, string uiFunc, object value)
{
string[] split = name.Split('.');
int arrayIndex = 0;
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork")))
return false;
Object parentObj = null;
Object curObj = this;
for (int i = 0; i < split.Length; i++)
{
//For arrays
if (split[i].Contains("["))
{
if (split[i].LastIndexOf(']') - split[i].IndexOf('[') <= 0)
return false;
arrayIndex = Convert.ToInt32(split[i].Substring(split[i].IndexOf('[') + 1, split[i].LastIndexOf(']') - split[i].LastIndexOf('[') - 1));
split[i] = split[i].Substring(0, split[i].IndexOf('['));
}
FieldInfo field = curObj.GetType().GetField(split[i]);
if (field == null)
return false;
if (i == split.Length - 1)
parentObj = curObj;
curObj = field.GetValue(curObj);
if (curObj == null)
return false;
}
if (curObj == null)
return false;
else
{
//Array, we actually care whats inside
if (curObj.GetType().IsArray)
{
if (((Array)curObj).Length <= arrayIndex)
return false;
if (value.GetType() == ((Array)curObj).GetType().GetElementType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(((Array)curObj).GetType().GetElementType()))
{
if (value.GetType() == ((Array)curObj).GetType().GetElementType())
((Array)curObj).SetValue(value, arrayIndex);
else
((Array)curObj).SetValue(TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType().GetElementType()), arrayIndex);
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
changeProperty.AddProperty(this, name);
changeProperty.AddTarget();
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
player.playerSession.QueuePacket(subpacket);
subpacket.DebugPrintSubPacket();
return true;
}
}
else
{
if (value.GetType() == curObj.GetType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(curObj.GetType()))
{
if (value.GetType() == curObj.GetType())
parentObj.GetType().GetField(split[split.Length - 1]).SetValue(parentObj, value);
else
parentObj.GetType().GetField(split[split.Length-1]).SetValue(parentObj, TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType()));
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
changeProperty.AddProperty(this, name);
changeProperty.AddTarget();
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
player.playerSession.QueuePacket(subpacket);
subpacket.DebugPrintSubPacket();
return true;
}
}
return false;
}
}
public List<float> GetPos()
{
List<float> pos = new List<float>();
pos.Add(positionX);
pos.Add(positionY);
pos.Add(positionZ);
pos.Add(rotation);
pos.Add(zoneId);
return pos;
}
public void SetPos(float x, float y, float z, float rot = 0, uint zoneId = 0)
{
oldPositionX = positionX;
oldPositionY = positionY;
oldPositionZ = positionZ;
oldRotation = rotation;
positionX = x;
positionY = y;
positionZ = z;
rotation = rot;
// todo: handle zone?
zone.BroadcastPacketAroundActor(this, MoveActorToPositionPacket.BuildPacket(actorId, x, y, z, rot, moveState));
}
public Area GetZone()
{
return zone;
}
public uint GetZoneID()
{
return zoneId;
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
namespace FFXIVClassic_Map_Server.actors
{
class EventList
{
public List<TalkEventCondition> talkEventConditions;
public List<NoticeEventCondition> noticeEventConditions;
public List<EmoteEventCondition> emoteEventConditions;
public List<PushCircleEventCondition> pushWithCircleEventConditions;
public List<PushFanEventCondition> pushWithFanEventConditions;
public List<PushBoxEventCondition> pushWithBoxEventConditions;
public class TalkEventCondition
{
public byte unknown1;
public bool isDisabled = false;
public string conditionName;
}
public class NoticeEventCondition
{
public byte unknown1;
public byte unknown2;
public string conditionName;
public NoticeEventCondition(string name, byte unk1, byte unk2)
{
conditionName = name;
unknown1 = unk1;
unknown2 = unk2;
}
}
public class EmoteEventCondition
{
public byte unknown1;
public byte unknown2;
public byte emoteId;
public string conditionName;
}
public class PushCircleEventCondition
{
public string conditionName = "";
public float radius = 30.0f;
public bool outwards = false;
public bool silent = true;
}
public class PushFanEventCondition
{
public string conditionName;
public float radius = 30.0f;
public bool outwards = false;
public bool silent = true;
}
public class PushBoxEventCondition
{
public string conditionName = "";
public float size = 30.0f;
public bool outwards = false;
public bool silent = true;
}
}
}

View File

@@ -1,114 +1,123 @@
using FFXIVClassic_Map_Server.actors.judge;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.dataobjects.actors;
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors
namespace FFXIVClassic_Map_Server.Actors
{
class StaticActors
{
private Dictionary<uint, Actor> mStaticActors = new Dictionary<uint, Actor>();
public StaticActors()
public StaticActors(string path)
{
//Judges
mStaticActors.Add(0xA0F5BAF1, new Judge(0x4BAF1, "JudgeMaster"));
mStaticActors.Add(0xA0F5E201, new Judge(0x4E201, "CommonJudge"));
mStaticActors.Add(0xA0F5E206, new Judge(0x4E206, "TutorialJudge"));
mStaticActors.Add(0xA0F5E20D, new Judge(0x4E20D, "ChocoboJudge"));
mStaticActors.Add(0xA0F50911, new Judge(0x50911, "DeceptionJudge"));
byte[] data = File.ReadAllBytes(path);
//Commands
mStaticActors.Add(0xA0F02EE2, new Command(0xA0F02EE2, "ResetOccupiedCommand"));
mStaticActors.Add(0xA0F02EE3, new Command(0xA0F02EE3, "CombinationManagementCommand"));
mStaticActors.Add(0xA0F02EE4, new Command(0xA0F02EE4, "CombinationStartCommand"));
mStaticActors.Add(0xA0F02EE5, new Command(0xA0F02EE5, "BonusPointCOmmand"));
mStaticActors.Add(0xA0F02EE7, new Command(0xA0F02EE7, "ChangeEquipCommand"));
mStaticActors.Add(0xA0F02EE9, new Command(0xA0F02EE9, "EquipCommand"));
mStaticActors.Add(0xA0F02EEA, new Command(0xA0F02EEA, "EquipAbilityCommand"));
mStaticActors.Add(0xA0F02EEB, new Command(0xA0F02EEB, "PartyTargetCommand"));
mStaticActors.Add(0xA0F02EED, new Command(0xA0F02EED, "EquipPartsShowHideCommand"));
mStaticActors.Add(0xA0F02EEE, new Command(0xA0F02EEE, "ChocoboRideCommand"));
mStaticActors.Add(0xA0F02EEF, new Command(0xA0F02EEF, "ChocoboRideCommand"));
mStaticActors.Add(0xA0F02EF0, new Command(0xA0F02EF0, "ReverseInputOperationCommand"));
mStaticActors.Add(0xA0F02EF1, new Command(0xA0F02EF1, "ChangeJobCommand"));
mStaticActors.Add(0xA0F05209, new Command(0xA0F05209, "ActivateCommand"));
mStaticActors.Add(0xA0F0520A, new Command(0xA0F0520A, "ActivateCommand"));
mStaticActors.Add(0xA0F0520D, new Command(0xA0F0520D, "CommandCancelCommand"));
mStaticActors.Add(0xA0F0520E, new Command(0xA0F0520E, "CommandCancelCommand"));
mStaticActors.Add(0xA0F0520F, new Command(0xA0F0520F, "ItemCommand"));
mStaticActors.Add(0xA0F05210, new Command(0xA0F05210, "AutoAttackTargetChangeCommand"));
mStaticActors.Add(0xA0F055FC, new Command(0xA0F055FC, "CraftCommand"));
mStaticActors.Add(0xA0F055FD, new Command(0xA0F055FD, "CraftCommand"));
mStaticActors.Add(0xA0F055FF, new Command(0xA0F055FF, "CraftCommand"));
mStaticActors.Add(0xA0F05E25, new Command(0xA0F05E25, "TalkCommand"));
mStaticActors.Add(0xA0F05E26, new Command(0xA0F05E26, "EmoteStandardCommand"));
mStaticActors.Add(0xA0F05E28, new Command(0xA0F05E28, "ContinueCommand"));
mStaticActors.Add(0xA0F05E29, new Command(0xA0F05E29, "LoginEventCommand"));
mStaticActors.Add(0xA0F05E8B, new Command(0xA0F05E8B, "PartyInviteCommand"));
mStaticActors.Add(0xA0F05E8C, new Command(0xA0F05E8C, "PartyJoinCommand"));
mStaticActors.Add(0xA0F05E8D, new Command(0xA0F05E8D, "PartyResignCommand"));
mStaticActors.Add(0xA0F05E8E, new Command(0xA0F05E8E, "PartyBreakupCommand"));
mStaticActors.Add(0xA0F05E8F, new Command(0xA0F05E8F, "PartyKickCommand"));
mStaticActors.Add(0xA0F05E90, new Command(0xA0F05E90, "PartyLeaderCommand"));
mStaticActors.Add(0xA0F05E91, new Command(0xA0F05E91, "PartyAcceptCommand"));
mStaticActors.Add(0xA0F05E93, new Command(0xA0F05E93, "RequestQuestJournalCommand"));
mStaticActors.Add(0xA0F05E94, new Command(0xA0F05E94, "RequestInformationCommand"));
mStaticActors.Add(0xA0F05E95, new Command(0xA0F05E95, "NpcLinkshellChatCommand"));
mStaticActors.Add(0xA0F05E96, new Command(0xA0F05E96, "BazaarDealCommand"));
mStaticActors.Add(0xA0F05E97, new Command(0xA0F05E97, "BazaarCheckCommand"));
mStaticActors.Add(0xA0F05E98, new Command(0xA0F05E98, "BazaarUndealCommand"));
mStaticActors.Add(0xA0F05E99, new Command(0xA0F05E99, "TradeOfferCommand"));
mStaticActors.Add(0xA0F05E9A, new Command(0xA0F05E9A, "TradeExecuteCommand"));
mStaticActors.Add(0xA0F05E9B, new Command(0xA0F05E9B, "LogoutCommand"));
mStaticActors.Add(0xA0F05E9C, new Command(0xA0F05E9C, "TeleportCommand"));
mStaticActors.Add(0xA0F05E9D, new Command(0xA0F05E9D, "ItemStuffCommand"));
mStaticActors.Add(0xA0F05E9E, new Command(0xA0F05E9E, "ItemArrangementCommand"));
mStaticActors.Add(0xA0F05E9F, new Command(0xA0F05E9F, "ItemMovePackageCommand"));
mStaticActors.Add(0xA0F05EA0, new Command(0xA0F05EA0, "ItemSplitCommand"));
mStaticActors.Add(0xA0F05EA1, new Command(0xA0F05EA1, "ItemTransferCommand"));
mStaticActors.Add(0xA0F05EA2, new Command(0xA0F05EA2, "ItemWasteCommand"));
mStaticActors.Add(0xA0F05EA3, new Command(0xA0F05EA3, "BazaarTradeCommand"));
mStaticActors.Add(0xA0F05EA4, new Command(0xA0F05EA4, "WidgetOpenCommand"));
mStaticActors.Add(0xA0F05EA5, new Command(0xA0F05EA5, "MacroCommand"));
mStaticActors.Add(0xA0F05EA6, new Command(0xA0F05EA6, "TradeOfferCancelCommand"));
mStaticActors.Add(0xA0F05EA7, new Command(0xA0F05EA7, "LinkshellAppointCommand"));
mStaticActors.Add(0xA0F05EA8, new Command(0xA0F05EA8, "LinkshellInviteCommand"));
mStaticActors.Add(0xA0F05EA9, new Command(0xA0F05EA9, "LinkshellInviteCancelCommand"));
mStaticActors.Add(0xA0F05EAA, new Command(0xA0F05EAA, "LinkshellKickCommand"));
mStaticActors.Add(0xA0F05EAB, new Command(0xA0F05EAB, "LinkshellResignCommand"));
mStaticActors.Add(0xA0F05EAC, new Command(0xA0F05EAC, "LinkshellChangeCommand"));
mStaticActors.Add(0xA0F05EAE, new Command(0xA0F05EAE, "CheckCommand"));
mStaticActors.Add(0xA0F05EAF, new Command(0xA0F05EAF, "NetStatUserSwitchCommand"));
mStaticActors.Add(0xA0F05EB0, new Command(0xA0F05EB0, "ItemMaterializeCommand"));
mStaticActors.Add(0xA0F05EB1, new Command(0xA0F05EB1, "JournalCommand"));
mStaticActors.Add(0xA0F05EB2, new Command(0xA0F05EB2, "DiceCommand"));
mStaticActors.Add(0xA0F05EB3, new Command(0xA0F05EB3, "RepairOrderCommand"));
mStaticActors.Add(0xA0F05EB4, new Command(0xA0F05EB4, "RepairEquipmentsCommand"));
mStaticActors.Add(0xA0F05EED, new Command(0xA0F05EED, "PlaceDrivenCommand"));
mStaticActors.Add(0xA0F05EEE, new Command(0xA0F05EEE, "ContentCommand"));
mStaticActors.Add(0xA0F05EEF, new Command(0xA0F05EEF, "ConfirmGroupCommand"));
mStaticActors.Add(0xA0F05EF0, new Command(0xA0F05EF0, "ConfirmWarpCOmmand"));
mStaticActors.Add(0xA0F05EF1, new Command(0xA0F05EF1, "ConfirmTradeCommand"));
mStaticActors.Add(0xA0F05EF2, new Command(0xA0F05EF2, "ConfirmRaiseCommand"));
mStaticActors.Add(0xA0F05EF8, new Command(0xA0F05EF8, "EmoteSitCommand "));
mStaticActors.Add(0xA0F06A0E, new Command(0xA0F06A0E, "AttackWeaponSKill"));
mStaticActors.Add(0xA0F07339, new Command(0xA0F07339, "NegotiateCommand"));
mStaticActors.Add(0xA0F07595, new Command(0xA0F07595, "DebugInputCommand"));
if (data[0] == 's' && data[1] == 'a' && data[2] == 'n' && data[3] == 'e')
data = DecryptStaticActorsFile(data);
LoadStaticActors(data);
}
public bool exists(uint actorId)
private byte[] DecryptStaticActorsFile(byte[] encoded)
{
byte[] decoded = new byte[encoded.Length - 13];
MemoryStream sIn = new MemoryStream(encoded);
MemoryStream sOut = new MemoryStream(decoded);
BinaryReader binReader = new BinaryReader(sIn);
BinaryWriter binWriter = new BinaryWriter(sOut);
binReader.BaseStream.Seek(13, SeekOrigin.Begin);
while (true)
{
try
{
byte byteIn = binReader.ReadByte();
byte byteOut = (Byte)(byteIn ^ 0x73);
binWriter.Write((Byte)byteOut);
}
catch (EndOfStreamException e) { break; }
}
binReader.Close();
binWriter.Close();
return decoded;
}
private bool LoadStaticActors(byte[] data)
{
try
{
using (MemoryStream s = new MemoryStream(data))
{
using (BinaryReader binReader = new BinaryReader(s))
{
while (binReader.BaseStream.Position != binReader.BaseStream.Length)
{
uint id = Utils.SwapEndian(binReader.ReadUInt32()) | 0xA0F00000;
List<byte> list = new List<byte>();
byte readByte;
while ((readByte = binReader.ReadByte()) != 0)
list.Add(readByte);
string output = Encoding.UTF8.GetString(list.ToArray());
string actorType = output.Split('/')[1];
string actorName = output.Substring(1 + output.LastIndexOf("/"));
if (actorType.Equals("Command"))
mStaticActors.Add(id, new Command(id, actorName));
else if (actorType.Equals("Quest"))
mStaticActors.Add(id, new Quest(id, actorName));
//else if (actorType.Equals("Status"))
//mStaticActors.Add(id, new Status(id, actorName));
else if (actorType.Equals("Judge"))
mStaticActors.Add(id, new Judge(id, actorName));
}
}
}
}
catch(FileNotFoundException e)
{ Program.Log.Error("Could not find staticactors file."); return false; }
Program.Log.Info("Loaded {0} static actors.", mStaticActors.Count());
return true;
}
public bool Exists(uint actorId)
{
return mStaticActors[actorId] != null;
}
public Actor getActor(uint actorId)
public Actor FindStaticActor(string name)
{
foreach (Actor a in mStaticActors.Values)
{
if (a.actorName.Equals(name))
return a;
}
return null;
}
public Actor GetActor(uint actorId)
{
if (mStaticActors.ContainsKey(actorId))
return mStaticActors[actorId];
else
return null;
}
}

View File

@@ -0,0 +1,592 @@
using FFXIVClassic_Map_Server;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.area;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.dataobjects.chara;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using MoonSharp.Interpreter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.actors.director;
namespace FFXIVClassic_Map_Server.Actors
{
class Area : Actor
{
public string zoneName;
public ushort regionId;
public bool isIsolated, canStealth, isInn, canRideChocobo, isInstanceRaid;
public ushort weatherNormal, weatherCommon, weatherRare;
public ushort bgmDay, bgmNight, bgmBattle;
protected string classPath;
public int boundingGridSize = 50;
public int minX = -5000, minY = -5000, maxX = 5000, maxY = 5000;
protected int numXBlocks, numYBlocks;
protected int halfWidth, halfHeight;
private Dictionary<uint, Director> currentDirectors = new Dictionary<uint, Director>();
private Object directorLock = new Object();
private uint directorIdCount = 0;
protected Director mWeatherDirector;
protected List<SpawnLocation> mSpawnLocations = new List<SpawnLocation>();
protected Dictionary<uint, Actor> mActorList = new Dictionary<uint, Actor>();
protected List<Actor>[,] mActorBlock;
LuaScript areaScript;
public Area(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
: base(id)
{
this.zoneName = zoneName;
this.regionId = regionId;
this.canStealth = canStealth;
this.isIsolated = isIsolated;
this.isInn = isInn;
this.canRideChocobo = canRideChocobo;
this.isInstanceRaid = isInstanceRaid;
this.bgmDay = bgmDay;
this.bgmNight = bgmNight;
this.bgmBattle = bgmBattle;
this.displayNameId = 0;
this.customDisplayName = "_areaMaster";
this.actorName = String.Format("_areaMaster@{0:X5}", id << 8);
this.classPath = classPath;
this.className = classPath.Substring(classPath.LastIndexOf("/") + 1);
numXBlocks = (maxX - minX) / boundingGridSize;
numYBlocks = (maxY - minY) / boundingGridSize;
mActorBlock = new List<Actor>[numXBlocks, numYBlocks];
halfWidth = numXBlocks / 2;
halfHeight = numYBlocks / 2;
for (int y = 0; y < numYBlocks; y++)
{
for (int x = 0; x < numXBlocks; x++)
{
mActorBlock[x, y] = new List<Actor>();
}
}
}
public override SubPacket CreateScriptBindPacket()
{
List<LuaParam> lParams;
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "/Area/Zone/ZoneDefault", -1, (byte)1, true, false, false, false, false, false, false, false);
return ActorInstantiatePacket.BuildPacket(actorId, actorName, "ZoneDefault", lParams);
}
public override List<SubPacket> GetSpawnPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(0));
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0x1));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket());
return subpackets;
}
#region Actor Management
public void AddActorToZone(Actor actor)
{
lock (mActorList)
{
if (!mActorList.ContainsKey(actor.actorId))
mActorList.Add(actor.actorId, actor);
}
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
lock (mActorBlock)
mActorBlock[gridX, gridY].Add(actor);
}
public void RemoveActorFromZone(Actor actor)
{
lock (mActorList)
mActorList.Remove(actor.actorId);
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
lock (mActorBlock)
mActorBlock[gridX, gridY].Remove(actor);
}
public void UpdateActorPosition(Actor actor)
{
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
int gridOldX = (int)actor.oldPositionX / boundingGridSize;
int gridOldY = (int)actor.oldPositionZ / boundingGridSize;
gridOldX += halfWidth;
gridOldY += halfHeight;
//Boundries
if (gridOldX < 0)
gridOldX = 0;
if (gridOldX >= numXBlocks)
gridOldX = numXBlocks - 1;
if (gridOldY < 0)
gridOldY = 0;
if (gridOldY >= numYBlocks)
gridOldY = numYBlocks - 1;
//Still in same block
if (gridX == gridOldX && gridY == gridOldY)
return;
lock (mActorBlock)
{
mActorBlock[gridOldX, gridOldY].Remove(actor);
mActorBlock[gridX, gridY].Add(actor);
}
}
public List<Actor> GetActorsAroundPoint(float x, float y, int checkDistance)
{
checkDistance /= boundingGridSize;
int gridX = (int)x/boundingGridSize;
int gridY = (int)y/boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
List<Actor> result = new List<Actor>();
lock (mActorBlock)
{
for (int gx = gridX - checkDistance; gx <= gridX + checkDistance; gx++)
{
for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++)
{
result.AddRange(mActorBlock[gx, gy]);
}
}
}
//Remove players if isolation zone
if (isIsolated)
{
for (int i = 0; i < result.Count; i++)
{
if (result[i] is Player)
result.RemoveAt(i);
}
}
return result;
}
public List<Actor> GetActorsAroundActor(Actor actor, int checkDistance)
{
checkDistance /= boundingGridSize;
int gridX = (int)actor.positionX / boundingGridSize;
int gridY = (int)actor.positionZ / boundingGridSize;
gridX += halfWidth;
gridY += halfHeight;
//Boundries
if (gridX < 0)
gridX = 0;
if (gridX >= numXBlocks)
gridX = numXBlocks - 1;
if (gridY < 0)
gridY = 0;
if (gridY >= numYBlocks)
gridY = numYBlocks - 1;
List<Actor> result = new List<Actor>();
lock (mActorBlock)
{
for (int gy = ((gridY - checkDistance) < 0 ? 0 : (gridY - checkDistance)); gy <= ((gridY + checkDistance) >= numYBlocks ? numYBlocks - 1 : (gridY + checkDistance)); gy++)
{
for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++)
{
result.AddRange(mActorBlock[gx, gy]);
}
}
}
//Remove players if isolation zone
if (isIsolated)
{
for (int i = 0; i < result.Count; i++)
{
if (result[i] is Player)
result.RemoveAt(i);
}
}
return result;
}
#endregion
public Actor FindActorInArea(uint id)
{
lock (mActorList)
{
if (!mActorList.ContainsKey(id))
return null;
return mActorList[id];
}
}
public Actor FindActorInZoneByUniqueID(string uniqueId)
{
lock (mActorList)
{
foreach (Actor a in mActorList.Values)
{
if (a is Npc)
{
if (((Npc)a).GetUniqueId().ToLower().Equals(uniqueId))
return a;
}
}
}
return null;
}
public Player FindPCInZone(string name)
{
lock (mActorList)
{
foreach (Actor a in mActorList.Values)
{
if (a is Player)
{
if (((Player)a).customDisplayName.ToLower().Equals(name.ToLower()))
return (Player)a;
}
}
return null;
}
}
public Player FindPCInZone(uint id)
{
lock (mActorList)
{
if (!mActorList.ContainsKey(id))
return null;
return (Player)mActorList[id];
}
}
public void Clear()
{
lock (mActorList)
{
//Clear All
mActorList.Clear();
lock (mActorBlock)
{
for (int y = 0; y < numYBlocks; y++)
{
for (int x = 0; x < numXBlocks; x++)
{
mActorBlock[x, y].Clear();
}
}
}
}
}
public void BroadcastPacketsAroundActor(Actor actor, List<SubPacket> packets)
{
foreach (SubPacket packet in packets)
BroadcastPacketAroundActor(actor, packet);
}
public void BroadcastPacketAroundActor(Actor actor, SubPacket packet)
{
if (isIsolated)
return;
List<Actor> aroundActor = GetActorsAroundActor(actor, 50);
foreach (Actor a in aroundActor)
{
if (a is Player)
{
if (isIsolated && packet.header.sourceId != a.actorId)
continue;
SubPacket clonedPacket = new SubPacket(packet, a.actorId);
Player p = (Player)a;
p.QueuePacket(clonedPacket);
}
}
}
public void SpawnActor(SpawnLocation location)
{
ActorClass actorClass = Server.GetWorldManager().GetActorClass(location.classId);
if (actorClass == null)
return;
uint zoneId;
if (this is PrivateArea)
zoneId = ((PrivateArea)this).GetParentZone().actorId;
else
zoneId = actorId;
Npc npc = new Npc(mActorList.Count + 1, actorClass, location.uniqueId, this, location.x, location.y, location.z, location.rot, location.state, location.animId, null);
npc.LoadEventConditions(actorClass.eventConditions);
AddActorToZone(npc);
}
public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, float rot = 0, ushort state = 0, uint animId = 0)
{
ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId);
if (actorClass == null)
return null;
uint zoneId;
if (this is PrivateArea)
zoneId = ((PrivateArea)this).GetParentZone().actorId;
else
zoneId = actorId;
Npc npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
npc.LoadEventConditions(actorClass.eventConditions);
AddActorToZone(npc);
return npc;
}
public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, uint regionId, uint layoutId)
{
ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId);
if (actorClass == null)
return null;
uint zoneId;
if (this is PrivateArea)
zoneId = ((PrivateArea)this).GetParentZone().actorId;
else
zoneId = actorId;
Npc npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, 0, regionId, layoutId);
npc.LoadEventConditions(actorClass.eventConditions);
AddActorToZone(npc);
return npc;
}
public void DespawnActor(string uniqueId)
{
RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId));
}
public void DespawnActor(Actor actor)
{
RemoveActorFromZone(actor);
}
public Director GetWeatherDirector()
{
return mWeatherDirector;
}
public void ChangeWeather(ushort weather, ushort transitionTime, Player player, bool zoneWide = false)
{
weatherNormal = weather;
if (player != null && !zoneWide)
{
player.QueuePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime));
}
if (zoneWide)
{
lock (mActorList)
{
foreach (var actor in mActorList)
{
if (actor.Value is Player)
{
player = ((Player)actor.Value);
player.QueuePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime));
}
}
}
}
}
public Director CreateDirector(string path, bool hasContentGroup, params object[] args)
{
lock (directorLock)
{
Director director = new Director(directorIdCount, this, path, hasContentGroup, args);
currentDirectors.Add(director.actorId, director);
directorIdCount++;
return director;
}
}
public Director CreateGuildleveDirector(uint glid, byte difficulty, Player owner, params object[] args)
{
String directorScriptPath = "";
uint type = Server.GetGuildleveGamedata(glid).plateId;
if (glid == 10801 || glid == 12401 || glid == 11601)
directorScriptPath = "Guildleve/PrivateGLBattleTutorial";
else
{
switch (type)
{
case 20021:
directorScriptPath = "Guildleve/PrivateGLBattleSweepNormal";
break;
case 20022:
directorScriptPath = "Guildleve/PrivateGLBattleChaseNormal";
break;
case 20023:
directorScriptPath = "Guildleve/PrivateGLBattleOrbNormal";
break;
case 20024:
directorScriptPath = "Guildleve/PrivateGLBattleHuntNormal";
break;
case 20025:
directorScriptPath = "Guildleve/PrivateGLBattleGatherNormal";
break;
case 20026:
directorScriptPath = "Guildleve/PrivateGLBattleRoundNormal";
break;
case 20027:
directorScriptPath = "Guildleve/PrivateGLBattleSurviveNormal";
break;
case 20028:
directorScriptPath = "Guildleve/PrivateGLBattleDetectNormal";
break;
}
}
lock (directorLock)
{
GuildleveDirector director = new GuildleveDirector(directorIdCount, this, directorScriptPath, glid, difficulty, owner, args);
currentDirectors.Add(director.actorId, director);
directorIdCount++;
return director;
}
}
public void DeleteDirector(uint id)
{
lock (directorLock)
{
if (currentDirectors.ContainsKey(id))
{
if (!currentDirectors[id].IsDeleted())
currentDirectors[id].EndDirector();
currentDirectors.Remove(id);
}
}
}
public Director GetDirectorById(uint id)
{
if (currentDirectors.ContainsKey(id))
return currentDirectors[id];
return null;
}
public void Update(double deltaTime)
{
lock (mActorList)
{
foreach (Actor a in mActorList.Values)
a.Update(deltaTime);
}
}
}
}

View File

@@ -0,0 +1,69 @@

using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.area
{
class PrivateArea : Area
{
private Zone parentZone;
private string privateAreaName;
private uint privateAreaType;
public PrivateArea(Zone parent, uint id, string classPath, string privateAreaName, uint privateAreaType, ushort bgmDay, ushort bgmNight, ushort bgmBattle)
: base(id, parent.zoneName, parent.regionId, classPath, bgmDay, bgmNight, bgmBattle, parent.isIsolated, parent.isInn, parent.canRideChocobo, parent.canStealth, true)
{
this.parentZone = parent;
this.zoneName = parent.zoneName;
this.privateAreaName = privateAreaName;
this.privateAreaType = privateAreaType;
}
public string GetPrivateAreaName()
{
return privateAreaName;
}
public uint GetPrivateAreaType()
{
return privateAreaType;
}
public Zone GetParentZone()
{
return parentZone;
}
public override SubPacket CreateScriptBindPacket()
{
List<LuaParam> lParams;
string path = className;
string realClassName = className.Substring(className.LastIndexOf("/") + 1);
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, privateAreaName, privateAreaType, canRideChocobo ? (byte)1 : (byte)0, canStealth, isInn, false, false, false, false, false, false);
ActorInstantiatePacket.BuildPacket(actorId, actorName, realClassName, lParams).DebugPrintSubPacket();
return ActorInstantiatePacket.BuildPacket(actorId, actorName, realClassName, lParams);
}
public void AddSpawnLocation(SpawnLocation spawn)
{
mSpawnLocations.Add(spawn);
}
public void SpawnAllActors()
{
foreach (SpawnLocation spawn in mSpawnLocations)
SpawnActor(spawn);
}
}
}

View File

@@ -0,0 +1,57 @@
using FFXIVClassic_Map_Server.actors.director;
using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.area
{
class PrivateAreaContent : PrivateArea
{
private Director currentDirector;
private bool isContentFinished = false;
public static PrivateAreaContent CreateContentArea(String scriptPath)
{
return null;
}
public PrivateAreaContent(Zone parent, string classPath, string privateAreaName, uint privateAreaType, Director director, Player contentStarter) //TODO: Make it a list
: base(parent, parent.actorId, classPath, privateAreaName, privateAreaType, 0, 0, 0)
{
currentDirector = director;
LuaEngine.GetInstance().CallLuaFunction(contentStarter, this, "onCreate", false, currentDirector);
}
public Director GetContentDirector()
{
return currentDirector;
}
public void ContentFinished()
{
isContentFinished = true;
}
public void CheckDestroy()
{
if (isContentFinished)
{
bool noPlayersLeft = true;
foreach (Actor a in mActorList.Values)
{
if (a is Player)
noPlayersLeft = false;
}
if (noPlayersLeft)
GetParentZone().DeleteContentArea(this);
}
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.area
{
class SpawnLocation
{
public uint classId;
public string uniqueId;
public uint zoneId;
public string privAreaName;
public uint privAreaLevel;
public float x;
public float y;
public float z;
public float rot;
public ushort state;
public uint animId;
public SpawnLocation(uint classId, string uniqueId, uint zoneId, string privAreaName, uint privAreaLevel, float x, float y, float z, float rot, ushort state, uint animId)
{
this.classId = classId;
this.uniqueId = uniqueId;
this.zoneId = zoneId;
this.privAreaName = privAreaName;
this.privAreaLevel = privAreaLevel;
this.x = x;
this.y = y;
this.z = z;
this.rot = rot;
this.state = state;
this.animId = animId;
}
}
}

View File

@@ -0,0 +1,143 @@
using FFXIVClassic_Map_Server;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.actors.director;
namespace FFXIVClassic_Map_Server.actors.area
{
class Zone : Area
{
Dictionary<string, Dictionary<uint, PrivateArea>> privateAreas = new Dictionary<string, Dictionary<uint, PrivateArea>>();
Dictionary<string, List<PrivateAreaContent>> contentAreas = new Dictionary<string, List<PrivateAreaContent>>();
Object contentAreasLock = new Object();
public Zone(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
: base(id, zoneName, regionId, classPath, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid)
{
}
public void AddPrivateArea(PrivateArea pa)
{
if (privateAreas.ContainsKey(pa.GetPrivateAreaName()))
privateAreas[pa.GetPrivateAreaName()][pa.GetPrivateAreaType()] = pa;
else
{
privateAreas[pa.GetPrivateAreaName()] = new Dictionary<uint, PrivateArea>();
privateAreas[pa.GetPrivateAreaName()][pa.GetPrivateAreaType()] = pa;
}
}
public PrivateArea GetPrivateArea(string type, uint number)
{
if (privateAreas.ContainsKey(type))
{
Dictionary<uint, PrivateArea> instances = privateAreas[type];
if (instances.ContainsKey(number))
return instances[number];
else
return null;
}
else
return null;
}
public override SubPacket CreateScriptBindPacket()
{
bool isEntranceDesion = false;
List<LuaParam> lParams;
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "", -1, canRideChocobo ? (byte)1 : (byte)0, canStealth, isInn, false, false, false, true, isInstanceRaid, isEntranceDesion);
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
}
public void AddSpawnLocation(SpawnLocation spawn)
{
//Is it in a private area?
if (!spawn.privAreaName.Equals(""))
{
if (privateAreas.ContainsKey(spawn.privAreaName))
{
Dictionary<uint, PrivateArea> levels = privateAreas[spawn.privAreaName];
if (levels.ContainsKey(spawn.privAreaLevel))
levels[spawn.privAreaLevel].AddSpawnLocation(spawn);
else
Program.Log.Error("Tried to add a spawn location to non-existing private area level \"{0}\" in area {1} in zone {2}", spawn.privAreaName, spawn.privAreaLevel, zoneName);
}
else
Program.Log.Error("Tried to add a spawn location to non-existing private area \"{0}\" in zone {1}", spawn.privAreaName, zoneName);
}
else
mSpawnLocations.Add(spawn);
}
public void SpawnAllActors(bool doPrivAreas)
{
foreach (SpawnLocation spawn in mSpawnLocations)
SpawnActor(spawn);
if (doPrivAreas)
{
foreach (Dictionary<uint, PrivateArea> areas in privateAreas.Values)
{
foreach (PrivateArea pa in areas.Values)
pa.SpawnAllActors();
}
}
}
public Actor FindActorInZone(uint id)
{
if (!mActorList.ContainsKey(id))
{
foreach(Dictionary<uint, PrivateArea> paList in privateAreas.Values)
{
foreach(PrivateArea pa in paList.Values)
{
Actor actor = pa.FindActorInArea(id);
if (actor != null)
return actor;
}
}
return null;
}
else
return mActorList[id];
}
public PrivateAreaContent CreateContentArea(Player starterPlayer, string areaClassPath, string contentScript, string areaName, string directorName, params object[] args)
{
lock (contentAreasLock)
{
Director director = CreateDirector(directorName, true, args);
if (director == null)
return null;
if (!contentAreas.ContainsKey(areaName))
contentAreas.Add(areaName, new List<PrivateAreaContent>());
PrivateAreaContent contentArea = new PrivateAreaContent(this, classPath, areaName, 1, director, starterPlayer);
contentAreas[areaName].Add(contentArea);
return contentArea;
}
}
public void DeleteContentArea(PrivateAreaContent area)
{
if (contentAreas.ContainsKey(area.GetPrivateAreaName()))
{
contentAreas[area.GetPrivateAreaName()].Remove(area);
}
}
}
}

View File

@@ -0,0 +1,22 @@
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class AetheryteWork
{
public int iconGil;
public short guildleveId;
public short clearTime;
public int missionBonus;
public int difficultyBonus;
public byte factionNumber;
public int factionBonus;
public byte factionCredit;
public int glRewardItem;
public int glRewardNumber;
public int glRewardSubItem;
public int glRewardSubNumber;
public byte difficulty;
}
}

View File

@@ -1,18 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class BattleSave
{
public float potencial;
public int skillLevel;
public int skillLevelCap;
public int[] skillPoint;
public float potencial = 6.6f;
public short[] skillLevel = new short[52];
public short[] skillLevelCap = new short[52];
public short[] skillPoint = new short[52];
public short physicalLevel;
public int physicalExp;
public bool[] negotiationFlag= new bool[2];

View File

@@ -1,34 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class BattleTemp
{
public const uint NAMEPLATE_SHOWN = 0xFBFBCFB1;
public const uint TARGETABLE = 0x2138FD71;
public const uint NAMEPLATE_SHOWN = 0;
public const uint TARGETABLE = 1;
//public const uint NAMEPLATE_SHOWN2 = 2;
public const uint NAMEPLATE_SHOWN2 = 3;
public const uint STAT_STRENGTH = 4;
public const uint STAT_VITALITY = 5;
public const uint STAT_DEXTERITY = 6;
public const uint STAT_INTELLIGENCE = 7;
public const uint STAT_MIND = 8;
public const uint STAT_PIETY = 9;
public const uint STAT_STRENGTH = 3;
public const uint STAT_VITALITY = 4;
public const uint STAT_DEXTERITY = 5;
public const uint STAT_INTELLIGENCE = 6;
public const uint STAT_MIND = 7;
public const uint STAT_PIETY = 8;
public const uint STAT_RESISTANCE_FIRE = 10;
public const uint STAT_RESISTANCE_ICE = 11;
public const uint STAT_RESISTANCE_WIND = 12;
public const uint STAT_RESISTANCE_LIGHTNING = 3;
public const uint STAT_RESISTANCE_EARTH = 14;
public const uint STAT_RESISTANCE_WATER = 15;
public const uint STAT_RESISTANCE_FIRE = 9;
public const uint STAT_RESISTANCE_ICE = 10;
public const uint STAT_RESISTANCE_WIND = 11;
public const uint STAT_RESISTANCE_LIGHTNING = 12;
public const uint STAT_RESISTANCE_EARTH = 13;
public const uint STAT_RESISTANCE_WATER = 14;
public const uint STAT_ATTACK = 18;
public const uint STAT_ACCURACY = 16;
public const uint STAT_NORMALDEFENSE = 19;
public const uint STAT_EVASION = 17;
public const uint STAT_ATTACK = 17;
public const uint STAT_ACCURACY = 15;
public const uint STAT_NORMALDEFENSE = 18;
public const uint STAT_EVASION = 16;
public const uint STAT_ATTACK_MAGIC = 24;
public const uint STAT_HEAL_MAGIC = 25;
public const uint STAT_ENCHANCEMENT_MAGIC_POTENCY = 26;
@@ -45,8 +41,8 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public const uint STAT_HARVEST_LIMIT = 34;
public const uint STAT_HARVEST_RATE = 35;
public int[] castGauge_speed = new int[2];
public float[] castGauge_speed = { 1.0f, 0.25f};
public bool[] timingCommandFlag = new bool[4];
public ushort[] generalParameter = new ushort[32];
public ushort[] generalParameter = new ushort[35];
}
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class CharaWork
{
@@ -18,17 +12,21 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public EventSave eventSave = new EventSave();
public EventTemp eventTemp = new EventTemp();
public bool gameParameter = false;
public byte[] property = new byte[32];
public ushort[] status = new ushort[20];
public uint[] statusShownTime = new uint[20];
public uint[] command = new uint[64];
public int[] commandCategory = new int[64];
public int commandBorder = 0x20;
public bool commandAcquired = false;
public bool[] additionalCommandAcquired = new bool[1];
public uint[] command = new uint[64]; //ACTORS
public byte[] commandCategory = new byte[64];
public byte commandBorder = 0x20;
public bool[] commandAcquired = new bool[4096];
public bool[] additionalCommandAcquired = new bool[36];
public uint currentContentGroup;
public uint depictionJudge = 0xa0f50911;
}
}

View File

@@ -1,13 +1,11 @@
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.actors.chara;

using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.Actors.Chara;
using FFXIVClassic_Map_Server.packets.send.actor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.utils;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors
{
class Character:Actor
{
@@ -16,29 +14,34 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public const int FACEINFO = 2;
public const int HIGHLIGHT_HAIR = 3;
public const int VOICE = 4;
public const int WEAPON1 = 5;
public const int WEAPON2 = 6;
public const int WEAPON3 = 7;
public const int UNKNOWN1 = 8;
public const int UNKNOWN2 = 9;
public const int UNKNOWN3 = 10;
public const int UNKNOWN4 = 11;
public const int MAINHAND = 5;
public const int OFFHAND = 6;
public const int SPMAINHAND = 7;
public const int SPOFFHAND = 8;
public const int THROWING = 9;
public const int PACK = 10;
public const int POUCH = 11;
public const int HEADGEAR = 12;
public const int BODYGEAR = 13;
public const int LEGSGEAR = 14;
public const int HANDSGEAR = 15;
public const int FEETGEAR = 16;
public const int WAISTGEAR = 17;
public const int UNKNOWN5 = 18;
public const int R_EAR = 19;
public const int L_EAR = 20;
public const int UNKNOWN6 = 21;
public const int UNKNOWN7 = 22;
public const int R_FINGER = 23;
public const int L_FINGER = 24;
public const int NECKGEAR = 18;
public const int L_EAR = 19;
public const int R_EAR = 20;
public const int R_WRIST = 21;
public const int L_WRIST = 22;
public const int R_RINGFINGER = 23;
public const int L_RINGFINGER = 24;
public const int R_INDEXFINGER = 25;
public const int L_INDEXFINGER = 26;
public const int UNKNOWN = 27;
public uint modelID;
public uint[] appearanceIDs = new uint[0x1D];
public bool isStatic = false;
public uint modelId;
public uint[] appearanceIds = new uint[28];
public uint animationId = 0;
@@ -50,29 +53,66 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public Work work = new Work();
public CharaWork charaWork = new CharaWork();
public Group currentParty = null;
public ContentGroup currentContentGroup = null;
public Character(uint actorID) : base(actorID)
{
//Init timer array to "notimer"
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
charaWork.statusShownTime[i] = 0xFFFFFFFF;
}
public SubPacket createAppearancePacket(uint playerActorId)
public SubPacket CreateAppearancePacket()
{
SetActorAppearancePacket setappearance = new SetActorAppearancePacket(modelID, appearanceIDs);
return setappearance.buildPacket(actorId, playerActorId);
SetActorAppearancePacket setappearance = new SetActorAppearancePacket(modelId, appearanceIds);
return setappearance.BuildPacket(actorId);
}
public SubPacket createInitStatusPacket(uint playerActorId)
public SubPacket CreateInitStatusPacket()
{
return (SetActorStatusAllPacket.buildPacket(actorId, playerActorId, charaWork.status));
return (SetActorStatusAllPacket.BuildPacket(actorId, charaWork.status));
}
public SubPacket createSetActorIconPacket(uint playerActorId)
public SubPacket CreateSetActorIconPacket()
{
return SetActorIconPacket.buildPacket(actorId, playerActorId, currentActorIcon);
return SetActorIconPacket.BuildPacket(actorId, currentActorIcon);
}
public SubPacket createIdleAnimationPacket(uint playerActorId)
public SubPacket CreateIdleAnimationPacket()
{
return SetActorIdleAnimationPacket.buildPacket(actorId, playerActorId, animationId);
return SetActorSubStatPacket.BuildPacket(actorId, 0, 0, 0, 0, 0, 0, animationId);
}
public void SetQuestGraphic(Player player, int graphicNum)
{
player.QueuePacket(SetActorQuestGraphicPacket.BuildPacket(actorId, graphicNum));
}
public void SetCurrentContentGroup(ContentGroup group)
{
if (group != null)
charaWork.currentContentGroup = group.GetTypeId();
else
charaWork.currentContentGroup = 0;
currentContentGroup = group;
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this);
propPacketUtil.AddProperty("charaWork.currentContentGroup");
zone.BroadcastPacketsAroundActor(this, propPacketUtil.Done());
}
public void PlayAnimation(uint animId, bool onlySelf = false)
{
if (onlySelf)
{
if (this is Player)
((Player)this).QueuePacket(PlayAnimationOnActorPacket.BuildPacket(actorId, animId));
}
else
zone.BroadcastPacketAroundActor(this, PlayAnimationOnActorPacket.BuildPacket(actorId, animId));
}
}

View File

@@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class EventSave
{
public bool bazaar;
public float bazaarTax;
public int repairType;
public byte bazaarTax;
public byte repairType;
}
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class EventTemp
{

View File

@@ -1,26 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class ParameterSave
{
public int[] hp = new int[1];
public int[] hpMax = new int[1];
public int mp;
public int mpMax;
public short[] hp = new short[8];
public short[] hpMax = new short[8];
public short mp;
public short mpMax;
public int[] state_mainSkill = new int[4];
public int state_mainSkillLevel;
public byte[] state_mainSkill = new byte[4];
public short state_mainSkillLevel;
public int[] state_boostPointForSkill;
public byte[] state_boostPointForSkill = new byte[4];
public int[] commandSlot_compatibility;
public uint[] commandSlot_recastTime = new uint[40];
public bool[] commandSlot_compatibility = new bool[40];
public int[] giftCommandSlot_commandId;
public ushort[] giftCommandSlot_commandId = new ushort[10];
public ushort[] constanceCommandSlot_commandId = new ushort[10];
public byte abilityCostPoint_used;
public byte abilityCostPoint_max;
public byte giftCostPoint_used;
public byte giftCostPoint_max;
public byte constanceCostPoint_used;
public byte constanceCostPoint_max;
}
}

View File

@@ -1,23 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class ParameterTemp
{
public int tp = 0;
public short tp = 0;
public int targetInformation = 0;
public int[] maxCommandRecastTime = new int[40];
public ushort[] maxCommandRecastTime = new ushort[40];
public float[] forceControl_float_forClientSelf = new float[4];
public short[] forceControl_int16_forClientSelf = new short[2];
public float[] forceControl_float_forClientSelf = { 1.0f, 1.0f, 0.0f, 0.0f};
public short[] forceControl_int16_forClientSelf = { -1, -1 };
public int[] otherClassAbilityCount = new int[2];
public int[] giftCount = new int[2];
public byte[] otherClassAbilityCount = new byte[2];
public byte[] giftCount = new byte[2];
}
}

View File

@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.chara
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class Work
{
public ushort[] guildleveId = new ushort[16];
public bool[] guildleveDone = new bool[16];
public bool[] guildleveChecked = new bool[16];
public bool betacheck = false;
public bool[] event_achieve_aetheryte = new bool[128];
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.chara.npc
{
class ActorClass
{
public readonly uint actorClassId;
public readonly string classPath;
public readonly uint displayNameId;
public readonly uint propertyFlags;
public readonly string eventConditions;
public readonly ushort pushCommand;
public readonly ushort pushCommandSub;
public readonly byte pushCommandPriority;
public ActorClass(uint id, string classPath, uint nameId, uint propertyFlags, string eventConditions, ushort pushCommand, ushort pushCommandSub, byte pushCommandPriority)
{
this.actorClassId = id;
this.classPath = classPath;
this.displayNameId = nameId;
this.propertyFlags = propertyFlags;
this.eventConditions = eventConditions;
this.pushCommand = pushCommand;
this.pushCommandSub = pushCommandSub;
this.pushCommandPriority = pushCommandPriority;
}
}
}

View File

@@ -1,91 +1,409 @@
using FFXIVClassic_Lobby_Server;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.dataobjects;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.actors.area;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.Actors.Chara;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.receive.events;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.utils;
using MoonSharp.Interpreter;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara.npc
namespace FFXIVClassic_Map_Server.Actors
{
class Npc : Character
{
public Npc(uint id, string actorName, uint displayNameId, string customDisplayName, float positionX, float positionY, float positionZ, float rotation, uint animationId, string className, byte[] initParams)
: base(id)
private uint actorClassId;
private string uniqueIdentifier;
private bool isMapObj = false;
private uint layout, instance;
public NpcWork npcWork = new NpcWork();
public Npc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, string customDisplayName)
: base((4 << 28 | spawnedArea.actorId << 19 | (uint)actorNumber))
{
this.actorName = actorName;
this.displayNameId = displayNameId;
this.customDisplayName = customDisplayName;
this.positionX = positionX;
this.positionY = positionY;
this.positionZ = positionZ;
this.rotation = rotation;
this.positionX = posX;
this.positionY = posY;
this.positionZ = posZ;
this.rotation = rot;
this.currentMainState = actorState;
this.animationId = animationId;
this.className = className;
if (initParams.Length != 0)
this.classParams = LuaUtils.readLuaParams(initParams);
this.displayNameId = actorClass.displayNameId;
this.customDisplayName = customDisplayName;
setPlayerAppearance();
}
this.uniqueIdentifier = uniqueId;
public void setPlayerAppearance()
this.zoneId = spawnedArea.actorId;
this.zone = spawnedArea;
this.actorClassId = actorClass.actorClassId;
LoadNpcAppearance(actorClass.actorClassId);
className = actorClass.classPath.Substring(actorClass.classPath.LastIndexOf("/") + 1);
this.classPath = String.Format("{0}/{1}", actorClass.classPath.Substring(0, actorClass.classPath.LastIndexOf('/')).ToLower(), className);
charaWork.battleSave.potencial = 1.0f;
charaWork.parameterSave.state_mainSkill[0] = 3;
charaWork.parameterSave.state_mainSkill[2] = 3;
charaWork.parameterSave.state_mainSkillLevel = 2;
charaWork.parameterSave.hp[0] = 500;
charaWork.parameterSave.hpMax[0] = 500;
for (int i = 0; i < 32; i++ )
charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1);
npcWork.pushCommand = actorClass.pushCommand;
npcWork.pushCommandSub = actorClass.pushCommandSub;
npcWork.pushCommandPriority = actorClass.pushCommandPriority;
if (actorClassId == 1080078 || actorClassId == 1080079 || actorClassId == 1080080 || (actorClassId >= 1080123 && actorClassId <= 1080135) || (actorClassId >= 5000001 && actorClassId <= 5000090) || (actorClassId >= 5900001 && actorClassId <= 5900038))
{
DBAppearance appearance = Database.getAppearance(false, actorId);
if (appearance == null)
return;
modelID = DBAppearance.getTribeModel(appearance.tribe);
appearanceIDs[SIZE] = appearance.size;
appearanceIDs[COLORINFO] = (uint)(appearance.skinColor | (appearance.hairColor << 10) | (appearance.eyeColor << 20));
appearanceIDs[FACEINFO] = PrimitiveConversion.ToUInt32(appearance.getFaceInfo());
appearanceIDs[HIGHLIGHT_HAIR] = (uint)(appearance.hairHighlightColor | appearance.hairStyle << 10);
appearanceIDs[VOICE] = appearance.voice;
appearanceIDs[WEAPON1] = appearance.mainHand;
appearanceIDs[WEAPON2] = appearance.offHand;
appearanceIDs[HEADGEAR] = appearance.head;
appearanceIDs[BODYGEAR] = appearance.body;
appearanceIDs[LEGSGEAR] = appearance.legs;
appearanceIDs[HANDSGEAR] = appearance.hands;
appearanceIDs[FEETGEAR] = appearance.feet;
appearanceIDs[WAISTGEAR] = appearance.waist;
appearanceIDs[R_EAR] = appearance.rightEar;
appearanceIDs[L_EAR] = appearance.leftEar;
appearanceIDs[R_FINGER] = appearance.rightFinger;
appearanceIDs[L_FINGER] = appearance.leftFinger;
isMapObj = true;
List<LuaParam> lParams = LuaEngine.GetInstance().CallLuaFunctionForReturn(null, this, "init", false);
if (lParams == null || lParams.Count < 6)
isMapObj = false;
else
{
layout = (uint)(Int32)lParams[4].value;
instance = (uint)(Int32)lParams[5].value;
isStatic = true;
}
}
public override SubPacket createScriptBindPacket(uint playerActorId)
GenerateActorName((int)actorNumber);
}
public Npc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, uint layout, uint instance)
: base((4 << 28 | spawnedArea.actorId << 19 | (uint)actorNumber))
{
this.positionX = posX;
this.positionY = posY;
this.positionZ = posZ;
this.rotation = rot;
this.currentMainState = 0;
this.animationId = 0;
this.displayNameId = actorClass.displayNameId;
this.uniqueIdentifier = uniqueId;
this.zoneId = spawnedArea.actorId;
this.zone = spawnedArea;
this.actorClassId = actorClass.actorClassId;
LoadNpcAppearance(actorClass.actorClassId);
this.classPath = actorClass.classPath;
className = classPath.Substring(classPath.LastIndexOf("/") + 1);
for (int i = 0; i < 32; i++)
charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1);
npcWork.pushCommand = actorClass.pushCommand;
npcWork.pushCommandSub = actorClass.pushCommandSub;
npcWork.pushCommandPriority = actorClass.pushCommandPriority;
this.isMapObj = true;
this.layout = layout;
this.instance = instance;
GenerateActorName((int)actorNumber);
}
public SubPacket CreateAddActorPacket()
{
return AddActorPacket.BuildPacket(actorId, 8);
}
// actorClassId, [], [], numBattleCommon, [battleCommon], numEventCommon, [eventCommon], args for either initForBattle/initForEvent
public override SubPacket CreateScriptBindPacket(Player player)
{
List<LuaParam> lParams;
lParams = LuaUtils.createLuaParamList("/Chara/Player/Player_work", false, false, false, false, false, true);
lParams = LuaEngine.GetInstance().CallLuaFunctionForReturn(player, this, "init", false);
return ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams);
if (lParams != null && lParams.Count >= 3 && lParams[2].typeID == 0 && (int)lParams[2].value == 0)
isStatic = true;
else
{
// charaWork.property[2] = 1;
// npcWork.hateType = 1;
}
public override BasePacket getInitPackets(uint playerActorId)
if (lParams == null)
{
string classPathFake = "/Chara/Npc/Populace/PopulaceStandard";
string classNameFake = "PopulaceStandard";
lParams = LuaUtils.CreateLuaParamList(classPathFake, false, false, false, false, false, 0xF47F6, false, false, 0, 0);
isStatic = true;
//ActorInstantiatePacket.BuildPacket(actorId, actorName, classNameFake, lParams).DebugPrintSubPacket();
return ActorInstantiatePacket.BuildPacket(actorId, actorName, classNameFake, lParams);
}
else
{
lParams.Insert(0, new LuaParam(2, classPath));
lParams.Insert(1, new LuaParam(4, 4));
lParams.Insert(2, new LuaParam(4, 4));
lParams.Insert(3, new LuaParam(4, 4));
lParams.Insert(4, new LuaParam(4, 4));
lParams.Insert(5, new LuaParam(4, 4));
lParams.Insert(6, new LuaParam(0, (int)actorClassId));
}
//ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams).DebugPrintSubPacket();
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
}
public override List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(createAddActorPacket(playerActorId));
subpackets.Add(createSpeedPacket(playerActorId));
subpackets.Add(createSpawnPositonPacket(playerActorId, 0xFF));
subpackets.Add(createAppearancePacket(playerActorId));
subpackets.Add(createNamePacket(playerActorId));
subpackets.Add(createStatePacket(playerActorId));
subpackets.Add(createIdleAnimationPacket(playerActorId));
subpackets.Add(createInitStatusPacket(playerActorId));
subpackets.Add(createSetActorIconPacket(playerActorId));
subpackets.Add(createIsZoneingPacket(playerActorId));
//subpackets.Add(createScriptBindPacket(playerActorId));
return BasePacket.createPacket(subpackets, true, false);
subpackets.Add(CreateAddActorPacket());
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0x0));
if (isMapObj)
subpackets.Add(SetActorBGPropertiesPacket.BuildPacket(actorId, instance, layout));
else
subpackets.Add(CreateAppearancePacket());
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIdleAnimationPacket());
subpackets.Add(CreateInitStatusPacket());
subpackets.Add(CreateSetActorIconPacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player));
return subpackets;
}
public override List<SubPacket> GetInitPackets()
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("/_init", this);
//Potential
propPacketUtil.AddProperty("charaWork.battleSave.potencial");
//Properties
for (int i = 0; i < charaWork.property.Length; i++)
{
if (charaWork.property[i] != 0)
propPacketUtil.AddProperty(String.Format("charaWork.property[{0}]", i));
}
//Parameters
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.mp");
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
propPacketUtil.AddProperty("charaWork.parameterTemp.tp");
if (charaWork.parameterSave.state_mainSkill[0] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[0]");
if (charaWork.parameterSave.state_mainSkill[1] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[1]");
if (charaWork.parameterSave.state_mainSkill[2] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[2]");
if (charaWork.parameterSave.state_mainSkill[3] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[3]");
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkillLevel");
//Status Times
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
{
if (charaWork.statusShownTime[i] != 0xFFFFFFFF)
propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i));
}
//General Parameters
for (int i = 3; i < charaWork.battleTemp.generalParameter.Length; i++)
{
if (charaWork.battleTemp.generalParameter[i] != 0)
propPacketUtil.AddProperty(String.Format("charaWork.battleTemp.generalParameter[{0}]", i));
}
propPacketUtil.AddProperty("npcWork.hateType");
if (npcWork.pushCommand != 0)
{
propPacketUtil.AddProperty("npcWork.pushCommand");
if (npcWork.pushCommandSub != 0)
propPacketUtil.AddProperty("npcWork.pushCommandSub");
propPacketUtil.AddProperty("npcWork.pushCommandPriority");
}
return propPacketUtil.Done();
}
public string GetUniqueId()
{
return uniqueIdentifier;
}
public uint GetActorClassId()
{
return actorClassId;
}
public void ChangeNpcAppearance(uint id)
{
LoadNpcAppearance(id);
zone.BroadcastPacketAroundActor(this, CreateAppearancePacket());
}
public void LoadNpcAppearance(uint id)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
string query = @"
SELECT
base,
size,
hairStyle,
hairHighlightColor,
hairVariation,
faceType,
characteristics,
characteristicsColor,
faceEyebrows,
faceIrisSize,
faceEyeShape,
faceNose,
faceFeatures,
faceMouth,
ears,
hairColor,
skinColor,
eyeColor,
voice,
mainHand,
offHand,
spMainHand,
spOffHand,
throwing,
pack,
pouch,
head,
body,
legs,
hands,
feet,
waist,
neck,
leftEar,
rightEar,
leftIndex,
rightIndex,
leftFinger,
rightFinger
FROM gamedata_actor_appearance
WHERE id = @templateId
";
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.Parameters.AddWithValue("@templateId", id);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
//Handle Appearance
modelId = reader.GetUInt32(0);
appearanceIds[Character.SIZE] = reader.GetUInt32(1);
appearanceIds[Character.COLORINFO] = (uint)(reader.GetUInt32(16) | (reader.GetUInt32(15) << 10) | (reader.GetUInt32(17) << 20)); //17 - Skin Color, 16 - Hair Color, 18 - Eye Color
appearanceIds[Character.FACEINFO] = PrimitiveConversion.ToUInt32(CharacterUtils.GetFaceInfo(reader.GetByte(6), reader.GetByte(7), reader.GetByte(5), reader.GetByte(14), reader.GetByte(13), reader.GetByte(12), reader.GetByte(11), reader.GetByte(10), reader.GetByte(9), reader.GetByte(8)));
appearanceIds[Character.HIGHLIGHT_HAIR] = (uint)(reader.GetUInt32(3) | reader.GetUInt32(2) << 10); //5- Hair Highlight, 4 - Hair Style
appearanceIds[Character.VOICE] = reader.GetUInt32(17);
appearanceIds[Character.MAINHAND] = reader.GetUInt32(19);
appearanceIds[Character.OFFHAND] = reader.GetUInt32(20);
appearanceIds[Character.SPMAINHAND] = reader.GetUInt32(21);
appearanceIds[Character.SPOFFHAND] = reader.GetUInt32(22);
appearanceIds[Character.THROWING] = reader.GetUInt32(23);
appearanceIds[Character.PACK] = reader.GetUInt32(24);
appearanceIds[Character.POUCH] = reader.GetUInt32(25);
appearanceIds[Character.HEADGEAR] = reader.GetUInt32(26);
appearanceIds[Character.BODYGEAR] = reader.GetUInt32(27);
appearanceIds[Character.LEGSGEAR] = reader.GetUInt32(28);
appearanceIds[Character.HANDSGEAR] = reader.GetUInt32(29);
appearanceIds[Character.FEETGEAR] = reader.GetUInt32(30);
appearanceIds[Character.WAISTGEAR] = reader.GetUInt32(31);
appearanceIds[Character.NECKGEAR] = reader.GetUInt32(32);
appearanceIds[Character.R_EAR] = reader.GetUInt32(33);
appearanceIds[Character.L_EAR] = reader.GetUInt32(34);
appearanceIds[Character.R_INDEXFINGER] = reader.GetUInt32(35);
appearanceIds[Character.L_INDEXFINGER] = reader.GetUInt32(36);
appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(37);
appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(38);
}
}
}
catch (MySqlException e)
{ Console.WriteLine(e); }
finally
{
conn.Dispose();
}
}
}
public void LoadEventConditions(string eventConditions)
{
EventList conditions = JsonConvert.DeserializeObject<EventList>(eventConditions);
this.eventConditions = conditions;
}
public void DoOnActorSpawn(Player player)
{
LuaEngine.GetInstance().CallLuaFunction(player, this, "onSpawn", true);
}
public void PlayMapObjAnimation(Player player, string animationName)
{
player.QueuePacket(PlayBGAnimation.BuildPacket(actorId, animationName));
}
public void Despawn()
{
zone.DespawnActor(this);
}
public void Update(double deltaTime)
{
LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime);
}
//A party member list packet came, set the party
/* public void SetParty(MonsterPartyGroup group)
{
if (group is MonsterPartyGroup)
currentParty = group;
}
*/
}
}

View File

@@ -0,0 +1,10 @@
namespace FFXIVClassic_Map_Server.Actors.Chara
{
class NpcWork
{
public ushort pushCommand;
public int pushCommandSub;
public byte pushCommandPriority;
public byte hateType = 1;
}
}

View File

@@ -0,0 +1,234 @@
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.actor.inventory;
using System.Collections.Generic;
namespace FFXIVClassic_Map_Server.actors.chara.player
{
class Equipment
{
public const int SLOT_MAINHAND = 0;
public const int SLOT_OFFHAND = 1;
public const int SLOT_THROWINGWEAPON = 4;
public const int SLOT_PACK = 5;
public const int SLOT_POUCH = 6;
public const int SLOT_HEAD = 8;
public const int SLOT_UNDERSHIRT = 9;
public const int SLOT_BODY = 10;
public const int SLOT_UNDERGARMENT = 11;
public const int SLOT_LEGS = 12;
public const int SLOT_HANDS = 13;
public const int SLOT_BOOTS = 14;
public const int SLOT_WAIST = 15;
public const int SLOT_NECK = 16;
public const int SLOT_EARS = 17;
public const int SLOT_WRISTS = 19;
public const int SLOT_RIGHTFINGER = 21;
public const int SLOT_LEFTFINGER = 22;
private Player owner;
private ushort inventoryCapacity;
private ushort inventoryCode;
private InventoryItem[] list;
private Inventory normalInventory;
private bool writeToDB = true;
public Equipment(Player ownerPlayer, Inventory normalInventory, ushort capacity, ushort code)
{
owner = ownerPlayer;
inventoryCapacity = capacity;
inventoryCode = code;
list = new InventoryItem[inventoryCapacity];
this.normalInventory = normalInventory;
}
public InventoryItem GetItemAtSlot(ushort slot)
{
if (slot < list.Length)
return list[slot];
else
return null;
}
public void SendCheckEquipmentToPlayer(Player toPlayer)
{
List<InventoryItem> items = new List<InventoryItem>();
for (ushort i = 0; i < list.Length; i++)
{
if (list[i] != null)
{
InventoryItem equipItem = new InventoryItem(list[i], i);
items.Add(equipItem);
}
}
toPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, 0x23, Inventory.EQUIPMENT_OTHERPLAYER));
int currentIndex = 0;
while (true)
{
if (items.Count - currentIndex >= 16)
toPlayer.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex > 1)
toPlayer.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex == 1)
{
toPlayer.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex]));
currentIndex++;
}
else
break;
}
toPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
public void SendFullEquipment(bool DoClear)
{
List<ushort> slotsToUpdate = new List<ushort>();
for (ushort i = 0; i < list.Length; i++)
{
if (list[i] == null && DoClear)
slotsToUpdate.Add(0);
else if (list[i] != null)
slotsToUpdate.Add(i);
}
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendEquipmentPackets(slotsToUpdate);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
public void SetEquipment(ushort[] slots, ushort[] itemSlots)
{
if (slots.Length != itemSlots.Length)
return;
for (int i = 0; i < slots.Length; i++)
{
InventoryItem item = normalInventory.GetItemBySlot(itemSlots[i]);
if (item == null)
continue;
Database.EquipItem(owner, slots[i], item.uniqueId);
list[slots[i]] = normalInventory.GetItemBySlot(itemSlots[i]);
}
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
SendFullEquipment(false);
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
}
public void SetEquipment(InventoryItem[] toEquip)
{
List<ushort> slotsToUpdate = new List<ushort>();
for (ushort i = 0; i < toEquip.Length; i++)
{
if (toEquip[i] != null)
slotsToUpdate.Add(i);
}
list = toEquip;
}
public void Equip(ushort slot, ushort invSlot)
{
InventoryItem item = normalInventory.GetItemBySlot(invSlot);
if (item == null)
return;
Equip(slot, item);
}
public void Equip(ushort slot, InventoryItem item)
{
if (slot >= list.Length)
return;
if (writeToDB)
Database.EquipItem(owner, slot, item.uniqueId);
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
if (list[slot] != null)
normalInventory.RefreshItem(list[slot], item);
else
normalInventory.RefreshItem(item);
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendEquipmentPackets(slot, item);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
list[slot] = item;
}
public void ToggleDBWrite(bool flag)
{
writeToDB = flag;
}
public void Unequip(ushort slot)
{
if (slot >= list.Length)
return;
if (writeToDB)
Database.UnequipItem(owner, slot);
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
normalInventory.RefreshItem(list[slot]);
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendEquipmentPackets(slot, null);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
list[slot] = null;
}
private void SendEquipmentPackets(ushort equipSlot, InventoryItem item)
{
if (item == null)
owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, equipSlot));
else
owner.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, equipSlot, item.slot));
}
private void SendEquipmentPackets(List<ushort> slotsToUpdate)
{
int currentIndex = 0;
while (true)
{
if (slotsToUpdate.Count - currentIndex >= 64)
owner.QueuePacket(EquipmentListX64Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex));
else if (slotsToUpdate.Count - currentIndex >= 32)
owner.QueuePacket(EquipmentListX32Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex));
else if (slotsToUpdate.Count - currentIndex >= 16)
owner.QueuePacket(EquipmentListX16Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex));
else if (slotsToUpdate.Count - currentIndex > 1)
owner.QueuePacket(EquipmentListX08Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex));
else if (slotsToUpdate.Count - currentIndex == 1)
{
owner.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, slotsToUpdate[currentIndex], list[slotsToUpdate[currentIndex]].slot));
currentIndex++;
}
else
break;
}
}
public int GetCapacity()
{
return list.Length;
}
}
}

View File

@@ -0,0 +1,511 @@

using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.actor.inventory;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FFXIVClassic_Map_Server.actors.chara.player
{
class Inventory
{
public const ushort NORMAL = 0x0000; //Max 0xC8
public const ushort LOOT = 0x0004; //Max 0xA
public const ushort MELDREQUEST = 0x0005; //Max 0x04
public const ushort BAZAAR = 0x0007; //Max 0x0A
public const ushort CURRENCY = 0x0063; //Max 0x140
public const ushort KEYITEMS = 0x0064; //Max 0x500
public const ushort EQUIPMENT = 0x00FE; //Max 0x23
public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23
private Player owner;
private ushort inventoryCapacity;
private ushort inventoryCode;
private List<InventoryItem> list;
public Inventory(Player ownerPlayer, ushort capacity, ushort code)
{
owner = ownerPlayer;
inventoryCapacity = capacity;
inventoryCode = code;
}
#region Inventory Management
public void InitList(List<InventoryItem> itemsFromDB)
{
list = itemsFromDB;
}
public InventoryItem GetItemBySlot(ushort slot)
{
if (slot < list.Count)
return list[slot];
else
return null;
}
public InventoryItem GetItemByUniqueId(ulong uniqueItemId)
{
foreach (InventoryItem item in list)
{
if (item.uniqueId == uniqueItemId)
return item;
}
return null;
}
public InventoryItem GetItemByCatelogId(ulong catelogId)
{
foreach (InventoryItem item in list)
{
if (item.itemId == catelogId)
return item;
}
return null;
}
public void RefreshItem(InventoryItem item)
{
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(item);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
public void RefreshItem(params InventoryItem[] items)
{
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(items.ToList());
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
public void RefreshItem(List<InventoryItem> items)
{
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(items);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
public void AddItem(uint itemId)
{
AddItem(itemId, 1, 1);
}
public void AddItem(uint itemId, int quantity)
{
AddItem(itemId, quantity, 1);
}
public bool AddItem(uint itemId, int quantity, byte quality)
{
if (!IsSpaceForAdd(itemId, quantity))
return false;
ItemData gItem = Server.GetItemGamedata(itemId);
List<ushort> slotsToUpdate = new List<ushort>();
List<SubPacket> addItemPackets = new List<SubPacket>();
if (gItem == null)
{
Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId);
return false;
}
//Check if item id exists
int quantityCount = quantity;
for (int i = 0; i < list.Count; i++)
{
InventoryItem item = list[i];
if (item.itemId == itemId && item.quantity < gItem.maxStack)
{
slotsToUpdate.Add(item.slot);
int oldQuantity = item.quantity;
item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack);
quantityCount -= (gItem.maxStack - oldQuantity);
if (quantityCount <= 0)
break;
}
}
//If it's unique, abort
//if (quantityCount > 0 && storedItem.isUnique)
// return ITEMERROR_UNIQUE;
//If Inventory is full
//if (quantityCount > 0 && isInventoryFull())
// return ITEMERROR_FULL;
//Update lists and db
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
//These had their quantities Changed
foreach (ushort slot in slotsToUpdate)
{
Database.SetQuantity(owner, slot, inventoryCode, list[slot].quantity);
if (inventoryCode != CURRENCY && inventoryCode != KEYITEMS)
SendInventoryPackets(list[slot]);
}
//New item that spilled over
while (quantityCount > 0)
{
InventoryItem addedItem = Database.AddItem(owner, itemId, Math.Min(quantityCount, gItem.maxStack), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode);
list.Add(addedItem);
if (inventoryCode != CURRENCY && inventoryCode != KEYITEMS)
SendInventoryPackets(addedItem);
quantityCount -= gItem.maxStack;
}
if (inventoryCode == CURRENCY || inventoryCode == KEYITEMS)
SendFullInventory();
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
return true;
}
public void AddItem(uint[] itemId)
{
if (!IsSpaceForAdd(itemId[0], itemId.Length))
return;
//Update lists and db
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
int startPos = list.Count;
//New item that spilled over
for (int i = 0; i < itemId.Length; i++)
{
ItemData gItem = Server.GetItemGamedata(itemId[i]);
InventoryItem addedItem = Database.AddItem(owner, itemId[i], 1, (byte)1, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode);
list.Add(addedItem);
}
SendInventoryPackets(startPos);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
}
public void RemoveItem(uint itemId, int quantity)
{
if (!HasItem(itemId, quantity))
return;
List<ushort> slotsToUpdate = new List<ushort>();
List<InventoryItem> itemsToRemove = new List<InventoryItem>();
List<ushort> slotsToRemove = new List<ushort>();
List<SubPacket> AddItemPackets = new List<SubPacket>();
//Remove as we go along
int quantityCount = quantity;
ushort lowestSlot = 0;
for (int i = list.Count - 1; i >= 0; i--)
{
InventoryItem item = list[i];
if (item.itemId == itemId)
{
int oldQuantity = item.quantity;
//Stack nomnomed
if (item.quantity - quantityCount <= 0)
{
itemsToRemove.Add(item);
slotsToRemove.Add(item.slot);
}
else
{
slotsToUpdate.Add(item.slot);
item.quantity -= quantityCount; //Stack reduced
}
quantityCount -= oldQuantity;
lowestSlot = item.slot;
if (quantityCount <= 0)
break;
}
}
for (int i = 0; i < slotsToUpdate.Count; i++)
{
Database.SetQuantity(owner, slotsToUpdate[i], inventoryCode, list[slotsToUpdate[i]].quantity);
}
int oldListSize = list.Count;
for (int i = 0; i < itemsToRemove.Count; i++)
{
Database.RemoveItem(owner, itemsToRemove[i].uniqueId, inventoryCode);
list.Remove(itemsToRemove[i]);
}
//Realign slots
for (int i = lowestSlot; i < list.Count; i++)
list[i].slot = (ushort)i;
//Added tail end items that need to be cleared for slot realignment
for (int i = oldListSize-1; i >= oldListSize - itemsToRemove.Count; i--)
{
if (!slotsToRemove.Contains((ushort)i))
slotsToRemove.Add((ushort)i);
}
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(lowestSlot);
SendInventoryRemovePackets(slotsToRemove);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
if (inventoryCode == NORMAL)
owner.GetEquipment().SendFullEquipment(false);
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
}
public void RemoveItem(ulong itemDBId)
{
ushort slot = 0;
InventoryItem toDelete = null;
foreach (InventoryItem item in list)
{
if (item.uniqueId == itemDBId)
{
toDelete = item;
break;
}
slot++;
}
if (toDelete == null)
return;
int oldListSize = list.Count;
list.RemoveAt(slot);
Database.RemoveItem(owner, itemDBId, inventoryCode);
//Realign slots
for (int i = slot; i < list.Count; i++)
list[i].slot = (ushort)i;
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(slot);
SendInventoryRemovePackets(slot);
if (slot != oldListSize - 1)
SendInventoryRemovePackets((ushort)(oldListSize - 1));
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
if (inventoryCode == NORMAL)
owner.GetEquipment().SendFullEquipment(false);
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
}
public void RemoveItem(ushort slot)
{
if (slot >= list.Count)
return;
int oldListSize = list.Count;
list.RemoveAt((int)slot);
Database.RemoveItem(owner, slot, inventoryCode);
//Realign slots
for (int i = slot; i < list.Count; i++)
list[i].slot = (ushort)i;
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(slot);
SendInventoryRemovePackets(slot);
if (slot != oldListSize - 1)
SendInventoryRemovePackets((ushort)(oldListSize - 1));
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
if (inventoryCode == NORMAL)
owner.GetEquipment().SendFullEquipment(false);
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
}
public void ChangeDurability(uint slot, uint durabilityChange)
{
}
public void ChangeSpiritBind(uint slot, uint spiritBindChange)
{
}
public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId)
{
}
#endregion
#region Packet Functions
public void SendFullInventory()
{
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode));
SendInventoryPackets(0);
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
}
private void SendInventoryPackets(InventoryItem item)
{
owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item));
}
private void SendInventoryPackets(List<InventoryItem> items)
{
int currentIndex = 0;
while (true)
{
if (items.Count - currentIndex >= 64)
owner.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex >= 32)
owner.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex >= 16)
owner.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex > 1)
owner.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex));
else if (items.Count - currentIndex == 1)
{
owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex]));
currentIndex++;
}
else
break;
}
}
private void SendInventoryPackets(int startOffset)
{
int currentIndex = startOffset;
while (true)
{
if (list.Count - currentIndex >= 64)
owner.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, list, ref currentIndex));
else if (list.Count - currentIndex >= 32)
owner.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, list, ref currentIndex));
else if (list.Count - currentIndex >= 16)
owner.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, list, ref currentIndex));
else if (list.Count - currentIndex > 1)
owner.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, list, ref currentIndex));
else if (list.Count - currentIndex == 1)
{
owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex]));
currentIndex++;
}
else
break;
}
}
private void SendInventoryRemovePackets(ushort index)
{
owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index));
}
private void SendInventoryRemovePackets(List<ushort> indexes)
{
int currentIndex = 0;
while (true)
{
if (indexes.Count - currentIndex >= 64)
owner.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
else if (indexes.Count - currentIndex >= 32)
owner.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
else if (indexes.Count - currentIndex >= 16)
owner.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
else if (indexes.Count - currentIndex > 1)
owner.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
else if (indexes.Count - currentIndex == 1)
{
owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex]));
currentIndex++;
}
else
break;
}
}
#endregion
#region Inventory Utils
public bool IsFull()
{
return list.Count >= inventoryCapacity;
}
public bool IsSpaceForAdd(uint itemId, int quantity)
{
int quantityCount = quantity;
for (int i = 0; i < list.Count; i++)
{
InventoryItem item = list[i];
ItemData gItem = Server.GetItemGamedata(item.itemId);
if (item.itemId == itemId && item.quantity < gItem.maxStack)
{
quantityCount -= (gItem.maxStack - item.quantity);
if (quantityCount <= 0)
break;
}
}
return quantityCount <= 0 || (quantityCount > 0 && !IsFull());
}
public bool HasItem(uint itemId)
{
return HasItem(itemId, 1);
}
public bool HasItem(uint itemId, int minQuantity)
{
int count = 0;
foreach (InventoryItem item in list)
{
if (item.itemId == itemId)
count += item.quantity;
if (count >= minQuantity)
return true;
}
return false;
}
public int GetNextEmptySlot()
{
return list.Count == 0 ? 0 : list.Count();
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.chara
namespace FFXIVClassic_Map_Server.dataobjects.chara
{
class PlayerWork
{
@@ -14,21 +8,21 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public byte birthdayDay;
public byte initialTown;
public int restBonusExpRate;
public float restBonusExpRate = 1.5f;
public uint[] questScenario = new uint[16];
public uint[] questGuildLeve = new uint[8];
public uint[] questGuildleve = new uint[8];
public int questScenarioComplete;
public int questGuildleveComplete;
public bool[] questScenarioComplete = new bool[2048];
public bool[] questGuildleveComplete = new bool[2048];
public bool isContentsCommand;
public int castCommandClient;
public int castEndClient;
public uint castCommandClient;
public uint castEndClient;
public int[] comboNextCommandId = new int[2];
public int comboCostBonusRate;
public float comboCostBonusRate;
public bool isRemainBonusPoint;
@@ -36,15 +30,15 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara
public bool[] npcLinkshellChatExtra = new bool[64];
public int variableCommandConfirmWarp;
public int variableCommandConfirmWarpSender;
public string variableCommandConfirmWarpSender;
public int variableCommandConfirmWarpSenderByID;
public int variableCommandConfirmWarpSenderSex;
public byte variableCommandConfirmWarpSenderSex;
public int variableCommandConfirmWarpPlace;
public int variableCommandConfirmRaise;
public int variableCommandConfirmRaiseSender;
public string variableCommandConfirmRaiseSender;
public int variableCommandConfirmRaiseSenderByID;
public int variableCommandConfirmRaiseSenderSex;
public byte variableCommandConfirmRaiseSenderSex;
public int variableCommandConfirmRaisePlace;
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.dataobjects.actors
namespace FFXIVClassic_Map_Server.Actors
{
class Command : Actor
{

View File

@@ -0,0 +1,42 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using System.Collections.Generic;
namespace FFXIVClassic_Map_Server.Actors
{
class DebugProg : Actor
{
public DebugProg()
: base(0x5FF80002)
{
this.displayNameId = 0;
this.customDisplayName = "debug";
this.actorName = "debug";
this.className = "Debug";
}
public override SubPacket CreateScriptBindPacket()
{
List<LuaParam> lParams;
lParams = LuaUtils.CreateLuaParamList("/System/Debug.prog", false, false, false, false, true, 0xC51F, true, true);
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
}
public override List<SubPacket> GetSpawnPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(0));
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0x1));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket());
return subpackets;
}
}
}

View File

@@ -0,0 +1,321 @@

using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.area;
using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using MoonSharp.Interpreter;
using System;
using System.Collections.Generic;
using System.IO;
namespace FFXIVClassic_Map_Server.actors.director
{
class Director : Actor
{
private uint directorId;
private string directorScriptPath;
private List<Actor> members = new List<Actor>();
protected ContentGroup contentGroup;
private bool isCreated = false;
private bool isDeleted = false;
private bool isDeleting = false;
private Script directorScript;
private Coroutine currentCoroutine;
public Director(uint id, Area zone, string directorPath, bool hasContentGroup, params object[] args)
: base((6 << 28 | zone.actorId << 19 | (uint)id))
{
directorId = id;
this.zone = zone;
this.zoneId = zone.actorId;
directorScriptPath = directorPath;
LoadLuaScript();
if (hasContentGroup)
contentGroup = Server.GetWorldManager().CreateContentGroup(this, GetMembers());
eventConditions = new EventList();
eventConditions.noticeEventConditions = new List<EventList.NoticeEventCondition>();
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("noticeEvent", 0xE,0x0));
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("noticeRequest", 0x0, 0x1));
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("reqForChild", 0x0, 0x1));
}
public override SubPacket CreateScriptBindPacket()
{
List<LuaParam> actualLParams = new List<LuaParam>();
actualLParams.Insert(0, new LuaParam(2, classPath));
actualLParams.Insert(1, new LuaParam(4, 4));
actualLParams.Insert(2, new LuaParam(4, 4));
actualLParams.Insert(3, new LuaParam(4, 4));
actualLParams.Insert(4, new LuaParam(4, 4));
actualLParams.Insert(5, new LuaParam(4, 4));
List<LuaParam> lparams = LuaEngine.GetInstance().CallLuaFunctionForReturn(null, this, "init", false);
for (int i = 1; i < lparams.Count; i++)
actualLParams.Add(lparams[i]);
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, actualLParams);
}
public override List<SubPacket> GetSpawnPackets(ushort spawnType = 1)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(0));
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket());
return subpackets;
}
public override List<SubPacket> GetInitPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
SetActorPropetyPacket initProperties = new SetActorPropetyPacket("/_init");
initProperties.AddTarget();
subpackets.Add(initProperties.BuildPacket(actorId));
return subpackets;
}
public void OnTalkEvent(Player player, Npc npc)
{
LuaEngine.GetInstance().CallLuaFunction(player, this, "onTalkEvent", false, npc);
}
public void OnCommandEvent(Player player, Command command)
{
LuaEngine.GetInstance().CallLuaFunction(player, this, "onCommandEvent", false, command);
}
public void StartDirector(bool spawnImmediate, params object[] args)
{
object[] args2 = new object[args.Length + 1];
args2[0] = this;
Array.Copy(args, 0, args2, 1, args.Length);
List<LuaParam> lparams = CallLuaScript("init", args2);
if (lparams.Count >= 1 && lparams[0].value is string)
{
classPath = (string)lparams[0].value;
className = classPath.Substring(classPath.LastIndexOf("/") + 1);
GenerateActorName((int)directorId);
isCreated = true;
}
if (isCreated && spawnImmediate)
{
if (contentGroup != null)
contentGroup.Start();
foreach (Player p in GetPlayerMembers())
{
p.QueuePackets(GetSpawnPackets());
p.QueuePackets(GetInitPackets());
}
}
if (this is GuildleveDirector)
{
((GuildleveDirector)this).LoadGuildleve();
}
StartCoroutine("main", this);
}
public void StartContentGroup()
{
if (contentGroup != null)
contentGroup.Start();
}
public void EndDirector()
{
isDeleting = true;
if (contentGroup != null)
contentGroup.DeleteGroup();
if (this is GuildleveDirector)
((GuildleveDirector)this).EndGuildleveDirector();
List<Actor> players = GetPlayerMembers();
foreach (Actor player in players)
((Player)player).RemoveDirector(this);
members.Clear();
isDeleted = true;
Server.GetWorldManager().GetZone(zoneId).DeleteDirector(actorId);
}
public void AddMember(Actor actor)
{
if (!members.Contains(actor))
{
members.Add(actor);
if (contentGroup != null)
contentGroup.AddMember(actor);
}
}
public void RemoveMember(Actor actor)
{
if (members.Contains(actor))
members.Remove(actor);
if (contentGroup != null)
contentGroup.RemoveMember(actor.actorId);
if (GetPlayerMembers().Count == 0 && !isDeleting)
EndDirector();
}
public List<Actor> GetMembers()
{
return members;
}
public List<Actor> GetPlayerMembers()
{
return members.FindAll(s => s is Player);
}
public List<Actor> GetNpcMembers()
{
return members.FindAll(s => s is Npc);
}
public bool IsCreated()
{
return isCreated;
}
public bool IsDeleted()
{
return isDeleted;
}
public bool HasContentGroup()
{
return contentGroup != null;
}
public ContentGroup GetContentGroup()
{
return contentGroup;
}
public void GenerateActorName(int actorNumber)
{
//Format Class Name
string className = this.className;
className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
//Format Zone Name
string zoneName = zone.zoneName.Replace("Field", "Fld")
.Replace("Dungeon", "Dgn")
.Replace("Town", "Twn")
.Replace("Battle", "Btl")
.Replace("Test", "Tes")
.Replace("Event", "Evt")
.Replace("Ship", "Shp")
.Replace("Office", "Ofc");
if (zone is PrivateArea)
{
//Check if "normal"
zoneName = zoneName.Remove(zoneName.Length - 1, 1) + "P";
}
zoneName = Char.ToLowerInvariant(zoneName[0]) + zoneName.Substring(1);
try
{
className = className.Substring(0, 20 - zoneName.Length);
}
catch (ArgumentOutOfRangeException e)
{ }
//Convert actor number to base 63
string classNumber = Utils.ToStringBase63(actorNumber);
//Get stuff after @
uint zoneId = zone.actorId;
uint privLevel = 0;
if (zone is PrivateArea)
privLevel = ((PrivateArea)zone).GetPrivateAreaType();
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
}
public string GetScriptPath()
{
return directorScriptPath;
}
private void LoadLuaScript()
{
string luaPath = String.Format(LuaEngine.FILEPATH_DIRECTORS, GetScriptPath());
directorScript = LuaEngine.LoadScript(luaPath);
if (directorScript == null)
Program.Log.Error("Could not find script for director {0}.", GetName());
}
private List<LuaParam> CallLuaScript(string funcName, params object[] args)
{
if (directorScript != null)
{
if (!directorScript.Globals.Get(funcName).IsNil())
{
DynValue result = directorScript.Call(directorScript.Globals[funcName], args);
List<LuaParam> lparams = LuaUtils.CreateLuaParamList(result);
return lparams;
}
else
Program.Log.Error("Could not find script for director {0}.", GetName());
}
return null;
}
private List<LuaParam> StartCoroutine(string funcName, params object[] args)
{
if (directorScript != null)
{
if (!directorScript.Globals.Get(funcName).IsNil())
{
currentCoroutine = directorScript.CreateCoroutine(directorScript.Globals[funcName]).Coroutine;
DynValue value = currentCoroutine.Resume(args);
LuaEngine.GetInstance().ResolveResume(null, currentCoroutine, value);
}
else
Program.Log.Error("Could not find script for director {0}.", GetName());
}
return null;
}
public void OnEventStart(Player player, object[] args)
{
object[] args2 = new object[args.Length + (player == null ? 1 : 2)];
Array.Copy(args, 0, args2, (player == null ? 1 : 2), args.Length);
if (player != null)
{
args2[0] = player;
args2[1] = this;
}
else
args2[0] = this;
Coroutine coroutine = directorScript.CreateCoroutine(directorScript.Globals["onEventStarted"]).Coroutine;
DynValue value = coroutine.Resume(args2);
LuaEngine.GetInstance().ResolveResume(player, coroutine, value);
}
}
}

View File

@@ -0,0 +1,256 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.area;
using FFXIVClassic_Map_Server.actors.director.Work;
using FFXIVClassic_Map_Server.actors.group;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.director
{
class GuildleveDirector : Director
{
public uint guildleveId;
public Player guildleveOwner;
public byte selectedDifficulty;
public GuildleveData guildleveData;
public GuildleveWork guildleveWork = new GuildleveWork();
public bool isEnded = false;
public uint completionTime = 0;
public GuildleveDirector(uint id, Area zone, string directorPath, uint guildleveId, byte selectedDifficulty, Player guildleveOwner, params object[] args)
: base(id, zone, directorPath, true, args)
{
this.guildleveId = guildleveId;
this.selectedDifficulty = selectedDifficulty;
this.guildleveData = Server.GetGuildleveGamedata(guildleveId);
this.guildleveOwner = guildleveOwner;
guildleveWork.aimNum[0] = guildleveData.aimNum[0];
guildleveWork.aimNum[1] = guildleveData.aimNum[1];
guildleveWork.aimNum[2] = guildleveData.aimNum[2];
guildleveWork.aimNum[3] = guildleveData.aimNum[3];
if (guildleveWork.aimNum[0] != 0)
guildleveWork.uiState[0] = 1;
if (guildleveWork.aimNum[1] != 0)
guildleveWork.uiState[1] = 1;
if (guildleveWork.aimNum[2] != 0)
guildleveWork.uiState[2] = 1;
if (guildleveWork.aimNum[3] != 0)
guildleveWork.uiState[3] = 1;
guildleveWork.aimNumNow[0] = guildleveWork.aimNumNow[1] = guildleveWork.aimNumNow[2] = guildleveWork.aimNumNow[3] = 0;
}
public void LoadGuildleve()
{
}
public void StartGuildleve()
{
foreach (Actor p in GetPlayerMembers())
{
Player player = (Player) p;
//Set music
if (guildleveData.location == 1)
player.ChangeMusic(22);
else if (guildleveData.location == 2)
player.ChangeMusic(14);
else if (guildleveData.location == 3)
player.ChangeMusic(26);
else if (guildleveData.location == 4)
player.ChangeMusic(16);
//Show Start Messages
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50022, 0x20, guildleveId, selectedDifficulty);
player.SendDataPacket("attention", Server.GetWorldManager().GetActor(), "", 50022, guildleveId, selectedDifficulty);
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50026, 0x20, (object)(int)guildleveData.timeLimit);
}
guildleveWork.startTime = Utils.UnixTimeStampUTC();
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/start", this);
propertyBuilder.AddProperty("guildleveWork.startTime");
SendPacketsToPlayers(propertyBuilder.Done());
}
public void EndGuildleve(bool wasCompleted)
{
if (isEnded)
return;
isEnded = true;
completionTime = Utils.UnixTimeStampUTC() - guildleveWork.startTime;
if (wasCompleted)
{
foreach (Actor a in GetPlayerMembers())
{
Player player = (Player)a;
player.MarkGuildleve(guildleveId, true, true);
player.PlayAnimation(0x02000002, true);
player.ChangeMusic(81);
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50023, 0x20, (object)(int)guildleveId);
player.SendDataPacket("attention", Server.GetWorldManager().GetActor(), "", 50023, (object)(int)guildleveId);
}
}
foreach (Actor a in GetNpcMembers())
{
Npc npc = (Npc)a;
npc.Despawn();
RemoveMember(a);
}
guildleveWork.startTime = 0;
guildleveWork.signal = -1;
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/signal", this);
propertyBuilder.AddProperty("guildleveWork.signal");
propertyBuilder.NewTarget("guildleveWork/start");
propertyBuilder.AddProperty("guildleveWork.startTime");
SendPacketsToPlayers(propertyBuilder.Done());
if (wasCompleted)
{
Npc aetheryteNode = zone.SpawnActor(1200040, String.Format("{0}:warpExit", guildleveOwner.actorName), guildleveOwner.positionX, guildleveOwner.positionY, guildleveOwner.positionZ);
AddMember(aetheryteNode);
foreach (Actor a in GetPlayerMembers())
{
Player player = (Player)a;
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50029, 0x20);
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50032, 0x20);
}
}
}
public void AbandonGuildleve()
{
foreach (Actor p in GetPlayerMembers())
{
Player player = (Player)p;
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50147, 0x20, (object)guildleveId);
player.MarkGuildleve(guildleveId, true, false);
}
EndGuildleve(false);
EndDirector();
}
//Delete ContentGroup, change music back
public void EndGuildleveDirector()
{
foreach (Actor p in GetPlayerMembers())
{
Player player = (Player)p;
player.ChangeMusic(player.GetZone().bgmDay);
}
}
public void SyncAllInfo()
{
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
if (guildleveWork.aimNum[0] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNum[0]");
if (guildleveWork.aimNum[1] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNum[1]");
if (guildleveWork.aimNum[2] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNum[2]");
if (guildleveWork.aimNum[3] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNum[3]");
if (guildleveWork.aimNumNow[0] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNumNow[0]");
if (guildleveWork.aimNumNow[1] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNumNow[1]");
if (guildleveWork.aimNumNow[2] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNumNow[2]");
if (guildleveWork.aimNumNow[3] != 0)
propertyBuilder.AddProperty("guildleveWork.aimNumNow[3]");
if (guildleveWork.uiState[0] != 0)
propertyBuilder.AddProperty("guildleveWork.uiState[0]");
if (guildleveWork.uiState[1] != 0)
propertyBuilder.AddProperty("guildleveWork.uiState[1]");
if (guildleveWork.uiState[2] != 0)
propertyBuilder.AddProperty("guildleveWork.uiState[2]");
if (guildleveWork.uiState[3] != 0)
propertyBuilder.AddProperty("guildleveWork.uiState[3]");
SendPacketsToPlayers(propertyBuilder.Done());
}
public void UpdateAimNumNow(int index, sbyte value)
{
guildleveWork.aimNumNow[index] = value;
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
propertyBuilder.AddProperty(String.Format("guildleveWork.aimNumNow[{0}]", index));
SendPacketsToPlayers(propertyBuilder.Done());
}
public void UpdateUiState(int index, sbyte value)
{
guildleveWork.uiState[index] = value;
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
propertyBuilder.AddProperty(String.Format("guildleveWork.uiState[{0}]", index));
SendPacketsToPlayers(propertyBuilder.Done());
}
public void UpdateMarkers(int markerIndex, float x, float y, float z)
{
guildleveWork.markerX[markerIndex] = x;
guildleveWork.markerY[markerIndex] = y;
guildleveWork.markerZ[markerIndex] = z;
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/marker", this);
propertyBuilder.AddProperty(String.Format("guildleveWork.markerX[{0}]", markerIndex));
propertyBuilder.AddProperty(String.Format("guildleveWork.markerY[{0}]", markerIndex));
propertyBuilder.AddProperty(String.Format("guildleveWork.markerZ[{0}]", markerIndex));
SendPacketsToPlayers(propertyBuilder.Done());
}
public void SendPacketsToPlayers(List<SubPacket> packets)
{
List<Actor> players = GetPlayerMembers();
foreach (Actor p in players)
{
((Player)p).QueuePackets(packets);
}
}
public static uint GlBorderIconIDToAnimID(uint iconId)
{
return iconId - 20000;
}
public static uint GlPlateIconIDToAnimID(uint iconId)
{
return iconId - 20020;
}
public static uint GetGLStartAnimationFromSheet(uint border, uint plate, bool isBoost)
{
return GetGLStartAnimation(GlBorderIconIDToAnimID(border), GlPlateIconIDToAnimID(plate), isBoost);
}
public static uint GetGLStartAnimation(uint border, uint plate, bool isBoost)
{
uint borderBits = border;
uint plateBits = plate << 7;
uint boostBits = isBoost ? (uint)0x8000 : (uint) 0;
return 0x0B000000 | boostBits | plateBits | borderBits;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.director.Work
{
class GuildleveWork
{
public uint startTime = 0;
public sbyte[] aimNum = new sbyte[4];
public sbyte[] aimNumNow = new sbyte[4];
public sbyte[] uiState = new sbyte[4];
public float[] markerX = new float[3];
public float[] markerY = new float[3];
public float[] markerZ = new float[3];
public sbyte signal;
}
}

View File

@@ -0,0 +1,173 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.director;
using FFXIVClassic_Map_Server.actors.group.Work;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using FFXIVClassic_Map_Server.packets.send.groups;
using FFXIVClassic_Map_Server.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.group
{
class ContentGroup : Group
{
public ContentGroupWork contentGroupWork = new ContentGroupWork();
private Director director;
private List<uint> members = new List<uint>();
private bool isStarted = false;
public ContentGroup(ulong groupIndex, Director director, uint[] initialMembers) : base(groupIndex)
{
if (initialMembers != null)
{
for (int i = 0; i < initialMembers.Length; i++)
{
Session s = Server.GetServer().GetSession(initialMembers[i]);
if (s != null)
s.GetActor().SetCurrentContentGroup(this);
members.Add(initialMembers[i]);
}
}
this.director = director;
contentGroupWork._globalTemp.director = (ulong)director.actorId << 32;
}
public void Start()
{
isStarted = true;
SendGroupPacketsAll(members);
}
public void AddMember(Actor actor)
{
if (actor == null)
return;
members.Add(actor.actorId);
if (actor is Character)
((Character)actor).SetCurrentContentGroup(this);
if (isStarted)
SendGroupPacketsAll(members);
}
public void RemoveMember(uint memberId)
{
members.Remove(memberId);
if (isStarted)
SendGroupPacketsAll(members);
CheckDestroy();
}
public override List<GroupMember> BuildMemberList(uint id)
{
List<GroupMember> groupMembers = new List<GroupMember>();
groupMembers.Add(new GroupMember(id, -1, 0, false, true, ""));
foreach (uint charaId in members)
{
if (charaId != id)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, ""));
}
return groupMembers;
}
public override int GetMemberCount()
{
return members.Count;
}
public override void SendInitWorkValues(Session session)
{
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
groupWork.addProperty(this, "contentGroupWork._globalTemp.director");
groupWork.addByte(Utils.MurmurHash2("contentGroupWork.property[0]", 0), 1);
groupWork.setTarget("/_init");
SubPacket test = groupWork.buildPacket(session.id, session.id);
test.DebugPrintSubPacket();
session.QueuePacket(test);
}
public override void SendGroupPackets(Session session)
{
ulong time = Utils.MilisUnixTimeStampUTC();
List<GroupMember> members = BuildMemberList(session.id);
session.QueuePacket(GroupHeaderPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
session.QueuePacket(GroupMembersBeginPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
int currentIndex = 0;
while (true)
{
if (GetMemberCount() - currentIndex >= 64)
session.QueuePacket(ContentMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex >= 32)
session.QueuePacket(ContentMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex >= 16)
session.QueuePacket(ContentMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex > 0)
session.QueuePacket(ContentMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else
break;
}
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
}
public override uint GetTypeId()
{
return Group.ContentGroup_SimpleContentGroup24B;
}
public void SendAll()
{
SendGroupPacketsAll(members);
}
public void DeleteGroup()
{
SendDeletePackets(members);
for (int i = 0; i < members.Count; i++)
{
Session s = Server.GetServer().GetSession(members[i]);
if (s != null)
s.GetActor().SetCurrentContentGroup(null);
Actor a = director.GetZone().FindActorInArea(members[i]);
if (a is Npc)
((Npc)a).Despawn();
members.Remove(members[i]);
i--;
}
Server.GetWorldManager().DeleteContentGroup(groupIndex);
}
public void CheckDestroy()
{
bool foundSession = false;
foreach (uint memberId in members)
{
Session session = Server.GetServer().GetSession(memberId);
if (session != null)
{
foundSession = true;
break;
}
}
if (!foundSession)
DeleteGroup();
}
}
}

View File

@@ -0,0 +1,29 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.director;
using FFXIVClassic_Map_Server.actors.group.Work;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using FFXIVClassic_Map_Server.packets.send.groups;
using FFXIVClassic_Map_Server.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.group
{
class GLContentGroup : ContentGroup
{
public GLContentGroup(ulong groupIndex, Director director, uint[] initialMembers)
: base(groupIndex, director, initialMembers)
{
}
public override uint GetTypeId()
{
return Group.ContentGroup_GuildleveGroup;
}
}
}

View File

@@ -0,0 +1,157 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using FFXIVClassic_Map_Server.packets.send.groups;
using System;
using System.Collections.Generic;
namespace FFXIVClassic_Map_Server.actors.group
{
class Group
{
public const uint PlayerPartyGroup = 10001;
public const uint CompanyGroup = 20002;
public const uint GroupInvitationRelationGroup = 50001;
public const uint TradeRelationGroup = 50002;
public const uint BazaarBuyItemRelationGroup = 50009;
public const uint RetainerGroup = 80001;
public const uint MonsterPartyGroup = 10002;
public const uint ContentGroup_GuildleveGroup = 30001;
public const uint ContentGroup_PublicPopGroup = 30002;
public const uint ContentGroup_SimpleContentGroup24A = 30003;
public const uint ContentGroup_SimpleContentGroup32A = 30004;
public const uint ContentGroup_SimpleContentGroup128 = 30005;
public const uint ContentGroup_SimpleContentGroup24B = 30006;
public const uint ContentGroup_SimpleContentGroup32B = 30007;
public const uint ContentGroup_RetainerAccessGroup = 30008;
public const uint ContentGroup_SimpleContentGroup99999 = 30009;
public const uint ContentGroup_SimpleContentGroup512 = 30010;
public const uint ContentGroup_SimpleContentGroup64A = 30011;
public const uint ContentGroup_SimpleContentGroup64B = 30012;
public const uint ContentGroup_SimpleContentGroup64C = 30013;
public const uint ContentGroup_SimpleContentGroup64D = 30014;
public const uint ContentGroup_SimpleContentGroup64E = 30015;
public const uint ContentGroup_SimpleContentGroup64F = 30016;
public const uint ContentGroup_SimpleContentGroup64G = 30017;
public const uint ContentGroup_SimpleContentGroup24C = 30018;
public readonly ulong groupIndex;
public Group(ulong groupIndex)
{
this.groupIndex = groupIndex;
}
public virtual int GetMemberCount()
{
return 0;
}
public virtual uint GetTypeId()
{
return 0;
}
public virtual string GetGroupName()
{
return "";
}
public virtual int GetGroupLocalizedName()
{
return -1;
}
public virtual List<GroupMember> BuildMemberList(uint id)
{
return new List<GroupMember>();
}
public void SendGroupPacketsAll(params uint[] ids)
{
for (int i = 0; i < ids.Length; i++)
{
Session session = Server.GetServer().GetSession(ids[i]);
if (session != null)
SendGroupPackets(session);
}
}
public void SendGroupPacketsAll(List<uint> ids)
{
for (int i = 0; i < ids.Count; i++)
{
Session session = Server.GetServer().GetSession(ids[i]);
if (session != null)
SendGroupPackets(session);
}
}
public void SendDeletePackets(params uint[] ids)
{
for (int i = 0; i < ids.Length; i++)
{
Session session = Server.GetServer().GetSession(ids[i]);
if (session != null)
SendDeletePacket(session);
}
}
public void SendDeletePackets(List<uint> ids)
{
for (int i = 0; i < ids.Count; i++)
{
Session session = Server.GetServer().GetSession(ids[i]);
if (session != null)
SendDeletePacket(session);
}
}
public virtual void SendGroupPackets(Session session)
{
ulong time = Utils.MilisUnixTimeStampUTC();
List<GroupMember> members = BuildMemberList(session.id);
session.QueuePacket(GroupHeaderPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
session.QueuePacket(GroupMembersBeginPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
int currentIndex = 0;
while (true)
{
if (GetMemberCount() - currentIndex >= 64)
session.QueuePacket(GroupMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex >= 32)
session.QueuePacket(GroupMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex >= 16)
session.QueuePacket(GroupMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else if (GetMemberCount() - currentIndex > 0)
session.QueuePacket(GroupMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
else
break;
}
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
}
public void SendDeletePacket(Session session)
{
if (session != null)
session.QueuePacket(DeleteGroupPacket.buildPacket(session.id, this));
}
public virtual void SendInitWorkValues(Session session)
{
}
}
}

View File

@@ -0,0 +1,63 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using FFXIVClassic_Map_Server.packets.send.groups;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.group
{
class MonsterParty : Group
{
private List<uint> monsterMembers = new List<uint>();
public MonsterParty(ulong groupIndex, uint[] initialMonsterMembers)
: base(groupIndex)
{
for (int i = 0; i < initialMonsterMembers.Length; i++)
monsterMembers.Add(initialMonsterMembers[i]);
}
public void AddMember(uint memberId)
{
monsterMembers.Add(memberId);
SendGroupPacketsAll(monsterMembers);
}
public void RemoveMember(uint memberId)
{
monsterMembers.Remove(memberId);
SendGroupPacketsAll(monsterMembers);
}
public override List<GroupMember> BuildMemberList(uint id)
{
List<GroupMember> groupMembers = new List<GroupMember>();
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
foreach (uint charaId in monsterMembers)
{
if (charaId != id)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName));
}
return groupMembers;
}
public override void SendInitWorkValues(Session session)
{
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
groupWork.setTarget("/_init");
SubPacket test = groupWork.buildPacket(session.id, session.id);
session.QueuePacket(test);
}
public override uint GetTypeId()
{
return Group.MonsterPartyGroup;
}
}
}

View File

@@ -0,0 +1,74 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.group.Work;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.group
{
class Party : Group
{
public PartyWork partyGroupWork = new PartyWork();
public List<uint> members = new List<uint>();
public Party(ulong groupId, uint leaderCharaId) : base(groupId)
{
partyGroupWork._globalTemp.owner = (ulong)(((ulong)leaderCharaId << 32) | 0xB36F92);
members.Add(leaderCharaId);
}
public void SetLeader(uint actorId)
{
partyGroupWork._globalTemp.owner = (ulong)(((ulong)actorId << 32) | 0xB36F92);
}
public uint GetLeader()
{
return (uint)(((ulong)partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF);
}
public uint GetIdForName(string name)
{
for (int i = 0; i < members.Count; i++)
{
if (Server.GetWorldManager().GetActorInWorld(members[i]).customDisplayName.Equals(name))
{
return members[i];
}
}
return 0;
}
public bool IsInParty(uint charaId)
{
return members.Contains(charaId);
}
public override int GetMemberCount()
{
return members.Count;
}
public override uint GetTypeId()
{
return Group.PlayerPartyGroup;
}
public override List<GroupMember> BuildMemberList(uint id)
{
List<GroupMember> groupMembers = new List<GroupMember>();
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
foreach (uint charaId in members)
{
if (charaId != id)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName));
}
return groupMembers;
}
}
}

View File

@@ -0,0 +1,77 @@
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.actors.group.Work;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.packets.send.group;
using FFXIVClassic_Map_Server.packets.send.groups;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.group
{
class Relation : Group
{
public RelationWork work = new RelationWork();
private uint charaOther;
private ulong topicGroup;
public Relation(ulong groupIndex, uint host, uint other, uint command, ulong topicGroup) : base (groupIndex)
{
this.charaOther = other;
work._globalTemp.host = ((ulong)host << 32) | (0xc17909);
work._globalTemp.variableCommand = command;
this.topicGroup = topicGroup;
}
public uint GetHost()
{
return (uint)(((ulong)work._globalTemp.host >> 32) & 0xFFFFFFFF);
}
public uint GetOther()
{
return charaOther;
}
public override int GetMemberCount()
{
return 2;
}
public override uint GetTypeId()
{
return Group.GroupInvitationRelationGroup;
}
public ulong GetTopicGroupIndex()
{
return topicGroup;
}
public override List<GroupMember> BuildMemberList(uint id)
{
List<GroupMember> groupMembers = new List<GroupMember>();
uint hostId = (uint)((work._globalTemp.host >> 32) & 0xFFFFFFFF);
groupMembers.Add(new GroupMember(hostId, -1, 0, false, Server.GetServer().GetSession(hostId) != null, Server.GetWorldManager().GetActorInWorld(hostId).customDisplayName));
groupMembers.Add(new GroupMember(charaOther, -1, 0, false, Server.GetServer().GetSession(charaOther) != null, Server.GetWorldManager().GetActorInWorld(charaOther).customDisplayName));
return groupMembers;
}
public override void SendInitWorkValues(Session session)
{
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
groupWork.addProperty(this, "work._globalTemp.host");
groupWork.addProperty(this, "work._globalTemp.variableCommand");
groupWork.setTarget("/_init");
SubPacket test = groupWork.buildPacket(session.id, session.id);
test.DebugPrintSubPacket();
session.QueuePacket(test);
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FFXIVClassic_Map_Server.actors.group.Work
{
class ContentGroupWork
{
public GlobalTemp _globalTemp = new GlobalTemp();
public bool[] property = new bool[32];
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace FFXIVClassic_Map_Server.actors.group.Work
{
class GlobalTemp
{
public ulong director;
}
}

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