201 Commits
1.0 ... 1.1

Author SHA1 Message Date
df655dfe29 added mobdata to config 2020-09-07 12:54:40 -05:00
c8c4ec7d01 updated readme & small refactor 2020-09-07 12:23:00 -05:00
458843958b Merge pull request #69 from SengokuNadeko/master
Adjustments made to regex
2020-09-06 14:06:37 -05:00
958d4a79eb Merge pull request #70 from CakeLancelot/stub-npcbarker
Temporarily set npcBarkHandler to a stub
2020-09-06 14:06:08 -05:00
e86860baf7 Merge pull request #71 from FinnHornhoover/mingw-compile-fix
MinGW optimization fix
2020-09-06 14:05:39 -05:00
FinnHornhoover
a8c88a9bd9 disabled unknown pragma warnings 2020-09-06 19:40:13 +03:00
FinnHornhoover
038ce984c5 O3 fix for mingw g++ 2020-09-06 18:52:50 +03:00
SengokuNadeko
361c069d0c Adjustments to the regex
I made the regex a bit less restrictive. If you want, you can push this if it seems appropriate.

Username should be at least 4 characters and max 32
Password should be at least 8 characters and max 32

Usernames can be any combination of letters and numbers, with no special characters except for dash and underscore.

Passwords can use any of the alphanumeric/special characters specified in the regex.
2020-09-06 10:49:14 -05:00
SengokuNadeko
3876e0537e Small regex fix
Old regex had some problems (a bit too restrictive). If you want, you can push this to loosen up the restrictions a little.
2020-09-06 10:42:07 -05:00
CakeLancelot
8a481acdae Stub npcBarkHandler for now 2020-09-06 09:59:43 -05:00
e936cb9fac Merge pull request #67 from darkredtitan/master
Fix social distancing bug (name trademarked by kamilprzyb)
2020-09-05 20:37:53 -05:00
darkredtitan
589da3f714 Fix social distancing bug (name trademarked by kamilprzyb) 2020-09-05 19:53:16 +02:00
266ca8b8c6 temp fix for mingw 2020-09-04 14:12:01 -05:00
00c5e07f4f small memory bugs fixed 2020-09-03 15:29:29 -05:00
05d035717d and here too 2020-09-02 22:49:51 -05:00
f41bf0ace2 switched to -O2 optimizations 2020-09-02 22:49:38 -05:00
063d302bd5 Merge pull request #62 from kamilprzyb/master
Rewrote DB and finished LoginServer
2020-09-02 22:49:08 -05:00
7e9793bf90 Merge pull request #59 from JadeShrineMaiden/additions
Colliding NPC IID quickfix
2020-09-02 22:48:09 -05:00
CakeLancelot
fa6b0b178c Remove unnecessary bracket in include statements 2020-09-02 21:25:09 -05:00
CakeLancelot
dadf1c5bcf Change sqlite3.h path in sqlite_orm.h
Should fix Appveyor compilation
2020-09-02 21:11:50 -05:00
kamilprzyb
359991e274 added back accidentaly removed files in Makefile 2020-09-02 18:05:18 +02:00
kamilprzyb
260759c20b replaced tabs with 4 spaces 2020-09-02 17:53:39 +02:00
kamilprzyb
1ff5694960 Fix APPROVEALLNAMES setting
fixed GetInteger->GetBoolean

Co-authored-by: FinnHornhoover <30576665+FinnHornhoover@users.noreply.github.com>
2020-09-02 17:47:10 +02:00
CakeLancelot
be4c5a8072 Update config.ini
Add acceptallcustomnames option
Correct comment
2020-09-02 07:12:24 -05:00
kamilprzyb
ef84ec8fca remove CMakeSettings 2020-09-02 00:38:33 +02:00
kamilprzyb
11801c1f89 Rewrote DB to use ORM, added all remaining features to LoginServer and refactored it 2020-09-02 00:37:09 +02:00
4f6c77be4f minor cleanup 2020-08-31 17:54:49 -05:00
73c67a814d Fix checking of header timestamps.
This is implemented in such a way that a change in one of our headers
won't cause recompilation of large C dependancies. It's a bit hacky, but
it works.
2020-08-31 22:40:33 +02:00
d7a41d40ab Replace signal() with sigaction(). 2020-08-31 22:40:33 +02:00
0aeac0f6f3 Improve the Makefile so we don't have to recompile the libs every time. 2020-08-31 22:40:33 +02:00
48b0866441 Merge pull request #60 from dongresource/contrib
Wrote a CONTRIBUTING.md with instructions for clean Pull Requests
2020-08-30 22:43:40 -05:00
4ade533f40 Wrote a CONTRIBUTING.md. 2020-08-31 03:47:56 +02:00
1e344c2dd8 Small tweak 2020-08-31 01:36:29 +01:00
fdd0160248 Colliding NPC IID quickfix 2020-08-30 23:29:28 +01:00
5f10718315 Merge pull request #56 from Eperty123/master
Add NPC barking, see saved characters
2020-08-30 10:41:24 -05:00
Eperty123
da293ba9b3 Add DB prefix to db stuff 2020-08-29 13:47:39 +02:00
Eperty123
437063d78a Add experimental TransportManager 2020-08-29 13:43:33 +02:00
Eperty123
b239fb9331 Add NPC barking, seeing saved characters 2020-08-29 13:14:21 +02:00
50431024c9 Merge pull request #55 from dongresource/combat1
Implemented combat, drops, crates and the guide changer
2020-08-28 19:11:56 -05:00
2a258a80f0 Database.cpp refactoring 2020-08-28 19:10:26 -05:00
322bc46604 Support plain POSIX make.
Also standardized the new variable names.
2020-08-28 22:54:28 +02:00
2551f74af1 Merge pull request #54 from CakeLancelot/editorconfig
Add .editorconfig to enforce 4 space indent, final newline, and LF EoL
2020-08-28 15:50:30 -05:00
CakeLancelot
1af240fa34 Add .editorconfig to enforce 4 space indent
Should be supported by most IDEs and text editors
2020-08-28 15:34:49 -05:00
a067975f27 Players can now see eachother fight monsters. 2020-08-28 22:22:24 +02:00
72a811d6ab Implemented guide changing.
This means the Time Machine works as well.
2020-08-28 22:22:24 +02:00
3b35e0017a Moved all JSON files into a dedicated data directory. 2020-08-28 22:22:24 +02:00
4df812f996 Implemented crates (dropping and opening).
Also fixed a bug in vaildOutVarPacket().
2020-08-28 22:22:24 +02:00
67d899efe6 Implemented proper validation of variable-length packets.
Also changed output buffer in pcAttackNpcs() from dynamically to
statically allocated. This in itself is temporary as I have a better
idea as to how we can allocate buffers with a bit less boilerplate.
2020-08-28 22:18:28 +02:00
64accecc30 Initial implementation of CombatManager.
Overflow detection must still be implemented.
2020-08-28 22:18:28 +02:00
c8c2f4b05f Catch SIGINT with signal(), to allow for gprof instrumentation.
Note: signal() is undefined behaviour in multithreaded programs and is
unportable for handling signals in general. This will need to be
replaced with sigaction() or something.
2020-08-28 22:18:28 +02:00
3c43dd0193 Try to transmit FF packets in one go, instead of sending the id first. 2020-08-28 22:18:28 +02:00
9e9161083d Reword some comments and correct paths in the Readme. 2020-08-28 22:18:28 +02:00
darkredtitan
5cf7225f52 Tried to manually merge kamilprzyb and main repo's code (#45)
* Merge kamilprzyb and main repo's code

* Update Makefile by FunnHornhoover

* Update Makefile by FinnHornhoover

* Add flag to Makefile by FinnHornhoover

* Remove extra line from makefile

* Remove lbcrypt from Makefile

* Fix flag to Makefile by FinnHornhoover

* Reimplement potential fix for tutorial blackscreen by Dongresources

* Update CMakeLists.txt

* Update CMakeLists.txt

* Reinsert Jade's changes

* Cosmetic Changes to Databases .h & .cpp

* Remove CMakeSettings.json

* Update Makefile by Finn Hornhoover

* More cosmetic changes to Databases.cpp

* More cosmetic changes to Databases.cpp

* Remove unnecessary line (CMakeSettings.json)

* Fix CNLoginServer.cpp

* More cosmetic Changes to Database.hpp, edit Database.cpp to use JSON library onstead of json11 library, and delete json11 library files

* Delete json11 library files

* Delete JSON library to reupload

* Reupload JSON library from main repo

* Reupload JSON library from main repo

* Fix syntax error

* Fix Makefile

* Remove commented line of code to be like master

Co-authored-by: CPunch <sethtstubbs@gmail.com>
2020-08-28 13:02:03 -05:00
JadeShrineMaiden
5c8a0069fc Vehicle and trading bugfixes (#51)
* Sanity checks + Starting level changes

- Item movement handler checks to make sure items aren't moved from equipment slot to equipment slot.
- Item give command checks to make sure an out of bounds item is not spawned (Below iType 0 or above iType 8)
- Players now begin at level 36, consequently the item give command does not level you up now.

* Initial Trade Implementation

* Sanity Check

- Prevents out of bounds item movement by comparing it to AINVEN_COUNT.

* Taros and Trading

* Update ItemManager.cpp

* Update ItemManager.cpp

* working trading system

* Trading system code pointerified

- It works with the recent pointer changes needed.

* Vehicles and Trading bugfixes
2020-08-26 21:35:13 -05:00
64d4b1d26a Merge pull request #50 from dongresource/bugfix
Fix crash when receiving invalid packets with very low ids.
2020-08-26 15:33:23 -05:00
9b0cb7f441 Fix crash when receiving invalid packets with very low ids.
Also fix benign NPC deallocation bug.
2020-08-26 22:22:52 +02:00
c48db0f9f9 ignore SIGPIPE 2020-08-26 14:38:09 -05:00
e0c00bcdc8 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-26 14:23:43 -05:00
6db1a7baf1 removed unnecessary allocation 2020-08-26 14:23:40 -05:00
99e357d24e Merge pull request #47 from OpenFusionProject/Raymonf-readme-fix
Correct AppVeyor link
2020-08-26 13:04:32 -05:00
Raymonf
57681cd669 Correct AppVeyor link 2020-08-26 13:53:50 -04:00
c9badae526 Merge pull request #46 from dongresource/mobs2
Populated the future with scraped mobs
2020-08-26 12:42:35 -05:00
JadeShrineMaiden
d3ca93a9b8 Trading System (#43)
* Sanity checks + Starting level changes

- Item movement handler checks to make sure items aren't moved from equipment slot to equipment slot.
- Item give command checks to make sure an out of bounds item is not spawned (Below iType 0 or above iType 8)
- Players now begin at level 36, consequently the item give command does not level you up now.

* Initial Trade Implementation

* Taros and Trading

* working trading system

* Trading system code pointerified

- It works with the recent pointer changes needed.
2020-08-26 12:40:10 -05:00
JadeShrineMaiden
6808365d48 Sanity checks fix (#41)
* Sanity checks + Starting level changes

- Item movement handler checks to make sure items aren't moved from equipment slot to equipment slot.
- Item give command checks to make sure an out of bounds item is not spawned (Below iType 0 or above iType 8)
- Players now begin at level 36, consequently the item give command does not level you up now.

* Sanity Check

- Prevents out of bounds item movement by comparing it to AINVEN_COUNT.

* Update ItemManager.cpp

* Update ItemManager.cpp
2020-08-26 12:39:49 -05:00
4178945abe Decoupled player and NPC view distance. 2020-08-26 04:58:17 +02:00
3e5101892b Populated the future with scraped mobs.
This system is temporary; meant to ease testing.
2020-08-26 04:57:59 +02:00
60be814e16 Merge pull request #42 from dongresource/bugfix
Fixed a use-after-free and a memory leak.
2020-08-25 17:42:22 -05:00
16c11dada0 Fixed a use-after-free and a memory leak. 2020-08-26 00:09:31 +02:00
260331715f Merge pull request #40 from dongresource/bugfix
Bugfixes
2020-08-25 13:52:51 -05:00
b187d4b65f Merge pull request #37 from dongresource/work1
Respawn points, NPC spawning, misc stuff.
2020-08-25 13:45:59 -05:00
3b3ddf08ef Fix github issue #38 2020-08-25 20:42:52 +02:00
41898bb6b7 Fix a bug where nanos aren't unsummoned when unequipped.
Thanks fabriXfinn for reporting it.
Also improved sanity checks.
2020-08-25 20:30:20 +02:00
dff710cf61 Enable vehicle spawning. 2020-08-25 19:43:46 +02:00
b79bc56b31 Implement NPC spawning.
Protected by a simplified GM system. Either everyone is a GM (local
servers) or nobody is (public servers).
2020-08-25 04:28:42 +02:00
9aa9b76826 Made the random characters level 36.
They are meant to make testing faster, after all. No point in
restricting them from fully testing items.
2020-08-25 04:17:47 +02:00
113ce0af07 Load the NPC and warp JSON files according to the config file.
NPC data was being read from the config, but ignored.
2020-08-25 03:45:04 +02:00
6f1a72ca0f Shut Computress up.
Setting all bits in the first use flags disables tutorial messages.
2020-08-25 03:42:52 +02:00
d964a83d6d Respawn points work now.
Note that some of them weren't present in clientnpc and will need to be
manually added later.
2020-08-25 03:34:53 +02:00
d025b611a1 Merge pull request #35 from dongresource/work1
Avoid excessive copying of large Player struct
2020-08-24 19:05:24 -05:00
2f1358c124 Potential solution for the tutorial problem.
Will need to be tested on the public server.
2020-08-25 01:57:53 +02:00
c1b6ae8466 No need to unpack zero-length packet structs.
Also fixed formatting and added subheading to README.md.
2020-08-25 01:08:02 +02:00
14bc368073 Dynamically allocate the Player struct in PlayerView.
This way we're not always passing it around by value.
Note that the Player structs in CNSharedData are still
pass-by-reference. Will probably change this later.
2020-08-25 00:59:55 +02:00
c5dacb4958 Removed CRs from .gitignore and an outdated comment in config.ini. 2020-08-25 00:11:54 +02:00
fb993f0c5d Added .vimrc configured for the 4 spaces style. 2020-08-25 00:11:54 +02:00
6d3868349d removed debug output in keepAliveTimer 2020-08-24 16:12:49 -05:00
16bca39dae added simple timer system to CNShardServer 2020-08-24 16:11:40 -05:00
Onii-chan
afbf309c7e Add player revive, vehicle mount/dismount and more (#33) 2020-08-24 16:04:56 -05:00
28ad1a0c25 fix windows support for sockets 2020-08-24 13:23:28 -05:00
JadeShrineMaiden
ff5f3966e3 Sanity checks + Starting level changes (#31)
* Sanity checks + Starting level changes

- Item movement handler checks to make sure items aren't moved from equipment slot to equipment slot.
- Item give command checks to make sure an out of bounds item is not spawned (Below iType 0 or above iType 8)
- Players now begin at level 36, consequently the item give command does not level you up now.

* Sanity Check

- Prevents out of bounds item movement by comparing it to AINVEN_COUNT.
2020-08-24 03:07:51 -05:00
55add82843 Merge pull request #32 from dongresource/work
-Wall, #pragma once, nanoSummonHandler() cleanup, verbosity levels
2020-08-23 16:57:58 -05:00
e99feb03d5 Add verbosity levels. 2020-08-23 23:09:31 +02:00
431448ffb7 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-23 15:10:00 -05:00
8105d0aa88 properly handle socket errors in recieving packets 2020-08-23 15:09:51 -05:00
acf358ef51 Merge pull request #30 from CakeLancelot/clion-gitignore
Update .gitignore for CLion
2020-08-23 13:03:10 -05:00
CakeLancelot
0a8e96ebc4 Update .gitignore for Clion 2020-08-23 12:58:08 -05:00
756074cc62 Remove redundant initialization in nanoSummonHandler(). 2020-08-23 19:46:51 +02:00
51a8cc8bdf Silence -Wall warnings. 2020-08-23 19:19:12 +02:00
651ccba932 Replace ifdef guards with #pragma once
tl;dr this has potentially significant compilation speed benefits.
2020-08-23 19:14:54 +02:00
1281fdaaf0 Add -Wall to Makefile. 2020-08-23 18:30:23 +02:00
561a809f33 load warps into memory in NPCManager::init 2020-08-23 11:26:25 -05:00
0d27412d81 added getNearbyPlayers 2020-08-23 10:42:37 -05:00
Zenpock
1d792a21dd Npc Warp implementation (#29)
* Update CNShardServer.hpp

* Update ChatManager.cpp

* Update ChatManager.hpp

* Update NPCManager.cpp

* Update NPCManager.hpp

* Add files via upload

* Update NPCManager.cpp

* Update NPCManager.cpp

* Update ChatManager.cpp

* Update ChatManager.cpp

* Update NPCManager.cpp

* Update NPCManager.cpp

Co-authored-by: CPunch <sethtstubbs@gmail.com>
2020-08-23 10:32:25 -05:00
d6b96389be added sanity checks to nanos 2020-08-22 22:15:27 -05:00
dongresource
6129c0b4e2 Players can now see eachother's nanos. (#28) 2020-08-22 19:52:54 -05:00
c9bf3d1896 restored default config.ini 2020-08-22 19:27:08 -05:00
88953541ef added INITSTRUCT to zero-out data 2020-08-22 19:26:18 -05:00
94b0dc724e major refactoring 2020-08-22 18:31:09 -05:00
0ff1f74cd3 fixed inet_ntoa warnings 2020-08-22 14:02:58 -05:00
35b424c531 fixed warnings for VC++ 2020-08-22 13:38:27 -05:00
2072bdcff7 updated appveyor 2020-08-22 13:29:38 -05:00
Zenpock
4f10ee0505 MenuChat Added (#25)
* Update CNShardServer.hpp

* Update ChatManager.cpp

* Update ChatManager.hpp

Co-authored-by: CPunch <sethtstubbs@gmail.com>
2020-08-22 13:11:47 -05:00
cd9fb6ec25 added sanity check to exitGame() 2020-08-22 13:08:37 -05:00
Onii-chan
56bf0db20d Added more nano features (commands, equip & unequip, powers) (#26)
* Revert "fixed window build"

This reverts commit b94f602537.

* Revert "Revert "fixed window build""

This reverts commit dac4457ed2.

* Add nano power feature

* Update CNShardServer.hpp

* Update CNShardServer.hpp

* Test: Add nano power feature

Nano powers are set to the first power in its selection by default.

* Update NanoManager.cpp

* Test: More nano features

* Update NanoManager.hpp

* Update PlayerManager.hpp

* Update PlayerManager.cpp

* Updated indentations

* Update PlayerManager.cpp

* Add DEBUGLOG()

Co-authored-by: CPunch <sethtstubbs@gmail.com>
2020-08-22 13:02:08 -05:00
11fed7db10 Merge pull request #27 from dongresource/defines
Enumerate all packets (+misc FF defines), print packet names, verbose printing
2020-08-22 12:50:16 -05:00
35c622d8a2 Add support for verbose logging. 2020-08-22 19:39:13 +02:00
43f2def80b Report unhandled packets in string form. 2020-08-22 19:19:46 +02:00
0ac600e223 Extracted all packet ids to a single, definitive enum.
It also contains other constant values that might be relevant at some
point.
2020-08-22 17:25:42 +02:00
FinnHornhoover
5f65a84b02 Fix unhandled exception in NPCManager (#24)
* fixed PROTOCOL_VERSION not being defined

* handle exceptions in NPCManager init
2020-08-22 01:46:52 -05:00
78c493b461 switched default ip in config.ini 2020-08-21 23:37:09 -05:00
f71e1349c1 temp fix for U16toU8 edgecase 2020-08-21 22:11:04 -05:00
cff382a8ce sets a limit for sendData() 2020-08-21 21:32:22 -05:00
bbd6c5b532 moved header libraries 2020-08-21 21:03:12 -05:00
JadeShrineMaiden
ab6df26f92 Disabled GM mode (#22)
Temporarily disabled, players can now use item commands without GM mode.
2020-08-21 19:38:10 -05:00
FinnHornhoover
786ee5f4f4 fixed PROTOCOL_VERSION not being defined (#23) 2020-08-21 19:37:59 -05:00
c5efbceca3 added sanity checks for sendPacket() 2020-08-21 19:33:42 -05:00
bf6c5d1b6b fixed NPC scrape 2020-08-21 18:08:42 -05:00
fdfa7b5776 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-21 17:14:38 -05:00
f289c72f6f populated NPCManager 2020-08-21 17:14:11 -05:00
Raymonf
7318d9b578 AppVeyor: Build all protocol versions on Linux (#21)
* let's try this

* Update appveyor.yml
2020-08-21 16:39:30 -04:00
JadeShrineMaiden
caaffcbe3d Item Deletion and extra fixes (#17)
* Deleting Items

* fixes

* fixes 2

* Basic GM login

* Update ItemManager.cpp

Co-authored-by: Raymonf <raymonf@outlook.com>
2020-08-21 15:09:52 -05:00
3fe1a02200 include 0104 if PROTO_VERSION is undefined 2020-08-21 14:29:09 -05:00
cd19c54824 itemManager now uses a reference to the PlayerView 2020-08-21 14:28:14 -05:00
Raymonf
88d08ffca7 Fix MSVC compilation by not using non-standard struct initialization (#20)
Co-authored-by: Raymonf <Raymonf@users.noreply.github.com>
2020-08-21 14:17:06 -04:00
Raymonf
7a46f061ed Fix Windows CI by removing leading 0 in packet versions 2020-08-21 13:50:09 -04:00
dongresource
df18f3ccd1 PROTOCOL_VERSION, test items, MOTD fix (#18)
* Cleaned up protocol selection.

* cmake now works even if protocol option is omitted
* make now supports protocol selection
* removed PACKET_VERSION/CNPROTO_VERSION* redundancy
* ubuntu appveyor script has yet to be written
* cleaned up some trailing spaces

* Add some test items.

Ironically, this change is untested.

* [bugfix] Transmit MOTD when entering the game, not when loading screen fades.

This fixes unnecessary retransmission when /warping.
2020-08-21 12:38:45 -05:00
1669ee3660 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-21 00:31:09 -05:00
5d0b30b4cb added AINVEN_COUT for 0728 2020-08-21 00:31:00 -05:00
Raymonf
0041da795a Build multiple packet versions on AppVeyor for Windows (#14)
* Move to PowerShell script for Windows build

* Allow CMake to override struct version

* PACKET_VERSION option

* Rename CNPROTO_CUSTOM to CNPROTO_OVERRIDE

Co-authored-by: Raymonf <Raymonf@users.noreply.github.com>
2020-08-21 01:18:19 -04:00
Raymonf
24be117e28 Add Discord badge to README (#13) 2020-08-20 23:26:27 -04:00
c7f9358ae5 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-20 22:25:53 -05:00
eee1b52722 fixed ItemManager 2020-08-20 22:25:39 -05:00
CakeLancelot
e3d53e8dcf Add Discord badge to README 2020-08-20 22:04:35 -05:00
Raymonf
b89df8d497 Mention AppVeyor in the readme 2020-08-20 22:54:11 -04:00
Raymonf
faf73fc835 Initial AppVeyor build configuration (#12) 2020-08-20 22:46:48 -04:00
JadeShrineMaiden
aa2adcd9e2 Items Implementation (#11)
* Item Manager (Initial Implementation)

* Item Manager (Second Phase)

* Item Manager (Phase Three)

* Not Working Code

* Inventory Implementation (Complete?)

* Items Implementation

-Fixed Indentations
-Final touches to make it all work

* Update Makefile

* Added small comments

-- needs to be fixed
2020-08-20 21:10:14 -05:00
dongresource
e044b4251a Cleanup indentation. (#10) 2020-08-20 19:37:34 -05:00
7b085e9c8b added sanity checks 2020-08-20 18:50:30 -05:00
Raymonf
da11220762 Allow opening CMakeLists as a CMake project in VS
Added another check just in case someone wants to do this for some reason. It's bad. You shouldn't do it.
2020-08-20 18:44:30 -04:00
1425074ccb edited config.ini default back, better plr pos 2020-08-20 16:59:32 -05:00
Raymonf
c66ac111ab Silence codecvt deprecation warning on VC++ 2020-08-20 17:47:38 -04:00
Raymonf
32a37acd5a Update CMakeLists to use correct binary name and pthreads if not using MinGW/VS 2020-08-20 17:47:27 -04:00
49fbdd2154 Merge branch 'master' of https://github.com/OpenFusionProject/OpenFusion 2020-08-20 16:43:57 -05:00
6857f50c30 added basic NPCManager 2020-08-20 16:43:48 -05:00
Raymonf
c827b5a1b6 Lower CMake version requirement to 3.13
Technically we could probably move this all the way down to 3.6, but 3.13 should be a good version to target.
2020-08-20 17:21:43 -04:00
Raymonf
7f8e7dfa1c Use regular old MIT license 2020-08-20 16:17:53 -04:00
Raymonf
09b21c54d3 Update compilation instructions 2020-08-20 16:10:29 -04:00
Raymonf
c549192f59 CMake build support (#8)
* CMake build support

* Make things nice for VS users

Co-authored-by: Raymonf <Raymonf@users.noreply.github.com>
2020-08-20 15:59:54 -04:00
6843faeb8d fixed windows makefile 2020-08-20 13:31:40 -05:00
b43628a19d faster player distance check 2020-08-20 13:19:03 -05:00
b94f602537 fixed window build 2020-08-20 12:05:01 -05:00
dongresource
fa5f194cc7 Standardized Makefile. (#7)
* Standardized Makefile.

* Incremental compilation
* C++ compilers are called CXX
* Removed excessive comments for well-understood things
* Added clean target
* Proper .PHONY usage
* Updated .gitignore for object files (+ ctags, and vim temp files)

* Add mkdir -p bin.
2020-08-20 12:03:15 -05:00
fbc3c79aa2 Merge pull request #6 from CakeLancelot/add-more-movement-packets
Add zipline, slide, cannon, and jump pad packets
2020-08-20 11:38:13 -05:00
93d973bf21 small CNStructs.hpp refactor 2020-08-20 11:37:47 -05:00
7cf239e3af added 0728 structs + small CNStruct.hpp refactor 2020-08-20 11:36:29 -05:00
CakeLancelot
14d556976d Add zipline, slide, cannon, and jump pad packets
Cannon still looks wonky from other player's PoV, something's up with the rotation - will investigate later.
2020-08-20 11:26:26 -05:00
af6158fbb2 Merge pull request #5 from dongresource/master
Add example config.ini with documented options.
2020-08-20 10:53:45 -05:00
c0abab39ae better structs header 2020-08-20 10:45:50 -05:00
dff6e8c23b better structs header 2020-08-20 10:44:49 -05:00
d6e1f57c23 merged master 2020-08-20 10:43:37 -05:00
9f3f9bb9c3 Fix grammar. 2020-08-20 17:24:27 +02:00
0654500df3 Add default config.ini.
Custom servers will always need one anyway. Might as well give them a
properly documented example to start with.
2020-08-20 17:14:25 +02:00
a0065c2050 Merge pull request #4 from Eperty123/master
Added nano summon feature
2020-08-20 09:48:31 -05:00
Onii-chan
795107a274 Added nano summon feature 2020-08-20 11:51:02 +02:00
42597c2a7a bitch, we good! 2020-08-19 22:32:33 -05:00
Raymonf
bb1ce5c28d fix: MOTD not showing up without config.ini
Since we do an early return without a config.ini file, the MOTDSTRING will still be the default std::string value (""), causing the game to output "Gamemaster: ". To fix this, we'll just hardcode the preferred default value for now.
2020-08-19 22:39:23 -04:00
Raymonf
e75049fc98 MSVC support: alternative implementation of getTime() 2020-08-19 22:26:46 -04:00
Raymonf
1ec4634f69 long -> int64_t; push and pop the original packing for structures
fixes the issue with the tutorial thing
2020-08-19 22:25:19 -04:00
4d9072a752 added radio's logo, started NPCManager 2020-08-19 17:21:35 -05:00
a0d59419f1 changed non-blocking failure to a socket shutdown 2020-08-19 15:54:44 -05:00
94cb89dd6b fixed warnings for windows mingw 2020-08-19 15:42:44 -05:00
ba81db97ef organized structs 2020-08-19 15:07:11 -05:00
ec84d6ca58 scraped all 0104 structs 2020-08-19 15:00:39 -05:00
b8f7d2efc6 added comments to chatHandler() 2020-08-19 13:25:54 -05:00
e7b58c4b32 fixed formatting 2020-08-19 13:22:51 -05:00
0f5be27c97 Merge pull request #3 from JadeShrineMaiden/master
Chat
2020-08-19 13:21:41 -05:00
8328ebf4f3 Chat Feature
Chat with other players, no swearing!!!
2020-08-19 18:47:25 +01:00
a17b72b0b3 Revert "Chat Feature"
This reverts commit 95e454232a.
2020-08-19 18:43:24 +01:00
95e454232a Chat Feature
It just works
2020-08-19 18:37:15 +01:00
fafde9348e lol, whoops 2020-08-19 12:24:05 -05:00
f2059c9ce1 merged motd and exit patch by dongresource 2020-08-19 12:22:54 -05:00
Raymonf
e5274045b0 fix the other type 2020-08-18 22:29:53 -04:00
Raymonf
941d98cc07 Merge pull request #2 from OpenFusionProject/Raymonf-readme-windows
Add a link to Windows compilation instructions
2020-08-18 22:28:58 -04:00
Raymonf
3a892de7c7 Add a link to Windows compilation instructions 2020-08-18 22:11:29 -04:00
8e87a3f102 release makefile 2020-08-18 20:35:48 -05:00
24d30a05bf added heartbeat support 2020-08-18 20:34:39 -05:00
b2325eb308 login server moved to main thread 2020-08-18 19:52:02 -05:00
47b76b422c added sanity checks 2020-08-18 19:11:31 -05:00
208f4b3bbd fixed typo in license lol 2020-08-18 18:45:43 -05:00
78e096a411 added cakes server to readme 2020-08-18 17:28:00 -05:00
2e9e265cba fixed readme 2020-08-18 16:06:24 -05:00
8400a15262 added release to readme 2020-08-18 15:55:06 -05:00
68 changed files with 298372 additions and 1041 deletions

23
.editorconfig Normal file
View File

@@ -0,0 +1,23 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# 4 space indentation
[*.cpp,*.hpp]
indent_style = space
indent_size = 4
# Tabs in makefile
[Makefile]
indent_style = tab
# Don't enforce anything in contrib
[/src/contrib/**]
end_of_line = unset
insert_final_newline = unset
indent_style = unset
indent_style = unset

13
.gitignore vendored
View File

@@ -1,4 +1,13 @@
.vscode
.vscode/
bin/*
notes.txt
config.ini
config.ini
*.o
tags
*~
CMakeFiles/
CMakeCache.txt
build/
.vs/
.idea/
*.db

9
.vimrc Normal file
View File

@@ -0,0 +1,9 @@
" vim configuration file
" You will need to put 'set exrc' and 'set secure' into your main .vimrc file,
" in which case this file will be loaded automatically, but *only* if you
" start vim in this dir. Alternatively you can just load it directly with
" ':so .vimrc' every time.
set tabstop=4
set shiftwidth=4
set expandtab

48
CMakeLists.txt Normal file
View File

@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.13)
project(OpenFusion)
set(CMAKE_CXX_STANDARD 17)
# OpenFusion supports multiple packet/struct versions
# 104 is the default version to build which can be changed
# For example: cmake -B build -DPROTOCOL_VERSION=728
set(PROTOCOL_VERSION 104 CACHE STRING "The packet version to build")
add_compile_definitions(PROTOCOL_VERSION=${PROTOCOL_VERSION})
# Disallow in-source builds
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please refer to the wiki for more information. Please remove the CMakeFiles folder and the CMakeCache.txt file.")
endif()
# Output binaries to the bin folder in the source directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
# Put CMake targets (ALL_BUILD/ZERO_CHECK) into a folder
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Set the OpenFusion project as the default startup project for VS
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT openfusion)
if (WIN32)
# Set the output binary name to winfusion to match the regular Makefile
set(BIN_NAME winfusion)
else()
set(BIN_NAME fusion)
endif()
include_directories(src)
file(GLOB_RECURSE SOURCES src/**.cpp src/**.hpp src/**.c src/**.h)
add_executable(openfusion ${SOURCES})
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
# Use pthreads if not generating a VS solution or MinGW makefile (because MinGW will prefer Win32 threads)
# Checking if the compiler ID is MSVC will allow us to open the project as a CMake project in VS.
# It's not something you should do, but it's there if you need it...
if (NOT CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND NOT CMAKE_GENERATOR MATCHES "MinGW Makefiles")
find_package(Threads REQUIRED)
target_link_libraries(openfusion pthread)
endif()

83
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,83 @@
# Contributing
If you want to contribute to OpenFusion's development, that's great!
We'd appreciate it if you already had some programming experience, as well as experience with software version control using git.
If you've never used git before, please take the time to research it yourself.
There is an abundance of online resources for getting started with git.
If, however, you have no experience programming, this type of project may not be the best one to start with.
OpenFusion is written in C++, which really isn't the best language to start with, as it has many extremely subtle pitfalls with potentially catastrophic consequences that are often very hard for an inexperienced programmer to detect and avoid.
This is compounded by the fact that this is a server, which means it's a long-running, multithreaded process exposed to the network, decoding an externally-imposed binary protocol -- written in C++.
A bad combination.
With that out of the way, the rest of this document will serve to address the matter of clean commits in Pull Requests.
## Repository cleanliness
The commit history in a git repository is important.
Unlike some other version control systems, git allows developers to destructively edit history so as to enjoy the benefits of both frequent "saves" (as in, having the ability to record corrections as soon as you make them) and clean commits which perfectly encapsulate a given change.
Each developer has their own preferred way of doing things, of course, but in general one commit should represent one functional change in the codebase as a whole.
Developers should be able to view a project's commit history and easily understand how the project has evolved over time.
Achieving this is often daunting for new users of git.
I know of two intermediate-level resources (that is, for after you've learned the basics of git) I can recommend for understanding how to properly manage your repository's history -- [think-like-a-git.net](http://think-like-a-git.net/) and [git-rebase.io](https://git-rebase.io/).
Both are pretty short reads and following them will get you up to speed with branches and the rebasing thereof.
I will now cover a few examples of the complications people have encountered contributing to this project, how to understand them, overcome them and henceforth avoid them.
## Dirty pull requests
Many Pull Requests OpenFusion receives fail to present a clean set of commits to merge.
These are generally either:
* Dozens of quick-fix commits the author made while working on their contribution
* Countless useless merge commits generated while trying to re-synchronize with the upstream repository
Few developers are fine with having their commit histories utterly destroyed by merging these changes in.
Many projects, when presented with such Pull Requests, will flat-out reject them or demand the author clean them up first before they can be accepted.
Cpunch, however, chooses to accept them anyway, but squashes them into the repository with the "rebase" merge strategy, instead of a regular merge.
Whereas a regular merge creates a "merge commit" which unites two branches together, a rebase instead *reconstructs* the commits from one branch onto the other, creating *different commits* with possibly the same contents.
If you read the above links, you'll note that this isn't exactly a perfect solution either, since rewriting history in public is usually a bad idea, but because these changes were originally only visible to the PR author, it is only they that will need to rebase their fork to re-sync with the upstream repository.
The obvious issue, then, is that the people submitting dirty PRs are the exact people who don't *know* how to rebase their fork to continue submitting their work cleanly.
So they end up creating countless merge commits when pulling upstream on top of their own incompatible histories, and then submitting those merge commits in their PRs and the cycle continues.
## The details
A git commit is uniquely identified by its SHA1 hash.
Its hash is generated from its contents, as well as the hash(es) of its parent commit(s).
(Most commits have one parent. Merge commits almost always have two, but octopus merges can have any number of parents.)
That means that even if two commits are exactly the same in terms of content, they're not the same commit if their parent commits differ (ex. if they've been rebased).
So if you keep issuing `git pull`s after upstream has merged a rebased version of your PR, you will never re-synchronize with it, and will instead construct an alternate history polluted by pointless merge commits.
## The solution
If you already have a messed-up fork and you have no changes on it that you're afraid to lose, the solution is simple:
* Ensure your `upstream` remote is up to date with `git fetch upstream`
* Make sure you're on master (`git branch master`)
* Set your master branch to the latest commit with `git reset --hard upstream/master`
* Propagate the change to your GitHub fork with `git push -f origin master`
And you're good to go.
If you do have some committed changes that haven't yet been merged upstream, you should probably save them on another branch (called "backup" or something) with `git checkout -b backup`.
If you do end up messing something up, don't worry, it most likely isn't really lost and `git reflog` is your friend.
(You can checkout an arbitrary commit, and make it into its own branch with `git checkout -b BRANCH` or set a pre-exisitng branch to it with `git reset --hard COMMIT`)
## Avoiding the problem
When working on a changeset you want to submit back upstream, don't do it on the main branch.
Create a work branch just for your changeset with `git checkout -b work`.
That way you can always keep master in sync with upstream with `git pull --ff-only upstream master`.
(`--ff-only` can be left out. If you find that git want you to make a merge commit, just back out of it by saving an empty commit message, then fix whatever the cause was.)
* If upstream gets new changes before you've had a chance to submit yours, just update master, create a new branch from your work branch and rebase your new work branch on master (such that your up-to-date changeset is now on the new work branch) and submit that one for your PR
* If you end up making a few ugly fixup commits, use `git rebase --interactive` to clean them up (on a new branch) before submitting your changeset
* If you get told to change something in the PR before it's merged, but after you've pushed it to your GitHub fork, rebase your changes locally, then force-push them onto the your fork's PR branch with `git push -f origin work1` or so
Creating new branches for the rebase isn't strictly necessary since you can always return a branch to its previous state with `git reflog`.
For moving uncommited changes around between branches, `git stash` is a real blessing.

View File

@@ -1,9 +1,21 @@
The OpenFusion MIT except Marlorn License
MIT License
Copyright 2020 Seth Stubbs
Copyright (c) 2020 Seth Stubbs
Excluding the individual known as "MarlornWS" and their associates, permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

139
Makefile
View File

@@ -1,23 +1,126 @@
# makefile for OpenFusion
OBJS = src/*.cpp # source files to compile
CC = clang++ # using GNU C++ compiler
WIN_CC = x86_64-w64-mingw32-g++ # using GNU C++ compiler
CC=clang
CXX=clang++
# -w suppresses all warnings (the part that's commented out helps me find memory leaks, it ruins performance though!)
COMPILER_FLAGS = -std=c++17 -o3 -static #-g3 -fsanitize=address
WIN_COMPILER_FLAGS = -std=c++17 -o3 -static #-g3 -fsanitize=address
CFLAGS=-O3 #-g3 -fsanitize=address
CXXFLAGS=-Wall -Wno-unknown-pragmas -std=c++17 -O2 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
LDFLAGS=-lpthread -ldl #-g3 -fsanitize=address
# specifies the name of our exectuable
SERVER=bin/fusion
#LINKER_FLAGS specifies the libraries we're linking against (NONE, this is a single header library.)
LINKER_FLAGS = -lpthread
WIN_LINKER_FLAGS = -lws2_32 -lwsock32
# assign protocol version
# this can be overriden by ex. make PROTOCOL_VERSION=728
PROTOCOL_VERSION?=104
#OBJ_NAME specifies the name of our exectuable
OBJ_NAME = bin/fusion # location of output for build
WIN_OBJ_NAME = bin/winfusion.exe # location of output for build
# Windows-specific
WIN_CC=x86_64-w64-mingw32-gcc
WIN_CXX=x86_64-w64-mingw32-g++
WIN_CFLAGS=-O3 #-g3 -fsanitize=address
WIN_CXXFLAGS=-Wall -Wno-unknown-pragmas -std=c++17 -O3 -fno-tree-dce -fno-inline-small-functions -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
WIN_LDFLAGS=-static -lws2_32 -lwsock32 #-g3 -fsanitize=address
WIN_SERVER=bin/winfusion.exe
all: $(OBJS)
$(CC) $(OBJS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME)
CSRC=\
src/contrib/sqlite/sqlite3.c\
src/contrib/bcrypt/bcrypt.c\
src/contrib/bcrypt/crypt_blowfish.c\
src/contrib/bcrypt/crypt_gensalt.c\
src/contrib/bcrypt/wrapper.c\
windows: $(OBJS)
$(WIN_CC) $(OBJS) $(WIN_COMPILER_FLAGS) $(WIN_LINKER_FLAGS) -o $(WIN_OBJ_NAME)
CXXSRC=\
src/ChatManager.cpp\
src/CombatManager.cpp\
src/CNLoginServer.cpp\
src/CNProtocol.cpp\
src/CNShardServer.cpp\
src/CNShared.cpp\
src/CNStructs.cpp\
src/Database.cpp\
src/Defines.cpp\
src/main.cpp\
src/MissionManager.cpp\
src/NanoManager.cpp\
src/ItemManager.cpp\
src/NPCManager.cpp\
src/Player.cpp\
src/PlayerManager.cpp\
src/settings.cpp\
src/TransportManager.cpp\
# headers (for timestamp purposes)
CHDR=\
src/contrib/sqlite/sqlite3.h\
src/contrib/sqlite/sqlite_orm.h\
src/contrib/bcrypt/bcrypt.h\
src/contrib/bcrypt/crypt_blowfish.h\
src/contrib/bcrypt/crypt_gensalt.h\
src/contrib/bcrypt/ow-crypt.h\
src/contrib/bcrypt/winbcrypt.h\
CXXHDR=\
src/contrib/bcrypt/BCrypt.hpp\
src/contrib/INIReader.hpp\
src/contrib/JSON.hpp\
src/ChatManager.hpp\
src/CombatManager.hpp\
src/CNLoginServer.hpp\
src/CNProtocol.hpp\
src/CNShardServer.hpp\
src/CNShared.hpp\
src/CNStructs.hpp\
src/Database.hpp\
src/Defines.hpp\
src/contrib/INIReader.hpp\
src/contrib/JSON.hpp\
src/MissionManager.hpp\
src/NanoManager.hpp\
src/ItemManager.hpp\
src/NPCManager.hpp\
src/Player.hpp\
src/PlayerManager.hpp\
src/settings.hpp\
src/TransportManager.hpp\
COBJ=$(CSRC:.c=.o)
CXXOBJ=$(CXXSRC:.cpp=.o)
OBJ=$(COBJ) $(CXXOBJ)
HDR=$(CHDR) $(CXXHDR)
all: $(SERVER)
windows: $(SERVER)
# assign Windows-specific values if targeting Windows
windows : CC=$(WIN_CC)
windows : CXX=$(WIN_CXX)
windows : CFLAGS=$(WIN_CFLAGS)
windows : CXXFLAGS=$(WIN_CXXFLAGS)
windows : LDFLAGS=$(WIN_LDFLAGS)
windows : SERVER=$(WIN_SERVER)
.SUFFIX: .o .c .cpp .h .hpp
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
.cpp.o:
$(CXX) -c $(CXXFLAGS) -o $@ $<
# header timestamps are a prerequisite for OF object files
$(CXXOBJ): $(CXXHDR)
$(SERVER): $(OBJ) $(CHDR) $(CXXHDR)
mkdir -p bin
$(CXX) $(OBJ) $(LDFLAGS) -o $(SERVER)
.PHONY: all windows clean nuke
# only gets rid of OpenFusion objects, so we don't need to
# recompile the libs every time
clean:
rm -f src/*.o $(SERVER) $(WIN_SERVER)
# gets rid of all compiled objects, including the libraries
nuke:
rm -f $(OBJ) $(SERVER) $(WIN_SERVER)

View File

@@ -1,4 +1,7 @@
# OpenFusion
![](res/radiorave_logo.png)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/OpenFusionProject/OpenFusion?svg=true)](https://ci.appveyor.com/project/OpenFusionProject/openfusion)
[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?logo=discord)](https://discord.gg/DYavckB)
OpenFusion is a landwalker server for FusionFall. It currently supports versions `beta-20100104` and `beta-20100728` of the original game.
@@ -8,17 +11,21 @@ Further documentation pending.
tl;dr:
1. Download the client+server bundle from [here](...).
1. Download the client+server bundle from [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.0/OpenFusion.zip).
2. Run `FreeClient/installUnity.bat` once
From then on, any time you want to run the "game":
3. Run `OpenFusion/winfusion.exe`
3. Run `Server/winfusion.exe`
4. Run `FreeClient/OpenFusionClient.exe`
Currently the client by default connects to a public server hosted by Cake. Change the loginInfo.php to point to your own server if you want to host your own.
You have two randomized characters available to you on the Character Selection screen, one boy, one girl.
You can also make your own character and play through the tutorial. The tutorial can be skipped by pressing the ~ key.
If you want, [compiled binaries (artifacts) for each new commit can be found on AppVeyor.](https://ci.appveyor.com/project/OpenFusionProject/openfusion)
For a more detailed overview of the game's architecture and how to configure it, read the following sections.
## Architecture
@@ -54,7 +61,7 @@ When the player clicks "ENTER THE GAME" (or completes the tutorial), the login s
## Configuration
You can change the ports the FusionFall server listens on in `OpenFusion/config.ini`. Make sure the login server port is in sync with `loginInfo.php`.
You can change the ports the FusionFall server listens on in `Server/config.ini`. Make sure the login server port is in sync with `loginInfo.php`.
The shard port needs no such synchronization.
You can also configure the distance at which you'll be able to see other players, though by default it's already as high as you'll want it.
@@ -63,14 +70,26 @@ This just works if you're all under the same LAN, but if you want to play over t
If you're in a region in which Turner's CDN doesn't still have the game's assets cached, you won't be able to play the game in its default configuration.
You'll need to obtain the necessary assets elsewhere and set up your own local web server to host them, because unlike web browsers, the game itself cannot interpret the `file://` schema, and will thus need the assets hosted on an actual HTTP server.
Don't forget to point `assetInfo.php` to where you're hosting the assets and change the `src` param of both the `<embed>` tag and the `<object>` tag in `FreeClient/resources/files/index.html` to where you're hosting the `.unity3d` entrypoint.
Don't forget to point `assetInfo.php` to where you're hosting the assets and change the `src` param of both the `<embed>` tag and the `<object>` tag in `FreeClient/resources/app/files/index.html` to where you're hosting the `.unity3d` entrypoint.
If you change `loginInfo.php` or `assetInfo.php`, make sure not to put any newline characters (or any other whitespace) at the end of the file(s).
Some modern IDEs/text editors do this automatically. If all else fails, use Notepad.
## Compiling
OpenFusion can be compiled from source using the included makefile. to compile for windows (on a *nix system) use `make windows`, otherwise to compile it for the current platform you're on just run `make`
You have two choices for compiling OpenFusion: the included Makefile and the included CMakeLists file.
### Makefile
A detailed compilation guide is available for Windows users in the wiki [using MinGW-w64 and MSYS2](https://github.com/OpenFusionProject/OpenFusion/wiki/Compilation-on-Windows). Otherwise, to compile it for the current platform you're on, just run `make` with the correct build tools installed (currently make and clang).
### CMake
A detailed guide is available [in the wiki](https://github.com/OpenFusionProject/OpenFusion/wiki/Compilation-with-CMake-or-Visual-Studio) for people using regular old CMake or the version of CMake that comes with Visual Studio. tl;dr: `cmake -B build`
## Contributing
If you'd like to contribute to this project, please read [CONTRIBUTING.md](CONTRIBUTING.md).
## "Gameplay"
@@ -81,7 +100,22 @@ It's what's called a landwalker; enough of the server has been implemented to al
To make your landwalking experience more pleasant, you can make use of a few admin commands to get around easier:
### Movement commands
* A `/speed` of around 2400 or 3000 is nice.
* A `/jump` of about 50 will send you soaring
* [This map](res/dong_number_map.png) (credit to Danny O) is useful for `/warp` coordinates.
* `/goto` is useful for more precise teleportation (ie. for getting into Infected Zones, etc.).
* `/goto` is useful for more precise teleportation (ie. for getting into Infected Zones, etc.).
### Item commands
* `/itemN [type] [itemId] [amount]`
(Refer to the [item list](https://docs.google.com/spreadsheets/d/1mpoJ9iTHl_xLI4wQ_9UvIDYNcsDYscdkyaGizs43TCg/))
### Nano commands
* `/nano [id] (1-36)`
* `/nano_equip [id] (1-36) [slot] (0-2)`
* `/nano_unequip [slot] (0-2)`
* `/nano_active [slot] (0-2)`
## Accounts
A basic account system has been added, when logging in if the username doesn't exist in the database, a new account with the provided password will be made and you'll be automatically logged in. Otherwise a login attempt will be made. A username must be between 4 and 32 characters, and a password must be between 8 and 32 characters otherwise the account will be rejected. Characters currently save only upon creation, any items add/traded will not be saved.

79
appveyor.yml Normal file
View File

@@ -0,0 +1,79 @@
version: 'openfusion-{branch}-{build}'
image:
- Visual Studio 2019
- Ubuntu2004
platform:
- x64
configuration:
- Release
for:
-
matrix:
only:
- image: Ubuntu2004
build_script:
- ps: |
$versions = "104", "728"
foreach ($version in $versions) {
Write-Output "Cleaning old output"
Invoke-Expression "make clean"
if ($LASTEXITCODE -ne "0") {
Write-Error "make clean failed for version $version" -ErrorAction Stop
}
Write-Output "Building version $version"
Invoke-Expression "make PROTOCOL_VERSION=$version"
if ($LASTEXITCODE -ne "0") {
Write-Error "make failed for version $version" -ErrorAction Stop
}
Rename-Item -Path "bin/fusion" -newName "$version-fusion"
Write-Output "Built version $version"
}
artifacts:
- path: bin
name: ubuntu20_04-bin-x64
type: zip
-
matrix:
only:
- image: Visual Studio 2019
build_script:
- ps: |
$versions = "104", "728"
$configurations = "Release", "Debug"
# AppVeyor uses VS2019 Community
$vsPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community"
Import-Module "$vsPath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation
foreach ($version in $versions) {
if (Test-Path -LiteralPath "build") {
Remove-Item "build" -Recurse
Write-Output "Deleted existing build folder"
}
Invoke-Expression "cmake -B build -DPROTOCOL_VERSION=$version"
if ($LASTEXITCODE -ne "0") {
Write-Error "cmake generation failed for version $version" -ErrorAction Stop
}
Write-Output "Generated build files for version $version"
foreach ($configuration in $configurations) {
Write-Output "Building version $version $configuration"
Invoke-Expression "msbuild build\OpenFusion.sln /p:Configuration=$configuration"
if ($LASTEXITCODE -ne "0") {
Write-Error "msbuild build failed for version $version" -ErrorAction Stop
}
Rename-Item -Path "bin/$configuration" -newName "$version-$configuration"
Write-Output "Built version $version $configuration"
}
}
artifacts:
- path: bin
name: windows-vs2019-bin-x64
type: zip

40
config.ini Normal file
View File

@@ -0,0 +1,40 @@
# verbosity level
# 0 = mostly silence
# 1 = debug prints and unknown packets
# 2 = print all packets except LIVE_CHECK and movement
# 3 = print all packets
verbosity=1
# Login Server configuration
[login]
# must be kept in sync with loginInfo.php
port=8001
# enables two randomly generated characters in the
# character selection menu for convenience
randomcharacters=true
# will all custom names be approved instantly?
acceptallcustomnames=true
# Shard Server configuration
[shard]
port=8002
ip=127.0.0.1
# distance at which other players and NPCs become visible
playerdistance=20000
npcdistance=16000
# little message players see when they enter the game
motd=Welcome to OpenFusion!
# NPC json data
npcdata=data/NPCs.json
# warp target json data
warpdata=data/warps.json
# mob json
mobdata=data/mobs.json
# is everyone a GM?
gm=true
# spawn coordinates (Z is height)
# the supplied defaults are at Sector V (future)
spawnx=632032
spawny=187177
spawnz=-5500

1
data/NPCs.json Normal file

File diff suppressed because one or more lines are too long

1
data/mobs.json Normal file

File diff suppressed because one or more lines are too long

1
data/warps.json Normal file

File diff suppressed because one or more lines are too long

BIN
res/radiorave_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -1,13 +1,13 @@
#include "CNLoginServer.hpp"
#include "CNShared.hpp"
#include "CNStructs.hpp"
#include "Database.hpp"
#include "PlayerManager.hpp"
#include <regex>
#include "contrib/bcrypt/BCrypt.hpp"
#include "settings.hpp"
/*
This is *not* connected to any database, so i'm sending dummy data just to get the client to work & connect to the shard <3
*/
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
CNLoginServer::CNLoginServer(uint16_t p) {
@@ -17,90 +17,133 @@ CNLoginServer::CNLoginServer(uint16_t p) {
}
void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
printPacket(data, CL2LS);
switch (data->type) {
case P_CL2LS_REQ_LOGIN: {
sP_CL2LS_REQ_LOGIN* login = (sP_CL2LS_REQ_LOGIN*)data->buf;
sP_LS2CL_REP_LOGIN_SUCC* response = (sP_LS2CL_REP_LOGIN_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_LOGIN_SUCC));
uint64_t cachedKey = sock->getEKey(); // so we can still send the response packet with the correct key
int charCount = 2; // send 4 randomly generated characters for now
if (data->size != sizeof(sP_CL2LS_REQ_LOGIN))
return; // ignore the malformed packet
DEBUGLOG(
std::cout << "P_CL2LS_REQ_LOGIN:" << std::endl;
std::cout << "\tClient version: " << login->iClientVerA << "." << login->iClientVerB << "." << login->iClientVerC << std::endl;
std::cout << "\tLogin type: " << login->iLoginType << std::endl;
std::cout << "\tID: " << U16toU8(login->szID) << " Password: " << U16toU8(login->szPassword) << std::endl;
)
sP_CL2LS_REQ_LOGIN* login = (sP_CL2LS_REQ_LOGIN*)data->buf;
//TODO: implement better way of sending credentials
std::string userLogin = U16toU8(login->szID);
std::string userPassword = U16toU8(login->szPassword);
response->iCharCount = charCount;
response->iSlotNum = 1;
response->iPaymentFlag = 1;
response->iOpenBetaFlag = 0;
response->uiSvrTime = getTime();
bool success = false;
int errorCode = 0;
// set username in response packet
memcpy(response->szID, login->szID, sizeof(char16_t) * 33);
// update keys
sock->setEKey(CNSocketEncryption::createNewKey(response->uiSvrTime, response->iCharCount + 1, response->iSlotNum + 1));
sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1));
// send the response in with original key
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC), cachedKey));
loginSessions[sock] = CNLoginData();
if (settings::LOGINRANDCHARACTERS) {
// now send the characters :)
for (int i = 0; i < charCount; i++) {
sP_LS2CL_REP_CHAR_INFO* charInfo = (sP_LS2CL_REP_CHAR_INFO*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_INFO));
charInfo->iSlot = (int8_t)i + 1;
charInfo->iLevel = (int16_t)1;
charInfo->sPC_Style.iPC_UID = rand(); // unique identifier for the character
charInfo->sPC_Style.iNameCheck = 1;
charInfo->sPC_Style.iGender = (i%2)+1; // can be 1(boy) or 2(girl)
charInfo->sPC_Style.iFaceStyle = 1;
charInfo->sPC_Style.iHairStyle = 1;
charInfo->sPC_Style.iHairColor = (rand()%19) + 1; // 1 - 19
charInfo->sPC_Style.iSkinColor = (rand()%13) + 1; // 1 - 13
charInfo->sPC_Style.iEyeColor = (rand()%6) + 1; // 1 - 6
charInfo->sPC_Style.iHeight = (rand()%6); // 0 -5
charInfo->sPC_Style.iBody = (rand()%4); // 0 - 3
charInfo->sPC_Style.iClass = 0;
charInfo->sPC_Style2 = sPCStyle2(1, 1, 1);
// past's town hall
charInfo->iX = settings::SPAWN_X;
charInfo->iY = settings::SPAWN_Y;
charInfo->iZ = settings::SPAWN_Z;
U8toU16(std::string("Player"), charInfo->sPC_Style.szFirstName);
U8toU16(std::to_string(i + 1), charInfo->sPC_Style.szLastName);
int64_t UID = charInfo->sPC_Style.iPC_UID;
loginSessions[sock].characters[UID] = Player();
loginSessions[sock].characters[UID].level = charInfo->iLevel;
loginSessions[sock].characters[UID].slot = charInfo->iSlot;
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
loginSessions[sock].characters[UID].x = charInfo->iX;
loginSessions[sock].characters[UID].y = charInfo->iY;
loginSessions[sock].characters[UID].z = charInfo->iZ;
loginSessions[sock].characters[UID].PCStyle = charInfo->sPC_Style;
loginSessions[sock].characters[UID].PCStyle2 = charInfo->sPC_Style2;
for (int i = 0; i < AEQUIP_COUNT; i++) {
// setup item
charInfo->aEquip[i].iID = 0;
charInfo->aEquip[i].iType = i;
charInfo->aEquip[i].iOpt = 0;
loginSessions[sock].characters[UID].Equip[i] = charInfo->aEquip[i];
//checking regex
if (!CNLoginServer::isLoginDataGood(userLogin, userPassword))
{
errorCode = (int)LOGINERRORID::login_error;
}
else
{
std::unique_ptr<Database::Account> findUser = Database::findAccount(userLogin);
//if account not found, create it
if (findUser == nullptr)
{
loginSessions[sock] = CNLoginData();
loginSessions[sock].userID = Database::addAccount(userLogin, userPassword);
loginSessions[sock].slot = 1;
success = true;
}
//if user exists, check if password is correct
else if (CNLoginServer::isPasswordCorrect(findUser->Password, userPassword))
{
//check if account isn't currently in use
if (CNLoginServer::isAccountInUse(findUser->AccountID) ||
PlayerManager::isAccountInUse(findUser->AccountID))
{
errorCode = (int)LOGINERRORID::id_already_in_use;
}
//if not, login success
else
{
loginSessions[sock] = CNLoginData();
loginSessions[sock].userID = findUser->AccountID;
loginSessions[sock].slot = findUser->Selected;
success = true;
}
}
else
{
errorCode = (int)LOGINERRORID::id_and_password_do_not_match;
}
}
if (success)
{
std::vector<Player> characters = Database::getCharacters(loginSessions[sock].userID);
int charCount = characters.size();
INITSTRUCT(sP_LS2CL_REP_LOGIN_SUCC, resp);
// set username in resp packet
memcpy(resp.szID, login->szID, sizeof(char16_t) * 33);
resp.iCharCount = charCount;
resp.iSlotNum = loginSessions[sock].slot;
resp.iPaymentFlag = 1;
resp.iOpenBetaFlag = 0;
resp.uiSvrTime = getTime();
// send the resp in with original key
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC));
// update keys
sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1));
sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1));
// now send the characters :)
std::vector<Player>::iterator it;
for (it = characters.begin(); it != characters.end(); it++)
{
sP_LS2CL_REP_CHAR_INFO charInfo = sP_LS2CL_REP_CHAR_INFO();
charInfo.iSlot = (int8_t)it->slot;
charInfo.iLevel = (int16_t)it->level;
charInfo.sPC_Style = it->PCStyle;
charInfo.sPC_Style2 = it->PCStyle2;
// position
charInfo.iX = it->x;
charInfo.iY = it->y;
charInfo.iZ = it->z;
//save character in session (for char select)
int UID = it->iID;
loginSessions[sock].characters[UID] = Player(*it);
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
//temporary inventory stuff
for (int i = 0; i < 4; i++) {
//equip char creation clothes and lightning rifle
charInfo.aEquip[i] = it->Equip[i];
}
for (int i = 5; i < AEQUIP_COUNT; i++) {
// empty equips
charInfo.aEquip[i].iID = 0;
charInfo.aEquip[i].iType = i;
charInfo.aEquip[i].iOpt = 0;
}
// set default to the first character
if (i == 0)
if (it == characters.begin())
loginSessions[sock].selectedChar = UID;
sock->sendPacket(new CNPacketData((void*)charInfo, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO), sock->getEKey()));
sock->sendPacket((void*)&charInfo, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO));
}
}
//Failure
else {
INITSTRUCT(sP_LS2CL_REP_LOGIN_FAIL, resp);
memcpy(resp.szID, login->szID, sizeof(char16_t) * 33);
resp.iErrorCode = errorCode;
sock->sendPacket((void*)&resp, P_LS2CL_REP_LOGIN_FAIL, sizeof(sP_LS2CL_REP_LOGIN_FAIL));
}
break;
@@ -110,44 +153,67 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
break;
}
case P_CL2LS_REQ_CHECK_CHAR_NAME: {
if (data->size != sizeof(sP_CL2LS_REQ_CHECK_CHAR_NAME))
return;
// naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player 0' if you manually type a name. It will show up for other connected players though)
sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck = (sP_CL2LS_REQ_CHECK_CHAR_NAME*)data->buf;
sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC* response = (sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC));
//check if name is occupied
if (Database::isNameFree(nameCheck))
{
// naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player + ID' if you manually type a name. It will show up for other connected players though)
DEBUGLOG(
INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC, resp);
DEBUGLOG(
std::cout << "P_CL2LS_REQ_CHECK_CHAR_NAME:" << std::endl;
std::cout << "\tFirstName: " << U16toU8(nameCheck->szFirstName) << " LastName: " << U16toU8(nameCheck->szLastName) << std::endl;
)
)
memcpy(response->szFirstName, nameCheck->szFirstName, sizeof(char16_t) * 9);
memcpy(response->szLastName, nameCheck->szLastName, sizeof(char16_t) * 17);
// fr*ck allowed!!!
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHECK_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC), sock->getEKey()));
memcpy(resp.szFirstName, nameCheck->szFirstName, sizeof(char16_t) * 9);
memcpy(resp.szLastName, nameCheck->szLastName, sizeof(char16_t) * 17);
// fr*ck allowed!!!
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC));
}
else {
INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL, resp);
resp.iErrorCode = 1;
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHECK_CHAR_NAME_FAIL, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL));
}
break;
}
case P_CL2LS_REQ_SAVE_CHAR_NAME: {
if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_NAME))
return;
sP_CL2LS_REQ_SAVE_CHAR_NAME* save = (sP_CL2LS_REQ_SAVE_CHAR_NAME*)data->buf;
sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC* response = (sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC));
INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp);
DEBUGLOG(
std::cout << "P_CL2LS_REQ_SAVE_CHAR_NAME:" << std::endl;
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
std::cout << "\tGender: " << (int)save->iGender << std::endl;
std::cout << "\tName: " << U16toU8(save->szFirstName) << " " << U16toU8(save->szLastName) << std::endl;
)
response->iSlotNum = save->iSlotNum;
response->iGender = save->iGender;
memcpy(response->szFirstName, save->szFirstName, sizeof(char16_t) * 9);
memcpy(response->szLastName, save->szLastName, sizeof(char16_t) * 17);
resp.iSlotNum = save->iSlotNum;
resp.iGender = save->iGender;
resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID);
memcpy(resp.szFirstName, save->szFirstName, sizeof(char16_t) * 9);
memcpy(resp.szLastName, save->szLastName, sizeof(char16_t) * 17);
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_SAVE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC), sock->getEKey()));
sock->sendPacket((void*)&resp, P_LS2CL_REP_SAVE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC));
Database::updateSelected(loginSessions[sock].userID, save->iSlotNum);
break;
}
case P_CL2LS_REQ_CHAR_CREATE: {
if (data->size != sizeof(sP_CL2LS_REQ_CHAR_CREATE))
return;
sP_CL2LS_REQ_CHAR_CREATE* character = (sP_CL2LS_REQ_CHAR_CREATE*)data->buf;
sP_LS2CL_REP_CHAR_CREATE_SUCC* response = (sP_LS2CL_REP_CHAR_CREATE_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC));
Database::finishCharacter(character);
DEBUGLOG(
std::cout << "P_CL2LS_REQ_CHAR_CREATE:" << std::endl;
@@ -167,36 +233,47 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
std::cout << "\tiEquipLBID: " << (int)character->sOn_Item.iEquipLBID << std::endl;
std::cout << "\tiEquipFootID: " << (int)character->sOn_Item.iEquipFootID << std::endl;
)
Player player =
Database::DbToPlayer(
Database::getDbPlayerById(character->PCStyle.iPC_UID)
);
int64_t UID = player.iID;
character->PCStyle.iNameCheck = 1;
response->PC_Style = character->PCStyle;
response->PC_Style2 = sPCStyle2(1, 1, 1);
response->iLevel = 1;
response->sOn_Item = character->sOn_Item;
INITSTRUCT(sP_LS2CL_REP_CHAR_CREATE_SUCC, resp);
resp.sPC_Style = player.PCStyle;
resp.sPC_Style2 = player.PCStyle2;
resp.iLevel = player.level;
resp.sOn_Item = character->sOn_Item;
int64_t UID = character->PCStyle.iPC_UID;
loginSessions[sock].characters[UID] = Player();
loginSessions[sock].characters[UID].level = 1;
//save player in session
loginSessions[sock].characters[UID] = Player(player);
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
loginSessions[sock].characters[UID].PCStyle = character->PCStyle;
loginSessions[sock].characters[UID].PCStyle2 = sPCStyle2(1, 0, 1);
loginSessions[sock].characters[UID].x = settings::SPAWN_X;
loginSessions[sock].characters[UID].y = settings::SPAWN_Y;
loginSessions[sock].characters[UID].z = settings::SPAWN_Z;
loginSessions[sock].characters[UID].Equip[1].iID = character->sOn_Item.iEquipUBID; // upper body
loginSessions[sock].characters[UID].Equip[1].iType = 1;
loginSessions[sock].characters[UID].Equip[2].iID = character->sOn_Item.iEquipLBID; // lower body
loginSessions[sock].characters[UID].Equip[2].iType = 2;
loginSessions[sock].characters[UID].Equip[3].iID = character->sOn_Item.iEquipFootID; // foot!
loginSessions[sock].characters[UID].Equip[3].iType = 3;
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC));
Database::updateSelected(loginSessions[sock].userID, player.slot);
break;
}
case P_CL2LS_REQ_CHAR_DELETE: {
if (data->size != sizeof(sP_CL2LS_REQ_CHAR_DELETE))
return;
sP_CL2LS_REQ_CHAR_DELETE* del = (sP_CL2LS_REQ_CHAR_DELETE*)data->buf;
int operationResult = Database::deleteCharacter(del->iPC_UID);
INITSTRUCT(sP_LS2CL_REP_CHAR_DELETE_SUCC, resp);
resp.iSlotNum = operationResult;
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_DELETE_SUCC, sizeof(sP_LS2CL_REP_CHAR_DELETE_SUCC));
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC), sock->getEKey()));
break;
}
case P_CL2LS_REQ_CHAR_SELECT: {
if (data->size != sizeof(sP_CL2LS_REQ_CHAR_SELECT))
return;
// character selected
sP_CL2LS_REQ_CHAR_SELECT* chararacter = (sP_CL2LS_REQ_CHAR_SELECT*)data->buf;
sP_LS2CL_REP_CHAR_SELECT_SUCC* response = (sP_LS2CL_REP_CHAR_SELECT_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC));
INITSTRUCT(sP_LS2CL_REP_CHAR_SELECT_SUCC, resp);
DEBUGLOG(
std::cout << "P_CL2LS_REQ_CHAR_SELECT:" << std::endl;
@@ -204,41 +281,132 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
)
loginSessions[sock].selectedChar = chararacter->iPC_UID;
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHAR_SELECT_SUCC, sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC), sock->getEKey()));
Database::updateSelected(loginSessions[sock].userID, loginSessions[sock].characters[chararacter->iPC_UID].slot);
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHAR_SELECT_SUCC, sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC));
break;
}
case P_CL2LS_REQ_SHARD_SELECT: {
if (data->size != sizeof(sP_CL2LS_REQ_SHARD_SELECT))
return;
// tell client to connect to the shard server
sP_CL2LS_REQ_SHARD_SELECT* shard = (sP_CL2LS_REQ_SHARD_SELECT*)data->buf;
sP_LS2CL_REP_SHARD_SELECT_SUCC* response = (sP_LS2CL_REP_SHARD_SELECT_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC));
INITSTRUCT(sP_LS2CL_REP_SHARD_SELECT_SUCC, resp);
DEBUGLOG(
std::cout << "P_CL2LS_REQ_SHARD_SELECT:" << std::endl;
std::cout << "\tShard: " << (int)shard->iShardNum << std::endl;
std::cout << "\tShard: " << (int)shard->ShardNum << std::endl;
)
const char* SHARD_IP = settings::SHARDSERVERIP.c_str();
response->iEnterSerialKey = rand();
response->g_FE_ServerPort = settings::SHARDPORT;
resp.iEnterSerialKey = rand();
resp.g_FE_ServerPort = settings::SHARDPORT;
// copy IP to response (this struct uses ASCII encoding so we don't have to goof around converting encodings)
memcpy(response->g_FE_ServerIP, SHARD_IP, strlen(SHARD_IP));
response->g_FE_ServerIP[strlen(SHARD_IP)] = '\0';
// copy IP to resp (this struct uses ASCII encoding so we don't have to goof around converting encodings)
memcpy(resp.g_FE_ServerIP, SHARD_IP, strlen(SHARD_IP));
resp.g_FE_ServerIP[strlen(SHARD_IP)] = '\0';
// pass player to CNSharedData
CNSharedData::setPlayer(response->iEnterSerialKey, loginSessions[sock].characters[loginSessions[sock].selectedChar]);
CNSharedData::setPlayer(resp.iEnterSerialKey, loginSessions[sock].characters[loginSessions[sock].selectedChar]);
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_SHARD_SELECT_SUCC, sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC), sock->getEKey()));
sock->kill(); // client should connect to the Shard server now
sock->sendPacket((void*)&resp, P_LS2CL_REP_SHARD_SELECT_SUCC, sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC));
break;
}
case P_CL2LS_REQ_SAVE_CHAR_TUTOR: {
if (data->size != sizeof(sP_CL2LS_REQ_SAVE_CHAR_TUTOR))
return;
sP_CL2LS_REQ_SAVE_CHAR_TUTOR* save = (sP_CL2LS_REQ_SAVE_CHAR_TUTOR*)data->buf;
Database::finishTutorial(save->iPC_UID);
loginSessions[sock].characters[save->iPC_UID].PCStyle2.iTutorialFlag = 1;
loginSessions[sock].characters[save->iPC_UID].Equip[0].iID = 328;
loginSessions[sock].characters[save->iPC_UID].Equip[0].iType = 0;
loginSessions[sock].characters[save->iPC_UID].Equip[0].iOpt = 1;
break;
}
case P_CL2LS_REQ_CHANGE_CHAR_NAME: {
if (data->size != sizeof(sP_CL2LS_REQ_CHANGE_CHAR_NAME))
return;
sP_CL2LS_REQ_CHANGE_CHAR_NAME* save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf;
Database::changeName(save);
INITSTRUCT(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, resp);
resp.iPC_UID = save->iPCUID;
memcpy(resp.szFirstName, save->szFirstName, sizeof(char16_t)*9);
memcpy(resp.szLastName, save->szLastName, sizeof(char16_t) * 17);
resp.iSlotNum = save->iSlotNum;
sock->sendPacket((void*)&resp, P_LS2CL_REP_CHANGE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHANGE_CHAR_NAME_SUCC));
break;
}
case P_CL2LS_REQ_PC_EXIT_DUPLICATE:{
if (data->size != sizeof(sP_CL2LS_REQ_PC_EXIT_DUPLICATE))
return;
sP_CL2LS_REQ_PC_EXIT_DUPLICATE* exit = (sP_CL2LS_REQ_PC_EXIT_DUPLICATE*)data->buf;
auto account = Database::findAccount(U16toU8(exit->szID));
if (account == nullptr)
break;
int accountId = account->AccountID;
if (!exitDuplicate(accountId))
PlayerManager::exitDuplicate(accountId);
break;
}
default:
std::cerr << "OpenFusion: LOGIN UNIMPLM ERR. PacketType: " << data->type << std::endl;
if (settings::VERBOSITY)
std::cerr << "OpenFusion: LOGIN UNIMPLM ERR. PacketType: " << Defines::p2str(CL2LS, data->type) << " (" << data->type << ")" << std::endl;
break;
/* Unimplemented CL2LS packets:
P_CL2LS_CHECK_NAME_LIST - unused by the client
P_CL2LS_REQ_SERVER_SELECT
P_CL2LS_REQ_SHARD_LIST_INFO - dev commands, useless as we only run 1 server
*/
}
}
void CNLoginServer::newConnection(CNSocket* cns) {
cns->setActiveKey(SOCKETKEY_E); // by default they accept keys encrypted with the default key
}
void CNLoginServer::killConnection(CNSocket* cns) {
loginSessions.erase(cns);
}
}
#pragma region helperMethods
bool CNLoginServer::isAccountInUse(int accountId) {
std::map<CNSocket*, CNLoginData>::iterator it;
for (it = CNLoginServer::loginSessions.begin(); it != CNLoginServer::loginSessions.end(); it++)
{
if (it->second.userID == accountId)
return true;
}
return false;
}
bool CNLoginServer::exitDuplicate(int accountId)
{
std::map<CNSocket*, CNLoginData>::iterator it;
for (it = CNLoginServer::loginSessions.begin(); it != CNLoginServer::loginSessions.end(); it++)
{
if (it->second.userID == accountId)
{
CNSocket* sock = it->first;
INITSTRUCT(sP_LS2CL_REP_PC_EXIT_DUPLICATE, resp);
resp.iErrorCode = 0;
sock->sendPacket((void*)&resp, P_LS2CL_REP_PC_EXIT_DUPLICATE, sizeof(sP_LS2CL_REP_PC_EXIT_DUPLICATE));
sock->kill();
return true;
}
}
return false;
}
bool CNLoginServer::isLoginDataGood(std::string login, std::string password)
{
std::regex loginRegex("[a-zA-Z0-9_-]{4,32}");
std::regex passwordRegex("[a-zA-Z0-9!@#$%^&*()_+]{8,32}");
return (std::regex_match(login, loginRegex) && std::regex_match(password, passwordRegex));
}
bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tryPassword)
{
return BCrypt::validatePassword(tryPassword, actualPassword);
}
#pragma endregion helperMethods

View File

@@ -1,37 +1,27 @@
#ifndef _CNLS_HPP
#define _CNLS_HPP
#pragma once
#include "CNProtocol.hpp"
#include "Defines.hpp"
#include "Player.hpp"
#include <map>
enum LOGINPACKETID {
// client to login server
P_CL2LS_REQ_LOGIN = 301989889,
P_CL2LS_REQ_CHECK_CHAR_NAME = 301989890,
P_CL2LS_REQ_SAVE_CHAR_NAME = 301989891,
P_CL2LS_REQ_CHAR_CREATE = 301989892,
P_CL2LS_REQ_CHAR_SELECT = 301989893,
P_CL2LS_REQ_SHARD_SELECT = 301989895,
P_CL2LS_REP_LIVE_CHECK = 301989900,
P_CL2LS_REQ_SHARD_LIST_INFO = 301989896,
// login server 2 client
P_LS2CL_REP_LOGIN_SUCC = 553648129,
P_LS2CL_REP_CHAR_INFO = 553648131,
P_LS2CL_REP_CHECK_CHAR_NAME_SUCC = 553648133,
P_LS2CL_REP_SAVE_CHAR_NAME_SUCC = 553648135,
P_LS2CL_REP_CHAR_CREATE_SUCC = 553648137,
P_LS2CL_REP_CHAR_SELECT_SUCC = 553648139,
P_LS2CL_REP_SHARD_SELECT_SUCC = 553648143,
P_LS2CL_REQ_LIVE_CHECK = 553648150,
P_LS2CL_REP_SHARD_LIST_INFO_SUCC = 553648153
};
struct CNLoginData {
std::map<int64_t, Player> characters;
int64_t selectedChar;
int userID; int slot;
};
enum class LOGINERRORID {
database_error = 0,
id_doesnt_exist = 1,
id_and_password_do_not_match = 2,
id_already_in_use = 3,
login_error = 4,
client_version_outdated = 6,
you_are_not_an_authorized_beta_tester = 7,
authentication_connection_error = 8,
updated_euala_required = 9
};
// WARNING: THERE CAN ONLY BE ONE OF THESE SERVERS AT A TIME!!!!!! TODO: change loginSessions & packet handlers to be non-static
@@ -39,11 +29,15 @@ class CNLoginServer : public CNServer {
private:
static void handlePacket(CNSocket* sock, CNPacketData* data);
static std::map<CNSocket*, CNLoginData> loginSessions;
static bool isLoginDataGood(std::string login, std::string password);
static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword);
static bool isAccountInUse(int accountId);
//returns true if success
static bool exitDuplicate(int accountId);
public:
CNLoginServer(uint16_t p);
void killConnection(CNSocket* cns);
void newConnection(CNSocket* cns);
void killConnection(CNSocket* cns);
};
#endif

View File

@@ -1,4 +1,5 @@
#include "CNProtocol.hpp"
#include "CNStructs.hpp"
// ========================================================[[ CNSocketEncryption ]]========================================================
@@ -58,11 +59,7 @@ int CNSocketEncryption::decryptData(uint8_t* buffer, uint8_t* key, int size) {
// ========================================================[[ CNPacketData ]]========================================================
CNPacketData::CNPacketData(void* b, uint32_t t, int l, uint64_t k): buf(b), type(t), size(l), key(k) {}
CNPacketData::~CNPacketData() {
free(buf); // we own the buffer
}
CNPacketData::CNPacketData(void* b, uint32_t t, int l): buf(b), size(l), type(t) {}
// ========================================================[[ CNSocket ]]========================================================
@@ -72,11 +69,18 @@ CNSocket::CNSocket(SOCKET s, PacketHandler ph): sock(s), pHandler(ph) {
bool CNSocket::sendData(uint8_t* data, int size) {
int sentBytes = 0;
int maxTries = 10;
while (sentBytes < size) {
int sent = send(sock, (buffer_t*)(data + sentBytes), size - sentBytes, 0); // no flags defined
if (SOCKETERROR(sent))
if (SOCKETERROR(sent)) {
if (OF_ERRNO == OF_EWOULD && maxTries > 0) {
maxTries--;
continue; // try again
}
std::cout << "[FATAL] SOCKET ERROR: " << OF_ERRNO << std::endl;
return false; // error occured while sending bytes
}
sentBytes += sent;
}
@@ -114,28 +118,51 @@ void CNSocket::kill() {
#endif
}
void CNSocket::sendPacket(CNPacketData* pak) {
int tmpSize = pak->size + sizeof(uint32_t);
uint8_t* tmpBuf = (uint8_t*)xmalloc(tmpSize);
// we don't own buf, TODO: queue packets up to send in step()
void CNSocket::sendPacket(void* buf, uint32_t type, size_t size) {
if (!alive)
return;
size_t bodysize = size + sizeof(uint32_t);
uint8_t* fullpkt = (uint8_t*)xmalloc(bodysize+4);
uint8_t* body = fullpkt+4;
memcpy(fullpkt, (void*)&bodysize, 4);
// copy packet type to the front of the buffer & then the actual buffer
memcpy(tmpBuf, (void*)&pak->type, sizeof(uint32_t));
memcpy(tmpBuf+sizeof(uint32_t), pak->buf, pak->size);
memcpy(body, (void*)&type, sizeof(uint32_t));
memcpy(body+sizeof(uint32_t), buf, size);
// encrypt the packet
CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&pak->key), tmpSize);
// send packet size
sendData((uint8_t*)&tmpSize, sizeof(uint32_t));
switch (activeKey) {
case SOCKETKEY_E:
CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&EKey), bodysize);
break;
case SOCKETKEY_FE:
CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&FEKey), bodysize);
break;
default: {
free(fullpkt);
DEBUGLOG(
std::cout << "[WARN]: UNSET KEYTYPE FOR SOCKET!! ABORTING SEND" << std::endl;
)
return;
}
}
// send packet data!
sendData(tmpBuf, tmpSize);
if (alive && !sendData(fullpkt, bodysize+4))
kill();
delete pak;
free(tmpBuf); // free tmp buffer
free(fullpkt);
}
void CNSocket::setActiveKey(ACTIVEKEY key) {
activeKey = key;
}
void CNSocket::step() {
// read step
if (readSize <= 0) {
// we aren't reading a packet yet, try to start looking for one
int recved = recv(sock, (buffer_t*)readBuffer, sizeof(int32_t), 0);
@@ -143,13 +170,17 @@ void CNSocket::step() {
// we got out packet size!!!!
readSize = *((int32_t*)readBuffer);
// sanity check
if (readSize > MAX_PACKETSIZE) {
if (readSize > CN_PACKET_BUFFER_SIZE) {
kill();
return;
}
// we'll just leave bufferIndex at 0 since we already have the packet size, it's safe to overwrite those bytes
activelyReading = true;
} else if (OF_ERRNO != OF_EWOULD) {
// serious socket issue, disconnect connection
kill();
return;
}
}
@@ -158,19 +189,22 @@ void CNSocket::step() {
int recved = recv(sock, (buffer_t*)(readBuffer + readBufferIndex), readSize - readBufferIndex, 0);
if (!SOCKETERROR(recved))
readBufferIndex += recved;
else if (OF_ERRNO != OF_EWOULD) {
// serious socket issue, disconnect connection
kill();
return;
}
}
if (activelyReading && readBufferIndex - readSize <= 0) {
// decrypt readBuffer and copy to CNPacketData
CNSocketEncryption::decryptData(readBuffer, (uint8_t*)(&EKey), readSize);
CNSocketEncryption::decryptData((uint8_t*)&readBuffer, (uint8_t*)(&EKey), readSize);
// this doesn't leak memory because we free it in CNPacketData's deconstructor LOL
void* tmpBuf = xmalloc(readSize-sizeof(int32_t));
memcpy(tmpBuf, readBuffer+sizeof(uint32_t), readSize-sizeof(int32_t));
CNPacketData tmp(tmpBuf, *((uint32_t*)readBuffer), readSize-sizeof(int32_t), EKey);
void* tmpBuf = readBuffer+sizeof(uint32_t);
CNPacketData tmp(tmpBuf, *((uint32_t*)readBuffer), readSize-sizeof(int32_t));
// CALL PACKET HANDLER!!
pHandler(this, &tmp); // tmp's deconstructor will be called when readStep returns so that tmpBuffer we made will be cleaned up :)
// call packet handler!!
pHandler(this, &tmp);
// reset vars :)
readSize = 0;
@@ -234,26 +268,36 @@ CNServer::CNServer(uint16_t p): port(p) {}
void CNServer::start() {
std::cout << "Starting server at *:" << port << std::endl;
// listen to new connections, add to connection list
while (true) {
while (active) {
std::lock_guard<std::mutex> lock(activeCrit);
// listen for a new connection
SOCKET newConnection = accept(sock, (struct sockaddr *)&(address), (socklen_t*)&(addressSize));
if (!SOCKETINVALID(newConnection)) {
SOCKET newConnectionSocket = accept(sock, (struct sockaddr *)&(address), (socklen_t*)&(addressSize));
if (!SOCKETINVALID(newConnectionSocket)) {
// new connection! make sure to set non-blocking!
#ifdef _WIN32
unsigned long mode = 1;
if (ioctlsocket(newConnection, FIONBIO, &mode) != 0) {
if (ioctlsocket(newConnectionSocket, FIONBIO, &mode) != 0) {
#else
if (fcntl(newConnection, F_SETFL, (fcntl(sock, F_GETFL, 0) | O_NONBLOCK)) != 0) {
if (fcntl(newConnectionSocket, F_SETFL, (fcntl(sock, F_GETFL, 0) | O_NONBLOCK)) != 0) {
#endif
std::cerr << "[FATAL] OpenFusion: fcntl failed on new connection" << std::endl;
exit(EXIT_FAILURE);
std::cerr << "[WARN] OpenFusion: fcntl failed on new connection" << std::endl;
#ifdef _WIN32
shutdown(newConnectionSocket, SD_BOTH);
closesocket(newConnectionSocket);
#else
shutdown(newConnectionSocket, SHUT_RDWR);
close(newConnectionSocket);
#endif
continue;
}
std::cout << "New connection! " << inet_ntoa(address.sin_addr) << std::endl;
// add connection to list!
CNSocket* tmp = new CNSocket(newConnection, pHandler);
CNSocket* tmp = new CNSocket(newConnectionSocket, pHandler);
connections.push_back(tmp);
newConnection(tmp);
}
// for each connection, check if it's alive, if not kill it!
@@ -272,6 +316,8 @@ void CNServer::start() {
}
}
onStep();
#ifdef _WIN32
Sleep(0);
#else
@@ -280,4 +326,48 @@ void CNServer::start() {
}
}
void CNServer::killConnection(CNSocket* cns) {} // stubbed lol
void CNServer::kill() {
std::lock_guard<std::mutex> lock(activeCrit); // the lock will be removed when the function ends
active = false;
// kill all connections
std::list<CNSocket*>::iterator i = connections.begin();
while (i != connections.end()) {
CNSocket* cSock = *i;
if (cSock->isAlive()) {
cSock->kill();
}
++i; // go to the next element
delete cSock;
}
connections.clear();
}
void CNServer::printPacket(CNPacketData *data, int type) {
if (settings::VERBOSITY < 2)
return;
if (settings::VERBOSITY < 3) switch (data->type) {
case P_CL2LS_REP_LIVE_CHECK:
case P_CL2FE_REP_LIVE_CHECK:
case P_CL2FE_REQ_PC_MOVE:
case P_CL2FE_REQ_PC_JUMP:
case P_CL2FE_REQ_PC_SLOPE:
case P_CL2FE_REQ_PC_MOVEPLATFORM:
case P_CL2FE_REQ_PC_MOVETRANSPORTATION:
case P_CL2FE_REQ_PC_ZIPLINE:
case P_CL2FE_REQ_PC_JUMPPAD:
case P_CL2FE_REQ_PC_LAUNCHER:
case P_CL2FE_REQ_PC_STOP:
return;
}
std::cout << "OpenFusion: received " << Defines::p2str(type, data->type) << " (" << data->type << ")" << std::endl;
}
void CNServer::newConnection(CNSocket* cns) {} // stubbed
void CNServer::killConnection(CNSocket* cns) {} // stubbed
void CNServer::onStep() {} // stubbed

View File

@@ -1,21 +1,21 @@
#ifndef _CNP_HPP
#define _CNP_HPP
#pragma once
#define MAX_PACKETSIZE 8192
#define DEBUGLOG(x) x
#define DEBUGLOG(x) if (settings::VERBOSITY) {x};
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#ifdef _WIN32
// windows (UNTESTED)
// windows
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
typedef char buffer_t;
//#define errno WSAGetLastError()
#define OF_ERRNO WSAGetLastError()
#define OF_EWOULD WSAEWOULDBLOCK
#define SOCKETINVALID(x) (x == INVALID_SOCKET)
#define SOCKETERROR(x) (x == SOCKET_ERROR)
#else
@@ -28,6 +28,8 @@
typedef int SOCKET;
typedef void buffer_t;
#define OF_ERRNO errno
#define OF_EWOULD EWOULDBLOCK
#define SOCKETINVALID(x) (x < 0)
#define SOCKETERROR(x) (x == -1)
#endif
@@ -39,12 +41,22 @@
#include <list>
#include <queue>
#include "Defines.hpp"
#include "settings.hpp"
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
#include "mingw/mingw.mutex.h"
#else
#include <mutex>
#endif
/*
Packets format (sent from the client):
[4 bytes] - size of packet (including these 4 bytes!)
[4 bytes] - size of packet including the 4 byte packet type
[size bytes] - Encrypted packet (byte swapped && xor'd with 8 byte key; see CNSocketEncryption)
[4 bytes] - packet type (which is a combination of the first 4 bytes of the packet and a checksum in some versions)
[structure]
[structure] - one member contains length of trailing data (expressed in packet-dependant structures)
[trailing data] - optional variable-length data that only some packets make use of
*/
// error checking calloc wrapper
@@ -59,6 +71,42 @@ inline void* xmalloc(size_t sz) {
return res;
}
// overflow-safe validation of variable-length packets
// for outbound packets
inline bool validOutVarPacket(size_t base, int32_t npayloads, size_t plsize) {
// check for multiplication overflow
if (npayloads > 0 && CN_PACKET_BUFFER_SIZE / (size_t)npayloads < plsize)
return false;
// it's safe to multiply
size_t trailing = npayloads * plsize;
// does it fit in a packet?
if (base + trailing > CN_PACKET_BUFFER_SIZE)
return false;
// everything is a-ok!
return true;
}
// for inbound packets
inline bool validInVarPacket(size_t base, int32_t npayloads, size_t plsize, size_t datasize) {
// check for multiplication overflow
if (npayloads > 0 && CN_PACKET_BUFFER_SIZE / (size_t)npayloads < plsize)
return false;
// it's safe to multiply
size_t trailing = npayloads * plsize;
// make sure size is exact
// datasize has already been validated against CN_PACKET_BUFFER_SIZE
if (datasize != base + trailing)
return false;
// everything is a-ok!
return true;
}
namespace CNSocketEncryption {
// you won't believe how complicated they made it in the client :facepalm:
static constexpr const char* defaultKey = "m@rQn~W#";
@@ -71,15 +119,17 @@ namespace CNSocketEncryption {
int decryptData(uint8_t* buffer, uint8_t* key, int size);
}
class CNPacketData {
public:
struct CNPacketData {
void* buf;
int size;
uint32_t type;
uint64_t key;
CNPacketData(void* b, uint32_t t, int l, uint64_t k);
~CNPacketData();
CNPacketData(void* b, uint32_t t, int l);
};
enum ACTIVEKEY {
SOCKETKEY_E,
SOCKETKEY_FE
};
class CNSocket;
@@ -90,12 +140,15 @@ private:
uint64_t EKey;
uint64_t FEKey;
int32_t readSize = 0;
uint8_t* readBuffer = new uint8_t[MAX_PACKETSIZE];
uint8_t readBuffer[CN_PACKET_BUFFER_SIZE];
int readBufferIndex = 0;
bool activelyReading = false;
bool alive = true;
ACTIVEKEY activeKey;
bool sendData(uint8_t* data, int size);
int recvData(buffer_t* data, int size);
public:
SOCKET sock;
@@ -107,17 +160,33 @@ public:
void setFEKey(uint64_t k);
uint64_t getEKey();
uint64_t getFEKey();
void setActiveKey(ACTIVEKEY t);
void kill();
void sendPacket(CNPacketData* pak);
void sendPacket(void* buf, uint32_t packetType, size_t size);
void step();
bool isAlive();
};
class CNServer;
typedef void (*TimerHandler)(CNServer* serv, uint64_t time);
// timer struct
struct TimerEvent {
TimerHandler handlr;
uint64_t delta; // time to be added to the current time on reset
uint64_t scheduledEvent; // time to call handlr()
TimerEvent(TimerHandler h, uint64_t d): handlr(h), delta(d) {
scheduledEvent = 0;
}
};
// in charge of accepting new connections and making sure each connection is kept alive
class CNServer {
protected:
std::list<CNSocket*> connections;
std::mutex activeCrit;
SOCKET sock;
uint16_t port;
@@ -125,6 +194,8 @@ protected:
struct sockaddr_in address;
void init();
bool active = true;
public:
PacketHandler pHandler;
@@ -132,7 +203,9 @@ public:
CNServer(uint16_t p);
void start();
void kill();
static void printPacket(CNPacketData *data, int type);
virtual void newConnection(CNSocket* cns);
virtual void killConnection(CNSocket* cns);
virtual void onStep(); // called every 2 seconds
};
#endif

View File

@@ -3,31 +3,76 @@
#include "CNShardServer.hpp"
#include "PlayerManager.hpp"
#include "CNShared.hpp"
#include "settings.hpp"
#include <iostream>
#include <sstream>
#include <cstdlib>
std::map<uint32_t, PacketHandler> CNShardServer::ShardPackets;
std::list<TimerEvent> CNShardServer::Timers;
CNShardServer::CNShardServer(uint16_t p) {
port = p;
pHandler = &CNShardServer::handlePacket;
REGISTER_SHARD_TIMER(keepAliveTimer, 2000);
init();
}
void CNShardServer::handlePacket(CNSocket* sock, CNPacketData* data) {
printPacket(data, CL2FE);
if (ShardPackets.find(data->type) != ShardPackets.end())
ShardPackets[data->type](sock, data);
else
std::cerr << "OpenFusion: SHARD UNIMPLM ERR. PacketType: " << data->type << std::endl;
else if (settings::VERBOSITY > 0)
std::cerr << "OpenFusion: SHARD UNIMPLM ERR. PacketType: " << Defines::p2str(CL2FE, data->type) << " (" << data->type << ")" << std::endl;
}
void CNShardServer::keepAliveTimer(CNServer* serv, uint64_t currTime) {
auto cachedPlayers = PlayerManager::players;
for (auto pair : cachedPlayers) {
if (pair.second.lastHeartbeat != 0 && currTime - pair.second.lastHeartbeat > 60000) { // if the client hadn't responded in 60 seconds, its a dead connection so throw it out
pair.first->kill();
continue;
}
// passed the heartbeat, send another
INITSTRUCT(sP_FE2CL_REQ_LIVE_CHECK, data);
pair.first->sendPacket((void*)&data, P_FE2CL_REQ_LIVE_CHECK, sizeof(sP_FE2CL_REQ_LIVE_CHECK));
}
}
void CNShardServer::newConnection(CNSocket* cns) {
cns->setActiveKey(SOCKETKEY_E); // by default they accept keys encrypted with the default key
}
void CNShardServer::killConnection(CNSocket* cns) {
// remove from CNSharedData
Player cachedPlr = PlayerManager::getPlayer(cns);
// check if the player ever sent a REQ_PC_ENTER
if (PlayerManager::players.find(cns) == PlayerManager::players.end())
return;
// remove from CNSharedData
int64_t key = PlayerManager::getPlayer(cns)->SerialKey;
PlayerManager::removePlayer(cns);
CNSharedData::erasePlayer(cachedPlr.SerialKey);
std::cout << U16toU8(cachedPlr.PCStyle.szFirstName) << " " << U16toU8(cachedPlr.PCStyle.szLastName) << " left" << std::endl;
}
CNSharedData::erasePlayer(key);
}
void CNShardServer::onStep() {
uint64_t currTime = getTime();
for (TimerEvent& event : Timers) {
if (event.scheduledEvent == 0) {
// event hasn't been queued yet, go ahead and do that
event.scheduledEvent = currTime + event.delta;
continue;
}
if (event.scheduledEvent < currTime) {
// timer needs to be called
event.handlr(this, currTime);
event.scheduledEvent = currTime + event.delta;
}
}
}

View File

@@ -1,49 +1,26 @@
#ifndef _CNSS_HPP
#define _CNSS_HPP
#pragma once
#include "CNProtocol.hpp"
#include "Defines.hpp"
#include <map>
enum SHARDPACKETID {
// client 2 shard
P_CL2FE_REQ_PC_ENTER = 318767105,
P_CL2FE_REQ_PC_LOADING_COMPLETE = 318767245,
P_CL2FE_REQ_PC_MOVE = 318767107,
P_CL2FE_REQ_PC_STOP = 318767108,
P_CL2FE_REQ_PC_JUMP = 318767109,
P_CL2FE_REQ_PC_MOVEPLATFORM = 318767168,
P_CL2FE_REQ_PC_GOTO = 318767124,
P_CL2FE_GM_REQ_PC_SET_VALUE = 318767211,
P_CL2FE_REQ_SEND_FREECHAT_MESSAGE = 318767111,
P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT = 318767184,
// shard 2 client
P_FE2CL_REP_PC_ENTER_SUCC = 822083586,
P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC = 822083833,
P_FE2CL_PC_NEW = 822083587,
P_FE2CL_PC_MOVE = 822083592,
P_FE2CL_PC_STOP = 822083593,
P_FE2CL_PC_JUMP = 822083594,
P_FE2CL_PC_EXIT = 822083590,
P_FE2CL_PC_MOVEPLATFORM = 822083704,
P_FE2CL_REP_PC_GOTO_SUCC = 822083633,
P_FE2CL_GM_REP_PC_SET_VALUE = 822083781,
P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT = 822083730
};
#define REGISTER_SHARD_PACKET(pactype, handlr) CNShardServer::ShardPackets[pactype] = handlr;
#define REGISTER_SHARD_TIMER(handlr, delta) CNShardServer::Timers.push_back(TimerEvent(handlr, delta));
// WARNING: THERE CAN ONLY BE ONE OF THESE SERVERS AT A TIME!!!!!! TODO: change players & packet handlers to be non-static
class CNShardServer : public CNServer {
private:
static void handlePacket(CNSocket* sock, CNPacketData* data);
static void keepAliveTimer(CNServer*, uint64_t);
public:
static std::map<uint32_t, PacketHandler> ShardPackets;
static std::list<TimerEvent> Timers;
CNShardServer(uint16_t p);
void newConnection(CNSocket* cns);
void killConnection(CNSocket* cns);
void onStep();
};
#endif

View File

@@ -1,6 +1,6 @@
#include "CNShared.hpp"
#ifdef __MINGW32__
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
#include "mingw/mingw.mutex.h"
#else
#include <mutex>
@@ -15,6 +15,8 @@ void CNSharedData::setPlayer(int64_t sk, Player& plr) {
}
Player CNSharedData::getPlayer(int64_t sk) {
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
return players[sk];
}

View File

@@ -3,8 +3,7 @@
There's some data shared between the Login Server and the Shard Server. Of course all of this needs to be thread-safe. No mucking about on this one!
*/
#ifndef _CNSD_HPP
#define _CNSD_HPP
#pragma once
#include <map>
#include <string>
@@ -19,5 +18,3 @@ namespace CNSharedData {
Player getPlayer(int64_t sk);
void erasePlayer(int64_t sk);
}
#endif

View File

@@ -1,12 +1,19 @@
#include "CNStructs.hpp"
#if defined _MSC_VER
#include <chrono>
#endif
std::string U16toU8(char16_t* src) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
return convert.to_bytes(src);
try {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
return convert.to_bytes(src);
} catch(std::exception e) {
return "";
}
}
// returns number of char16_t that was written at des
int U8toU16(std::string src, char16_t* des) {
size_t U8toU16(std::string src, char16_t* des) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::u16string tmp = convert.from_bytes(src);
@@ -18,7 +25,12 @@ int U8toU16(std::string src, char16_t* des) {
}
uint64_t getTime() {
#ifndef _MSC_VER
struct timeval tp;
gettimeofday(&tp, NULL);
return tp.tv_sec * 1000 + tp.tv_usec / 1000;
#else
std::chrono::milliseconds value = std::chrono::duration_cast<std::chrono::milliseconds>((std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now())).time_since_epoch());
return (uint64_t)(value.count());
#endif
}

View File

@@ -1,533 +1,46 @@
/*
CNStructs.hpp - defines some basic structs & useful methods for packets used by FusionFall
NOTE: this is missing the vast majority of packets, I have also ommitted the ERR & FAIL packets for simplicity
/*
CNStructs.hpp - defines some basic structs & useful methods for packets used by FusionFall based on the version defined
*/
#ifndef _CNS_HPP
#define _CNS_HPP
#pragma once
#ifdef _MSC_VER
// codecvt_* is deprecated in C++17 and MSVC will throw an annoying warning because of that.
// Defining this before anything else to silence it.
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#ifndef _MSC_VER
#include <sys/time.h>
#else
// Can't use this in MSVC.
#include <time.h>
#endif
#include <cstring>
#include <string>
#include <locale>
#include <codecvt>
#include <string>
#include <locale>
#include <codecvt>
// yes this is ugly, but this is needed to zero out the memory so we don't have random stackdata in our structs.
#define INITSTRUCT(T, x) T x; \
memset(&x, 0, sizeof(T));
// TODO: rewrite U16toU8 & U8toU16 to not use codecvt
std::string U16toU8(char16_t* src);
// returns number of char16_t that was written at des
int U8toU16(std::string src, char16_t* des);
size_t U8toU16(std::string src, char16_t* des); // returns number of char16_t that was written at des
uint64_t getTime();
//#define CNPROTO_VERSION_0728
#ifdef CNPROTO_VERSION_0728
#define AEQUIP_COUNT 12
// The PROTOCOL_VERSION definition is defined by the build system.
#if !defined(PROTOCOL_VERSION)
#include "structs/0104.hpp"
#elif PROTOCOL_VERSION == 728
#include "structs/0728.hpp"
#elif PROTOCOL_VERSION == 104
#include "structs/0104.hpp"
#else
#define AEQUIP_COUNT 9
#error Invalid PROTOCOL_VERSION
#endif
// ========================================================[[ General Purpose ]]========================================================
// sets the same byte alignment as the structs in the client
#pragma pack(push, 4)
struct sPCStyle {
int64_t iPC_UID;
int8_t iNameCheck;
char16_t szFirstName[9];
char16_t szLastName[17];
int8_t iGender;
int8_t iFaceStyle;
int8_t iHairStyle;
int8_t iHairColor;
int8_t iSkinColor;
int8_t iEyeColor;
int8_t iHeight;
int8_t iBody;
int32_t iClass;
};
#pragma pack(1)
struct sPCStyle2 {
int8_t iAppearanceFlag;
int8_t iTutorialFlag;
int8_t iPayzoneFlag;
sPCStyle2() {}
sPCStyle2(int8_t a, int8_t t, int8_t p):
iAppearanceFlag(a), iTutorialFlag(t), iPayzoneFlag(p) {}
};
#pragma pack(2)
struct sOnItem {
int16_t iEquipHandID;
int16_t iEquipUBID;
int16_t iEquipLBID;
int16_t iEquipFootID;
int16_t iEquipHeadID;
int16_t iEquipFaceID;
int16_t iEquipBackID;
};
struct sOnItem_Index {
int16_t iEquipUBID_index;
int16_t iEquipLBID_index;
int16_t iEquipFootID_index;
int16_t iFaceStyle;
int16_t iHairStyle;
};
struct sNano {
int16_t iID;
int16_t iSkillID;
int16_t iStamina;
};
#pragma pack(4)
struct sItemBase {
int16_t iType;
int16_t iID;
int32_t iOpt;
int32_t iTimeLimit;
#ifdef CNPROTO_VERSION_0728
int32_t iSerial;
#endif
};
struct sTimeBuff {
uint64_t iTimeLimit;
uint64_t iTimeDuration;
int32_t iTimeRepeat;
int32_t iValue;
int32_t iConfirmNum;
};
struct sRunningQuest {
int32_t m_aCurrTaskID;
int32_t m_aKillNPCID[3];
int32_t m_aKillNPCCount[3];
int32_t m_aNeededItemID[3];
int32_t m_aNeededItemCount[3];
};
struct sPCLoadData2CL {
int16_t iUserLevel;
sPCStyle PCStyle;
sPCStyle2 PCStyle2;
int16_t iLevel;
int16_t iMentor;
int16_t iMentorCount;
int32_t iHP;
int32_t iBatteryW;
int32_t iBatteryN;
int32_t iCandy;
int32_t iFusionMatter;
int8_t iSpecialState;
int32_t iMapNum;
int32_t iX;
int32_t iY;
int32_t iZ;
int32_t iAngle;
sItemBase aEquip[AEQUIP_COUNT];
sItemBase aInven[50];
sItemBase aQInven[50];
sNano aNanoBank[37];
int16_t aNanoSlots[3];
int16_t iActiveNanoSlotNum;
int32_t iConditionBitFlag;
int32_t eCSTB___Add;
sTimeBuff TimeBuff;
int64_t aQuestFlag[32];
int64_t aRepeatQuestFlag[8];
sRunningQuest aRunningQuest[9];
int32_t iCurrentMissionID;
int32_t iWarpLocationFlag;
int64_t aWyvernLocationFlag[2];
int32_t iBuddyWarpTime;
int32_t iFatigue;
int32_t iFatigue_Level;
int32_t iFatigueRate;
int64_t iFirstUseFlag1;
int64_t iFirstUseFlag2;
int32_t aiPCSkill[33];
sPCLoadData2CL() {};
};
struct sPCAppearanceData {
int32_t iID;
sPCStyle PCStyle;
int32_t iConditionBitFlag;
int8_t iPCState;
int8_t iSpecialState;
int16_t iLv;
int32_t iHP;
int32_t iMapNum;
int32_t iX;
int32_t iY;
int32_t iZ;
int32_t iAngle;
sItemBase ItemEquip[AEQUIP_COUNT];
sNano Nano;
int32_t eRT;
};
// ========================================================[[ Client2LoginServer packets ]]========================================================
#pragma pack(4)
struct sP_CL2LS_REQ_LOGIN {
char16_t szID[33];
char16_t szPassword[33];
int32_t iClientVerA;
int32_t iClientVerB;
int32_t iClientVerC;
int32_t iLoginType;
uint8_t szCookie_TEGid[64];
uint8_t szCookie_authid[255];
};
struct sP_CL2LS_REQ_CHECK_CHAR_NAME {
int32_t iFNCode;
int32_t iLNCode;
int32_t iMNCode;
char16_t szFirstName[9];
char16_t szLastName[17];
};
struct sP_CL2LS_REQ_SAVE_CHAR_NAME {
int8_t iSlotNum;
int8_t iGender;
int32_t iFNCode;
int32_t iLNCode;
int32_t iMNCode;
char16_t szFirstName[9];
char16_t szLastName[17];
};
struct sP_CL2LS_REQ_CHAR_CREATE {
sPCStyle PCStyle;
sOnItem sOn_Item;
sOnItem_Index sOn_Item_Index;
};
struct sP_CL2LS_REQ_CHAR_SELECT {
int64_t iPC_UID;
};
struct sP_CL2LS_REP_LIVE_CHECK {
int32_t unused;
};
#pragma pack(1)
struct sP_CL2LS_REQ_SHARD_SELECT {
int8_t iShardNum;
};
struct sP_CL2LS_REQ_SHARD_LIST_INFO {
uint8_t unused;
};
// ========================================================[[ LoginServer2Client packets ]]========================================================
#pragma pack(4)
struct sP_LS2CL_REP_LOGIN_SUCC {
int8_t iCharCount;
int8_t iSlotNum;
int8_t iPaymentFlag;
int8_t iTempForPacking4; // UNUSED
uint64_t uiSvrTime; // UNIX timestamp
char16_t szID[33];
uint32_t iOpenBetaFlag;
};
#pragma pack(2)
struct sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC {
char16_t szFirstName[9];
char16_t szLastName[17];
};
#pragma pack(4)
struct sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC {
int64_t iPC_UID;
int8_t iSlotNum;
int8_t iGender;
char16_t szFirstName[9];
char16_t szLastName[17];
};
struct sP_LS2CL_REP_CHAR_CREATE_SUCC {
int16_t iLevel;
sPCStyle PC_Style;
sPCStyle2 PC_Style2;
sOnItem sOn_Item;
};
struct sP_LS2CL_REP_CHAR_INFO {
int8_t iSlot;
int16_t iLevel;
sPCStyle sPC_Style;
sPCStyle2 sPC_Style2;
int32_t iX;
int32_t iY;
int32_t iZ;
sItemBase aEquip[AEQUIP_COUNT];
};
struct sP_LS2CL_REP_SHARD_SELECT_SUCC {
uint8_t g_FE_ServerIP[16]; // Ascii
int32_t g_FE_ServerPort;
int64_t iEnterSerialKey;
};
#pragma pack(1)
struct sP_LS2CL_REP_CHAR_SELECT_SUCC {
int8_t unused;
};
struct sP_LS2CL_REP_SHARD_LIST_INFO_SUCC {
uint8_t aShardConnectFlag[27];
};
// ========================================================[[ Client2ShardServer packets ]]========================================================
#pragma pack(4)
struct sP_CL2FE_REQ_PC_ENTER {
char16_t szID[33];
int32_t iTempValue;
int64_t iEnterSerialKey;
};
struct sP_CL2FE_REQ_PC_LOADING_COMPLETE {
int32_t iPC_ID;
};
struct sP_CL2FE_REQ_PC_MOVE {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
float fVX;
float fVY;
float fVZ;
int32_t iAngle;
uint8_t cKeyValue;
int32_t iSpeed;
};
struct sP_CL2FE_REQ_PC_STOP {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
};
struct sP_CL2FE_REQ_PC_JUMP {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
int32_t iVX;
int32_t iVY;
int32_t iVZ;
int32_t iAngle;
uint8_t cKeyValue;
int32_t iSpeed;
};
struct sP_CL2FE_REQ_PC_MOVEPLATFORM {
uint64_t iCliTime;
int32_t iLcX;
int32_t iLcY;
int32_t iLcZ;
int32_t iX;
int32_t iY;
int32_t iZ;
float fVX;
float fVY;
float fVZ;
int32_t bDown;
uint32_t iPlatformID;
int32_t iAngle;
uint32_t cKeyValue;
int32_t iSpeed;
};
struct sP_CL2FE_REQ_PC_GOTO {
int32_t iToX;
int32_t iToY;
int32_t iToZ;
};
struct sP_CL2FE_GM_REQ_PC_SET_VALUE {
int32_t iPC_ID;
int32_t iSetValueType;
int32_t iSetValue;
};
struct sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE {
char16_t szFreeChat[128];
int32_t iEmoteCode;
};
struct sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT {
int32_t iID_From;
int32_t iEmoteCode;
};
// ========================================================[[ ShardServer2Client packets ]]========================================================
struct sP_FE2CL_REP_PC_ENTER_SUCC {
int32_t iID;
sPCLoadData2CL PCLoadData2CL;
uint64_t uiSvrTime;
};
struct sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC {
int32_t iPC_ID;
};
// literally just a wrapper for a sPCAppearanceData struct :/
struct sP_FE2CL_PC_NEW {
sPCAppearanceData PCAppearanceData;
};
struct sP_FE2CL_PC_MOVE {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
float fVX;
float fVY;
float fVZ;
int32_t iAngle;
uint8_t cKeyValue;
int32_t iSpeed;
int32_t iID;
uint64_t iSvrTime;
};
struct sP_FE2CL_PC_STOP {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
int32_t iID;
uint64_t iSvrTime;
};
struct sP_FE2CL_PC_JUMP {
uint64_t iCliTime;
int32_t iX;
int32_t iY;
int32_t iZ;
int32_t iVX;
int32_t iVY;
int32_t iVZ;
int32_t iAngle;
uint8_t cKeyValue;
int32_t iSpeed;
int32_t iID;
uint64_t iSvrTime;
};
struct sP_FE2CL_PC_MOVEPLATFORM {
uint64_t iCliTime;
int32_t iLcX;
int32_t iLcY;
int32_t iLcZ;
int32_t iX;
int32_t iY;
int32_t iZ;
float fVX;
float fVY;
float fVZ;
int32_t bDown;
uint32_t iPlatformID;
int32_t iAngle;
int8_t cKeyValue;
int32_t iSpeed;
int32_t iPC_ID;
uint64_t iSvrTime;
};
struct sP_FE2CL_GM_REP_PC_SET_VALUE {
int32_t iPC_ID;
int32_t iSetValueType;
int32_t iSetValue;
};
struct sP_FE2CL_PC_EXIT {
int32_t iID;
int32_t iExitType;
};
struct sP_FE2CL_REP_PC_GOTO_SUCC {
int32_t iX;
int32_t iY;
int32_t iZ;
};
struct sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT {
int32_t iID_From;
int32_t iEmoteCode;
};
#pragma pack(pop)
#endif

View File

@@ -6,32 +6,62 @@
void ChatManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_FREECHAT_MESSAGE, chatHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT, emoteHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE, menuChatHandler);
}
void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE))
return; // malformed packet
sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE*)data->buf;
PlayerView plr = PlayerManager::players[sock];
// stubbed for now
// send to client
INITSTRUCT(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, resp);
memcpy(resp.szFreeChat, chat->szFreeChat, sizeof(chat->szFreeChat));
resp.iPC_ID = plr.plr->iID;
resp.iEmoteCode = chat->iEmoteCode;
sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC));
// send to visible players
for (CNSocket* otherSock : plr.viewable) {
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC));
}
}
void ChatManager::menuChatHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE))
return; // malformed packet
sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE*)data->buf;
PlayerView plr = PlayerManager::players[sock];
// send to client
INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, resp);
memcpy(resp.szFreeChat, chat->szFreeChat, sizeof(chat->szFreeChat));
resp.iPC_ID = PlayerManager::players[sock].plr->iID;
resp.iEmoteCode = chat->iEmoteCode;
sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC));
// send to visible players
for (CNSocket* otherSock : plr.viewable) {
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC));
}
}
void ChatManager::emoteHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT))
return; // ignore the malformed packet
// you can dance with friends!!!!!!!!
sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT* emote = (sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT*)data->buf;
PlayerView plr = PlayerManager::players[sock];
// send to client
sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT* resp = (sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT*)xmalloc(sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
resp->iEmoteCode = emote->iEmoteCode;
resp->iID_From = plr.plr.iID;
sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT), sock->getFEKey()));
INITSTRUCT(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, resp);
resp.iEmoteCode = emote->iEmoteCode;
resp.iID_From = plr.plr->iID;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
// send to visible players (players within render distance)
for (CNSocket* otherSock : plr.viewable) {
resp = (sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT*)xmalloc(sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
resp->iEmoteCode = emote->iEmoteCode;
resp->iID_From = plr.plr.iID;
otherSock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT), otherSock->getFEKey()));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
}
}

View File

@@ -1,5 +1,4 @@
#ifndef _CM_HPP
#define _CM_HPP
#pragma once
#include "CNShardServer.hpp"
@@ -8,6 +7,5 @@ namespace ChatManager {
void chatHandler(CNSocket* sock, CNPacketData* data);
void emoteHandler(CNSocket* sock, CNPacketData* data);
void menuChatHandler(CNSocket* sock, CNPacketData* data);
}
#endif

131
src/CombatManager.cpp Normal file
View File

@@ -0,0 +1,131 @@
#include "CombatManager.hpp"
#include "PlayerManager.hpp"
#include "NPCManager.hpp"
#include "ItemManager.hpp"
#include <assert.h>
void CombatManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_BEGIN, combatBegin);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_END, combatEnd);
REGISTER_SHARD_PACKET(P_CL2FE_DOT_DAMAGE_ONOFF, dotDamageOnOff);
}
void CombatManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
sP_CL2FE_REQ_PC_ATTACK_NPCs* pkt = (sP_CL2FE_REQ_PC_ATTACK_NPCs*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// sanity check
if (!validInVarPacket(sizeof(sP_CL2FE_REQ_PC_ATTACK_NPCs), pkt->iNPCCnt, sizeof(int32_t), data->size)) {
std::cout << "[WARN] bad sP_CL2FE_REQ_PC_ATTACK_NPCs packet size\n";
return;
}
int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_PC_ATTACK_NPCs));
/*
* Due to the possibility of multiplication overflow (and regular buffer overflow),
* both incoming and outgoing variable-length packets must be validated, at least if
* the number of trailing structs isn't well known (ie. it's from the client).
*/
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC), pkt->iNPCCnt, sizeof(sAttackResult))) {
std::cout << "[WARN] bad sP_FE2CL_PC_ATTACK_NPCs_SUCC packet size\n";
return;
}
// initialize response struct
size_t resplen = sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC) + pkt->iNPCCnt * sizeof(sAttackResult);
uint8_t respbuf[4096];
memset(respbuf, 0, resplen);
sP_FE2CL_PC_ATTACK_NPCs_SUCC *resp = (sP_FE2CL_PC_ATTACK_NPCs_SUCC*)respbuf;
sAttackResult *respdata = (sAttackResult*)(respbuf+sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC));
resp->iNPCCnt = pkt->iNPCCnt;
for (int i = 0; i < pkt->iNPCCnt; i++) {
if (NPCManager::NPCs.find(pktdata[i]) == NPCManager::NPCs.end()) {
// not sure how to best handle this
std::cout << "[WARN] pcAttackNpcs: mob ID not found" << std::endl;
return;
}
BaseNPC& mob = NPCManager::NPCs[pktdata[i]];
mob.appearanceData.iHP -= 100;
if (mob.appearanceData.iHP <= 0)
giveReward(sock);
// TODO: despawn mobs when they die
respdata[i].iID = mob.appearanceData.iNPC_ID;
respdata[i].iDamage = 100;
respdata[i].iHP = mob.appearanceData.iHP;
respdata[i].iHitFlag = 2;
}
sock->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_NPCs_SUCC, resplen);
// a bit of a hack: these are the same size, so we can reuse the output packet
assert(sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC) == sizeof(sP_FE2CL_PC_ATTACK_NPCs));
sP_FE2CL_PC_ATTACK_NPCs *resp1 = (sP_FE2CL_PC_ATTACK_NPCs*)respbuf;
resp1->iPC_ID = plr->iID;
// send to other players
for (CNSocket *s : PlayerManager::players[sock].viewable) {
if (s == sock)
continue;
s->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
}
}
void CombatManager::combatBegin(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::combatEnd(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::giveReward(CNSocket *sock) {
Player *plr = PlayerManager::getPlayer(sock);
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
assert(resplen < CN_PACKET_BUFFER_SIZE);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[resplen]; // not a variable length array, don't worry
sP_FE2CL_REP_REWARD_ITEM *reward = (sP_FE2CL_REP_REWARD_ITEM *)respbuf;
sItemReward *item = (sItemReward *)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
// don't forget to zero the buffer!
memset(respbuf, 0, resplen);
// update player
plr->money += 50;
plr->fusionmatter += 70;
// simple rewards
reward->m_iCandy = plr->money;
reward->m_iFusionMatter = plr->fusionmatter;
reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this
int slot = ItemManager::findFreeSlot(plr);
if (slot == -1) {
// no room for an item, but you still get FM and taros
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM));
} else {
// item reward
item->sItem.iType = 9;
item->sItem.iID = 1;
item->iSlotNum = slot;
item->eIL = 1; // Inventory Location. 1 means player inventory.
// update player
plr->Inven[slot] = item->sItem;
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
}
}

16
src/CombatManager.hpp Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "CNProtocol.hpp"
#include "CNShared.hpp"
#include "CNShardServer.hpp"
namespace CombatManager {
void init();
void pcAttackNpcs(CNSocket *sock, CNPacketData *data);
void combatBegin(CNSocket *sock, CNPacketData *data);
void combatEnd(CNSocket *sock, CNPacketData *data);
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
void giveReward(CNSocket *sock);
}

357
src/Database.cpp Normal file
View File

@@ -0,0 +1,357 @@
#include "Database.hpp"
#include "contrib/bcrypt/BCrypt.hpp"
#include "CNProtocol.hpp"
#include <string>
#include "contrib/JSON.hpp"
#include "CNStructs.hpp"
#include "settings.hpp"
#include "Player.hpp"
#include "CNStructs.hpp"
#include "contrib/sqlite/sqlite_orm.h"
using namespace sqlite_orm;
# pragma region DatabaseScheme
auto db = make_storage("database.db",
make_table("Accounts",
make_column("AccountID", &Database::Account::AccountID, autoincrement(), primary_key()),
make_column("Login", &Database::Account::Login),
make_column("Password", &Database::Account::Password),
make_column("Selected", &Database::Account::Selected)
),
make_table("Players",
make_column("PlayerID", &Database::DbPlayer::PlayerID, autoincrement(), primary_key()),
make_column("AccountID", &Database::DbPlayer::AccountID),
make_column("Slot", &Database::DbPlayer::slot),
make_column("Firstname", &Database::DbPlayer::FirstName),
make_column("LastName", &Database::DbPlayer::LastName),
make_column("Level", &Database::DbPlayer::Level),
make_column("AppearanceFlag", &Database::DbPlayer::AppearanceFlag),
make_column("TutorialFlag", &Database::DbPlayer::TutorialFlag),
make_column("PayZoneFlag", &Database::DbPlayer::PayZoneFlag),
make_column("XCoordinates", &Database::DbPlayer::x_coordinates),
make_column("YCoordinates", &Database::DbPlayer::y_coordinates),
make_column("ZCoordinates", &Database::DbPlayer::z_coordinates),
make_column("Body", &Database::DbPlayer::Body),
make_column("Class", &Database::DbPlayer::Class),
make_column("EquipFoot", &Database::DbPlayer::EquipFoot),
make_column("EquipLB", &Database::DbPlayer::EquipLB),
make_column("EquipUB", &Database::DbPlayer::EquipUB),
make_column("EquipWeapon1", &Database::DbPlayer::EquipWeapon1),
make_column("EyeColor", &Database::DbPlayer::EyeColor),
make_column("FaceStyle", &Database::DbPlayer::FaceStyle),
make_column("Gender", &Database::DbPlayer::Gender),
make_column("HP", &Database::DbPlayer::HP),
make_column("HairColor", &Database::DbPlayer::HairColor),
make_column("HairStyle", &Database::DbPlayer::HairStyle),
make_column("Height", &Database::DbPlayer::Height),
make_column("NameCheck", &Database::DbPlayer::NameCheck),
make_column("SkinColor", &Database::DbPlayer::SkinColor),
make_column("isGM", &Database::DbPlayer::isGM),
make_column("FusionMatter", &Database::DbPlayer::FusionMatter),
make_column("Taros", &Database::DbPlayer::Taros)
),
make_table("Inventory",
make_column("AccountID", &Database::Inventory::AccountID, primary_key())
)
);
# pragma endregion DatabaseScheme
#pragma region LoginServer
void Database::open()
{
//this parameter means it will try to preserve data during migration
bool preserve = true;
db.sync_schema(preserve);
DEBUGLOG(
std::cout << "[DB] Database in operation" << std::endl;
)
}
int Database::addAccount(std::string login, std::string password)
{
password = BCrypt::generateHash(password);
Account x = {};
x.Login = login;
x.Password = password;
x.Selected = 1;
return db.insert(x);
}
void Database::updateSelected(int accountId, int slot)
{
Account acc = db.get<Account>(accountId);
acc.Selected = slot;
db.update(acc);
}
std::unique_ptr<Database::Account> Database::findAccount(std::string login)
{
//this is awful, I've tried everything to improve it
auto find = db.get_all<Account>(
where(c(&Account::Login) == login), limit(1));
if (find.empty())
return nullptr;
return
std::unique_ptr<Account>(new Account(find.front()));
}
bool Database::isNameFree(sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck)
{
//TODO: add colate nocase
std::string First = U16toU8(nameCheck->szFirstName);
std::string Last = U16toU8(nameCheck->szLastName);
return
(db.get_all<DbPlayer>
(where((c(&DbPlayer::FirstName) == First)
and (c(&DbPlayer::LastName) == Last)))
.empty());
}
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
{
DbPlayer create = {};
//save packet data
create.FirstName = U16toU8(save->szFirstName);
create.LastName = U16toU8(save->szLastName);
create.slot = save->iSlotNum;
create.AccountID = AccountID;
//set flags
create.AppearanceFlag = 0;
create.TutorialFlag = 0;
create.PayZoneFlag = 0;
//set namecheck based on setting
if (settings::APPROVEALLNAMES || save->iFNCode)
create.NameCheck = 1;
else
create.NameCheck = 0;
//create default body character
create.Body= 0;
create.Class= 0;
create.EquipFoot= 0;
create.EquipLB= 0;
create.EquipUB= 0;
create.EquipWeapon1= 0;
create.EquipWeapon2= 0;
create.EyeColor= 1;
create.FaceStyle= 1;
create.Gender= 1;
create.HP= 1000;
create.HairColor= 1;
create.HairStyle = 1;
create.Height= 0;
create.Level= 1;
create.SkinColor= 1;
create.isGM = false;
//commented and disabled for now
//if (U16toU8(save->szFirstName) == settings::GMPASS) {
// create.isGM = true;
//}
create.FusionMatter= 0;
create.Taros= 0;
create.x_coordinates = settings::SPAWN_X;
create.y_coordinates= settings::SPAWN_Y;
create.z_coordinates= settings::SPAWN_Z;
return db.insert(create);
}
void Database::finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character)
{
DbPlayer finish = getDbPlayerById(character->PCStyle.iPC_UID);
finish.AppearanceFlag = 1;
finish.Body = character->PCStyle.iBody;
finish.Class = character->PCStyle.iClass;
finish.EquipFoot = character->sOn_Item.iEquipFootID;
finish.EquipLB = character->sOn_Item.iEquipLBID;
finish.EquipUB = character->sOn_Item.iEquipUBID;
finish.EyeColor = character->PCStyle.iEyeColor;
finish.FaceStyle = character->PCStyle.iFaceStyle;
finish.Gender = character->PCStyle.iGender;
finish.HairColor = character->PCStyle.iHairColor;
finish.HairStyle = character->PCStyle.iHairStyle;
finish.Height = character->PCStyle.iHeight;
finish.Level = 1;
finish.SkinColor = character->PCStyle.iSkinColor;
db.update(finish);
}
void Database::finishTutorial(int PlayerID)
{
DbPlayer finish = getDbPlayerById(PlayerID);
finish.TutorialFlag = 1;
//equip lightning gun
finish.EquipWeapon1 = 328;
db.update(finish);
}
int Database::deleteCharacter(int characterID)
{
auto find =
db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == characterID));
int slot = find.front().slot;
db.remove<DbPlayer>(find.front().PlayerID);
return slot;
}
std::vector <Player> Database::getCharacters(int UserID)
{
std::vector<DbPlayer>characters =
db.get_all<DbPlayer>(where
(c(&DbPlayer::AccountID) == UserID));
//parsing DbPlayer to Player
std::vector<Player> result = std::vector<Player>();
for (auto &character : characters) {
Player toadd = DbToPlayer(character);
result.push_back(
toadd
);
}
return result;
}
void Database::evaluateCustomName(int characterID, CUSTOMNAME decision) {
DbPlayer player = getDbPlayerById(characterID);
player.NameCheck = (int)decision;
db.update(player);
}
void Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save) {
DbPlayer Player = getDbPlayerById(save->iPCUID);
Player.FirstName = U16toU8(save->szFirstName);
Player.LastName = U16toU8(save->szLastName);
if (settings::APPROVEALLNAMES || save->iFNCode)
Player.NameCheck = 1;
else
Player.NameCheck = 0;
db.update(Player);
}
Database::DbPlayer Database::playerToDb(Player player)
{
DbPlayer result = {}; // fixes some weird memory errors, this zeros out the members (not the padding inbetween though)
result.PlayerID = player.iID;
result.AccountID = player.accountId;
result.AppearanceFlag = player.PCStyle2.iAppearanceFlag;
result.Body = player.PCStyle.iBody;
result.Class = player.PCStyle.iClass;
//equipment
result.EyeColor = player.PCStyle.iEyeColor;
result.FaceStyle = player.PCStyle.iFaceStyle;
result.FirstName = U16toU8( player.PCStyle.szFirstName);
//fm
result.Gender = player.PCStyle.iGender;
result.HairColor = player.PCStyle.iHairColor;
result.HairStyle = player.PCStyle.iHairStyle;
result.Height = player.PCStyle.iHeight;
result.HP = player.HP;
result.isGM = player.IsGM;
result.LastName = U16toU8(player.PCStyle.szLastName);
result.Level = player.level;
result.NameCheck = player.PCStyle.iNameCheck;
result.PayZoneFlag = player.PCStyle2.iPayzoneFlag;
result.PlayerID = player.PCStyle.iPC_UID;
result.SkinColor = player.PCStyle.iSkinColor;
result.slot = player.slot;
//taros
result.TutorialFlag = player.PCStyle2.iTutorialFlag;
result.x_coordinates = player.x;
result.y_coordinates = player.y;
result.z_coordinates = player.z;
return result;
}
Player Database::DbToPlayer(DbPlayer player) {
Player result = {}; // fixes some weird memory errors, this zeros out the members (not the padding inbetween though)
result.accountId = player.AccountID;
result.PCStyle2.iAppearanceFlag = player.AppearanceFlag;
result.PCStyle.iBody = player.Body;
result.PCStyle.iClass = player.Class;
result.PCStyle.iEyeColor = player.EyeColor;
result.PCStyle.iFaceStyle = player.FaceStyle;
U8toU16(player.FirstName, result.PCStyle.szFirstName);
result.PCStyle.iGender = player.Gender;
result.PCStyle.iHairColor = player.HairColor;
result.PCStyle.iHairStyle = player.HairStyle;
result.PCStyle.iHeight = player.Height;
result.HP = player.HP;
result.IsGM = player.isGM;
U8toU16(player.LastName, result.PCStyle.szLastName);
result.level = player.Level;
result.PCStyle.iNameCheck = player.NameCheck;
result.PCStyle2.iPayzoneFlag = player.PayZoneFlag;
result.iID = player.PlayerID;
result.PCStyle.iPC_UID = player.PlayerID;
result.PCStyle.iSkinColor = player.SkinColor;
result.slot = player.slot;
result.PCStyle2.iTutorialFlag = player.TutorialFlag;
result.x = player.x_coordinates;
result.y = player.y_coordinates;
result.z = player.z_coordinates;
//TODO:: implement all of below
result.SerialKey = 0;
result.money = 0;
result.fusionmatter = 0;
result.activeNano = 0;
result.iPCState = 0;
result.equippedNanos[0] = 1;
result.equippedNanos[1] = 0;
result.equippedNanos[2] = 0;
result.isTrading = false;
result.isTradeConfirm = false;
result.Nanos[1].iID = 1;
result.Nanos[1].iSkillID = 1;
result.Nanos[1].iStamina = 150;
for (int i = 0; i < 37; i++) {
result.Nanos[i].iID = 0;
result.Nanos[i].iSkillID = 0;
result.Nanos[i].iStamina = 0;
}
result.Equip[0].iID = player.EquipWeapon1;
result.Equip[0].iType = 0;
(player.EquipWeapon1) ? result.Equip[0].iOpt = 1 : result.Equip[0].iOpt = 0;
result.Equip[1].iID = player.EquipUB;
result.Equip[1].iType = 1;
(player.EquipUB) ? result.Equip[1].iOpt = 1 : result.Equip[1].iOpt = 0;
result.Equip[2].iID = player.EquipLB;
result.Equip[2].iType = 2;
(player.EquipLB) ? result.Equip[2].iOpt = 1 : result.Equip[2].iOpt = 0;
result.Equip[3].iID = player.EquipFoot;
result.Equip[3].iType = 3;
(player.EquipFoot) ? result.Equip[3].iOpt = 1 : result.Equip[3].iOpt = 0;
for (int i = 4; i < AEQUIP_COUNT; i++) {
// empty equips
result.Equip[i].iID = 0;
result.Equip[i].iType = i;
result.Equip[i].iOpt = 0;
}
for (int i = 0; i < AINVEN_COUNT; i++) {
// setup inventories
result.Inven[i].iID = 0;
result.Inven[i].iType = 0;
result.Inven[i].iOpt = 0;
}
return result;
}
Database::DbPlayer Database::getDbPlayerById(int id) {
return db.get_all<DbPlayer>(where(c(&DbPlayer::PlayerID) == id))
.front();
}
#pragma endregion LoginServer

91
src/Database.hpp Normal file
View File

@@ -0,0 +1,91 @@
#pragma once
#include "CNStructs.hpp"
#include "Player.hpp"
#include <string>
#include <vector>
namespace Database {
#pragma region DatabaseStructs
struct Account
{
int AccountID;
std::string Login;
std::string Password;
int Selected;
};
struct Inventory
{
int AccountID;
};
struct DbPlayer
{
int PlayerID;
int AccountID;
short int slot;
std::string FirstName;
std::string LastName;
short int AppearanceFlag;
short int Body;
short int Class;
short int EquipFoot;
short int EquipLB;
short int EquipUB;
short int EquipWeapon1;
short int EquipWeapon2;
short int EyeColor;
short int FaceStyle;
short int Gender;
int HP;
short int HairColor;
short int HairStyle;
short int Height;
short int Level;
short int NameCheck;
short int PayZoneFlag;
short int SkinColor;
bool TutorialFlag;
bool isGM;
int FusionMatter;
int Taros;
int x_coordinates;
int y_coordinates;
int z_coordinates;
};
#pragma endregion DatabaseStructs
//handles migrations
void open();
//returns ID
int addAccount(std::string login, std::string password);
void updateSelected(int accountId, int playerId);
std::unique_ptr<Account> findAccount(std::string login);
bool isNameFree(sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck);
//called after chosing name, returns ID
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
//called after finishing creation
void finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character);
//called after tutorial
void finishTutorial(int PlayerID);
//returns slot number
int deleteCharacter(int characterID);
std::vector <Player> getCharacters(int userID);
//accepting/declining custom name
enum class CUSTOMNAME {
approve = 1,
disapprove = 2
};
void evaluateCustomName(int characterID, CUSTOMNAME decision);
void changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save);
//parsing DbPlayer
DbPlayer playerToDb(Player player);
Player DbToPlayer(DbPlayer player);
//getting players
DbPlayer getDbPlayerById(int id);
}

220
src/Defines.cpp Normal file
View File

@@ -0,0 +1,220 @@
#include <string>
#include "Defines.hpp"
#define STRINGIFY(x) PacketMap(x, #x)
/*
* Turns out there isn't better way to do this...
* We'll only support CL2* packets for now, since we only
* need to print those.
*/
struct PacketMap {
int val;
std::string name;
PacketMap(int v, std::string n) : val(v), name(n) {};
};
PacketMap cl2ls_map[] = {
STRINGIFY(P_CL2LS_REQ_LOGIN),
STRINGIFY(P_CL2LS_REQ_CHECK_CHAR_NAME),
STRINGIFY(P_CL2LS_REQ_SAVE_CHAR_NAME),
STRINGIFY(P_CL2LS_REQ_CHAR_CREATE),
STRINGIFY(P_CL2LS_REQ_CHAR_SELECT),
STRINGIFY(P_CL2LS_REQ_CHAR_DELETE),
STRINGIFY(P_CL2LS_REQ_SHARD_SELECT),
STRINGIFY(P_CL2LS_REQ_SHARD_LIST_INFO),
STRINGIFY(P_CL2LS_CHECK_NAME_LIST),
STRINGIFY(P_CL2LS_REQ_SAVE_CHAR_TUTOR),
STRINGIFY(P_CL2LS_REQ_PC_EXIT_DUPLICATE),
STRINGIFY(P_CL2LS_REP_LIVE_CHECK),
STRINGIFY(P_CL2LS_REQ_CHANGE_CHAR_NAME),
STRINGIFY(P_CL2LS_REQ_SERVER_SELECT),
};
PacketMap cl2fe_map[] = {
STRINGIFY(P_CL2FE_REQ_PC_ENTER),
STRINGIFY(P_CL2FE_REQ_PC_EXIT),
STRINGIFY(P_CL2FE_REQ_PC_MOVE),
STRINGIFY(P_CL2FE_REQ_PC_STOP),
STRINGIFY(P_CL2FE_REQ_PC_JUMP),
STRINGIFY(P_CL2FE_REQ_PC_ATTACK_NPCs),
STRINGIFY(P_CL2FE_REQ_SEND_FREECHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_PC_REGEN),
STRINGIFY(P_CL2FE_REQ_ITEM_MOVE),
STRINGIFY(P_CL2FE_REQ_PC_TASK_START),
STRINGIFY(P_CL2FE_REQ_PC_TASK_END),
STRINGIFY(P_CL2FE_REQ_NANO_EQUIP),
STRINGIFY(P_CL2FE_REQ_NANO_UNEQUIP),
STRINGIFY(P_CL2FE_REQ_NANO_ACTIVE),
STRINGIFY(P_CL2FE_REQ_NANO_TUNE),
STRINGIFY(P_CL2FE_REQ_NANO_SKILL_USE),
STRINGIFY(P_CL2FE_REQ_PC_TASK_STOP),
STRINGIFY(P_CL2FE_REQ_PC_TASK_CONTINUE),
STRINGIFY(P_CL2FE_REQ_PC_GOTO),
STRINGIFY(P_CL2FE_REQ_CHARGE_NANO_STAMINA),
STRINGIFY(P_CL2FE_REQ_PC_KILL_QUEST_NPCs),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_ITEM_BUY),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_ITEM_SELL),
STRINGIFY(P_CL2FE_REQ_PC_ITEM_DELETE),
STRINGIFY(P_CL2FE_REQ_PC_GIVE_ITEM),
STRINGIFY(P_CL2FE_REQ_PC_ROCKET_STYLE_READY),
STRINGIFY(P_CL2FE_REQ_PC_ROCKET_STYLE_FIRE),
STRINGIFY(P_CL2FE_REQ_PC_ROCKET_STYLE_HIT),
STRINGIFY(P_CL2FE_REQ_PC_GRENADE_STYLE_READY),
STRINGIFY(P_CL2FE_REQ_PC_GRENADE_STYLE_FIRE),
STRINGIFY(P_CL2FE_REQ_PC_GRENADE_STYLE_HIT),
STRINGIFY(P_CL2FE_REQ_PC_NANO_CREATE),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_OFFER),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_OFFER_CANCEL),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_OFFER_ABORT),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_CONFIRM),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_CONFIRM_ABORT),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER),
STRINGIFY(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT),
STRINGIFY(P_CL2FE_REQ_PC_BANK_OPEN),
STRINGIFY(P_CL2FE_REQ_PC_BANK_CLOSE),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_START),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY),
STRINGIFY(P_CL2FE_REQ_PC_COMBAT_BEGIN),
STRINGIFY(P_CL2FE_REQ_PC_COMBAT_END),
STRINGIFY(P_CL2FE_REQ_REQUEST_MAKE_BUDDY),
STRINGIFY(P_CL2FE_REQ_ACCEPT_MAKE_BUDDY),
STRINGIFY(P_CL2FE_REQ_SEND_BUDDY_FREECHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_SEND_BUDDY_MENUCHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_GET_BUDDY_STYLE),
STRINGIFY(P_CL2FE_REQ_SET_BUDDY_BLOCK),
STRINGIFY(P_CL2FE_REQ_REMOVE_BUDDY),
STRINGIFY(P_CL2FE_REQ_GET_BUDDY_STATE),
STRINGIFY(P_CL2FE_REQ_PC_JUMPPAD),
STRINGIFY(P_CL2FE_REQ_PC_LAUNCHER),
STRINGIFY(P_CL2FE_REQ_PC_ZIPLINE),
STRINGIFY(P_CL2FE_REQ_PC_MOVEPLATFORM),
STRINGIFY(P_CL2FE_REQ_PC_SLOPE),
STRINGIFY(P_CL2FE_REQ_PC_STATE_CHANGE),
STRINGIFY(P_CL2FE_REQ_PC_MAP_WARP),
STRINGIFY(P_CL2FE_REQ_PC_GIVE_NANO),
STRINGIFY(P_CL2FE_REQ_NPC_SUMMON),
STRINGIFY(P_CL2FE_REQ_NPC_UNSUMMON),
STRINGIFY(P_CL2FE_REQ_ITEM_CHEST_OPEN),
STRINGIFY(P_CL2FE_REQ_PC_GIVE_NANO_SKILL),
STRINGIFY(P_CL2FE_DOT_DAMAGE_ONOFF),
STRINGIFY(P_CL2FE_REQ_PC_VENDOR_BATTERY_BUY),
STRINGIFY(P_CL2FE_REQ_PC_WARP_USE_NPC),
STRINGIFY(P_CL2FE_REQ_PC_GROUP_INVITE),
STRINGIFY(P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE),
STRINGIFY(P_CL2FE_REQ_PC_GROUP_JOIN),
STRINGIFY(P_CL2FE_REQ_PC_GROUP_LEAVE),
STRINGIFY(P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT),
STRINGIFY(P_CL2FE_REQ_PC_BUDDY_WARP),
STRINGIFY(P_CL2FE_REQ_GET_MEMBER_STYLE),
STRINGIFY(P_CL2FE_REQ_GET_GROUP_STYLE),
STRINGIFY(P_CL2FE_REQ_PC_CHANGE_MENTOR),
STRINGIFY(P_CL2FE_REQ_GET_BUDDY_LOCATION),
STRINGIFY(P_CL2FE_REQ_NPC_GROUP_SUMMON),
STRINGIFY(P_CL2FE_REQ_PC_WARP_TO_PC),
STRINGIFY(P_CL2FE_REQ_EP_RANK_GET_LIST),
STRINGIFY(P_CL2FE_REQ_EP_RANK_GET_DETAIL),
STRINGIFY(P_CL2FE_REQ_EP_RANK_GET_PC_INFO),
STRINGIFY(P_CL2FE_REQ_EP_RACE_START),
STRINGIFY(P_CL2FE_REQ_EP_RACE_END),
STRINGIFY(P_CL2FE_REQ_EP_RACE_CANCEL),
STRINGIFY(P_CL2FE_REQ_EP_GET_RING),
STRINGIFY(P_CL2FE_REQ_IM_CHANGE_SWITCH_STATUS),
STRINGIFY(P_CL2FE_REQ_SHINY_PICKUP),
STRINGIFY(P_CL2FE_REQ_SHINY_SUMMON),
STRINGIFY(P_CL2FE_REQ_PC_MOVETRANSPORTATION),
STRINGIFY(P_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_SEND_ANY_GROUP_FREECHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_BARKER),
STRINGIFY(P_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_SEND_ANY_GROUP_MENUCHAT_MESSAGE),
STRINGIFY(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION),
STRINGIFY(P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION),
STRINGIFY(P_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH),
STRINGIFY(P_CL2FE_GM_REQ_PC_SET_VALUE),
STRINGIFY(P_CL2FE_GM_REQ_KICK_PLAYER),
STRINGIFY(P_CL2FE_GM_REQ_TARGET_PC_TELEPORT),
STRINGIFY(P_CL2FE_GM_REQ_PC_LOCATION),
STRINGIFY(P_CL2FE_GM_REQ_PC_ANNOUNCE),
STRINGIFY(P_CL2FE_REQ_SET_PC_BLOCK),
STRINGIFY(P_CL2FE_REQ_REGIST_RXCOM),
STRINGIFY(P_CL2FE_GM_REQ_PC_MOTD_REGISTER),
STRINGIFY(P_CL2FE_REQ_ITEM_USE),
STRINGIFY(P_CL2FE_REQ_WARP_USE_RECALL),
STRINGIFY(P_CL2FE_REP_LIVE_CHECK),
STRINGIFY(P_CL2FE_REQ_PC_MISSION_COMPLETE),
STRINGIFY(P_CL2FE_REQ_PC_TASK_COMPLETE),
STRINGIFY(P_CL2FE_REQ_NPC_INTERACTION),
STRINGIFY(P_CL2FE_DOT_HEAL_ONOFF),
STRINGIFY(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH),
STRINGIFY(P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK),
STRINGIFY(P_CL2FE_REQ_PC_READ_EMAIL),
STRINGIFY(P_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST),
STRINGIFY(P_CL2FE_REQ_PC_DELETE_EMAIL),
STRINGIFY(P_CL2FE_REQ_PC_SEND_EMAIL),
STRINGIFY(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM),
STRINGIFY(P_CL2FE_REQ_PC_RECV_EMAIL_CANDY),
STRINGIFY(P_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF),
STRINGIFY(P_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID),
STRINGIFY(P_CL2FE_REQ_NPC_GROUP_INVITE),
STRINGIFY(P_CL2FE_REQ_NPC_GROUP_KICK),
STRINGIFY(P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET),
STRINGIFY(P_CL2FE_REQ_PC_TRANSPORT_WARP),
STRINGIFY(P_CL2FE_REQ_PC_TIME_TO_GO_WARP),
STRINGIFY(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL),
STRINGIFY(P_CL2FE_REQ_CHANNEL_INFO),
STRINGIFY(P_CL2FE_REQ_PC_CHANNEL_NUM),
STRINGIFY(P_CL2FE_REQ_PC_WARP_CHANNEL),
STRINGIFY(P_CL2FE_REQ_PC_LOADING_COMPLETE),
STRINGIFY(P_CL2FE_REQ_PC_FIND_NAME_MAKE_BUDDY),
STRINGIFY(P_CL2FE_REQ_PC_FIND_NAME_ACCEPT_BUDDY),
STRINGIFY(P_CL2FE_REQ_PC_ATTACK_CHARs),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_READY),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_CANCEL),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_REGIST_ITEM),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_UNREGIST_ITEM),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_SALE_START),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_ITEM_LIST),
STRINGIFY(P_CL2FE_PC_STREETSTALL_REQ_ITEM_BUY),
STRINGIFY(P_CL2FE_REQ_PC_ITEM_COMBINATION),
STRINGIFY(P_CL2FE_GM_REQ_SET_PC_SKILL),
STRINGIFY(P_CL2FE_REQ_PC_SKILL_ADD),
STRINGIFY(P_CL2FE_REQ_PC_SKILL_DEL),
STRINGIFY(P_CL2FE_REQ_PC_SKILL_USE),
STRINGIFY(P_CL2FE_REQ_PC_ROPE),
STRINGIFY(P_CL2FE_REQ_PC_BELT),
STRINGIFY(P_CL2FE_REQ_PC_VEHICLE_ON),
STRINGIFY(P_CL2FE_REQ_PC_VEHICLE_OFF),
STRINGIFY(P_CL2FE_REQ_PC_REGIST_QUICK_SLOT),
STRINGIFY(P_CL2FE_REQ_PC_DISASSEMBLE_ITEM),
STRINGIFY(P_CL2FE_GM_REQ_REWARD_RATE),
STRINGIFY(P_CL2FE_REQ_PC_ITEM_ENCHANT),
};
std::string Defines::p2str(int type, int val) {
switch (type) {
case CL2LS:
val = val - CL2LS - 1;
if (val > N_CL2LS || val < 0)
break;
return cl2ls_map[val].name;
case CL2FE:
val = val - CL2FE - 1;
if (val > N_CL2FE || val < 0)
break;
return cl2fe_map[val].name;
}
return "UNKNOWN";
}

857
src/Defines.hpp Normal file
View File

@@ -0,0 +1,857 @@
/* enum definitions from the client */
#pragma once
/*
* TODO: It might be a good idea to make this file build-specific, but
* I'm pretty sure there were seldom any new packets added, and they're
* probably always going to be the non-essential things that we won't be
* implementing just yet anyway.
*/
const float VALUE_BATTERY_EMPTY_PENALTY = 0.5f;
const float CN_EP_RANK_1 = 0.8f;
const float CN_EP_RANK_2 = 0.7f;
const float CN_EP_RANK_3 = 0.5f;
const float CN_EP_RANK_4 = 0.3f;
const float CN_EP_RANK_5 = 0.29f;
enum {
SUCC = 1,
FAIL = 0,
SIZEOF_BYTE = 1,
SIZEOF_DWORD = 4,
SIZEOF_INT = 4,
SIZEOF_FLOAT = 4,
SIZEOF_SHORT = 2,
SIZEOF_ULONG = 4,
SIZEOF_UINT64 = 8,
SIZEOF_IP_STRING = 16,
SIZEOF_CN_UID_STRING = 50,
SIZEOF_ACCOUNT_STRING = 33,
SIZEOF_PASSWORD_STRING = 33,
SIZEOF_AUTH_ID_STRING = 255,
CN_MAX_COUNT_GROUP_MEMBER = 5,
CN_MAX_COUNT_PC_GROUP_MEMBER = 4,
CN_MAX_COUNT_NPC_GROUP_MEMBER = 5,
CHAT_MAX_STRING = 128,
PC_START_LOCATION_RANDOM_RANGE = 10000,
SIZEOF_ANNOUNCE_STRING = 512,
SERVER_COUNT_SHARD_CLIENT = 25,
EXIT_CODE_DISCONNECT = 0,
EXIT_CODE_REQ_BY_PC = 1,
EXIT_CODE_REQ_BY_SVR = 2,
EXIT_CODE_REQ_BY_GM = 3,
EXIT_CODE_HACK = 4,
EXIT_CODE_ERROR = 5,
EXIT_CODE_LIVE_CHECK = 6,
EXIT_CODE_REQ_BY_PC_DUPE_LOGIN = 7,
EXIT_CODE_SERVER_ERROR = 99,
SIZEOF_USER_ID = 32,
SIZEOF_USER_PW = 32,
SIZEOF_PC_SLOT = 4,
SIZEOF_PC_NAME = 16,
SIZEOF_PC_FIRST_NAME = 9,
SIZEOF_PC_LAST_NAME = 17,
SIZEOF_PC_NAME_FLAG = 8,
GENDER_NONE = 0,
GENDER_MALE = 1,
GENDER_FEMALE = 2,
MENTOR_CHANGE_BASE_COST = 100,
REPEAT_MISSION_RESET_TIME = 9,
SIZEOF_REPEAT_QUESTFLAG_NUMBER = 8,
FATIGUE_RESET_TIME = 0,
PC_FATIGUE_KILL_UNIT = 7,
PC_FATIGUE_1_LEVEL = 11420,
PC_FATIGUE_2_LEVEL = 6480,
PC_FATIGUE_MAX_LEVEL = 2,
PC_FUSIONMATTER_MAX = 999999999,
PC_CANDY_MAX = 999999999,
PC_BATTERY_MAX = 9999,
PC_LEVEL_MAX = 36,
SIZEOF_PC_BULLET_SLOT = 3,
PC_TICK_TIME = 5000,
SIZEOF_EQUIP_SLOT = 9,
EQUIP_SLOT_HAND = 0,
EQUIP_SLOT_UPPERBODY = 1,
EQUIP_SLOT_LOWERBODY = 2,
EQUIP_SLOT_FOOT = 3,
EQUIP_SLOT_HEAD = 4,
EQUIP_SLOT_FACE = 5,
EQUIP_SLOT_BACK = 6,
EQUIP_SLOT_END = 6,
EQUIP_SLOT_HAND_EX = 7,
EQUIP_SLOT_VEHICLE = 8,
WPN_EQUIP_TYPE_NONE = 0,
WPN_EQUIP_TYPE_OH_BLADE = 1,
WPN_EQUIP_TYPE_OH_CLUB = 2,
WPN_EQUIP_TYPE_OH_PISTOL = 3,
WPN_EQUIP_TYPE_OH_RIPLE = 4,
WPN_EQUIP_TYPE_OH_THROW = 5,
WPN_EQUIP_TYPE_DH_BLADE = 6,
WPN_EQUIP_TYPE_DH_CLUB = 7,
WPN_EQUIP_TYPE_DH_DPISTOL = 8,
WPN_EQUIP_TYPE_DH_RIPLE = 9,
WPN_EQUIP_TYPE_DH_THROW = 10,
WPN_EQUIP_TYPE_DH_ROCKET = 11,
SIZEOF_INVEN_SLOT = 50,
SIZEOF_QINVEN_SLOT = 50,
SIZEOF_BANK_SLOT = 119,
SIZEOF_RESTORE_SLOT = 5,
SIZEOF_NANO_BANK_SLOT = 37,
SIZEOF_QUEST_SLOT = 1024,
NANO_QUEST_INDEX = 0,
SIZEOF_RQUEST_SLOT = 9,
SIZEOF_QUESTFLAG_NUMBER = 32,
SIZEOF_EP_RECORD_SLOT = 51,
SIZEOF_TRADE_SLOT = 12,
SIZEOF_VENDOR_TABLE_SLOT = 20,
SIZEOF_VENDOR_RESTORE_SLOT = 5,
SIZEOF_QUEST_NPC_SLOT = 3,
SIZEOF_QUEST_ITEM_SLOT = 3,
SIZEOF_MAX_ITEM_STACK = 100,
SIZEOF_PC_SKILL_SLOT = 33,
SIZEOF_QUICK_SLOT = 8,
ENCHANT_WEAPON_MATERIAL_ID = 101,
ENCHANT_DEFENCE_MATERIAL_ID = 102,
SIZEOF_NANO_CARRY_SLOT = 3,
COUNTOF_NANO_PER_SET = 3,
SIZEOF_NANO_SET = 13,
SIZEOF_NANO_STYLE = 3,
NANO_STYLE_NONE = 1,
NANO_STYLE_CRYSTAL = 0,
NANO_STYLE_ENERGY = 1,
NANO_STYLE_FLUID = 2,
SIZEOF_NANO_TYPE = 4,
NANO_TYPE_POWER = 0,
NANO_TYPE_ACCURACY = 1,
NANO_TYPE_PROTECT = 2,
NANO_TYPE_DODGE = 3,
SIZEOF_NANO_TUNE_NEED_ITEM_SLOT = 10,
VALUE_ATTACK_MISS = 1,
MSG_ONLINE = 1,
MSG_BUSY = 2,
MSG_OFFLINE = 0,
SIZEOF_FREE_CHAT_STRING = 128,
SIZEOF_MENU_CHAT_STRING = 128,
SIZEOF_BUDDYLIST_SLOT = 50,
SIZEOF_EMAIL_SUBJECT_STRING = 32,
SIZEOF_EMAIL_CONTENT_STRING = 512,
SIZEOF_EMAIL_PAGE_SIZE = 5,
SIZEOF_EMAIL_ITEM_CNT = 4,
EMAIL_AND_MONEY_COST = 50,
EMAIL_ITEM_COST = 20,
BUDDYWARP_INTERVAL = 60,
EMAILSEND_TIME_DELAY = 60,
DB_ERROR_INVALID_DATA = 1,
DB_ERROR_HACK_ATTEMPT = 2,
DB_ERROR_ACCESS_FAIL = 3,
DB_ERROR_PC_INSERT_FAIL = 4,
CALL_NPC_MAX_CNT = 2048,
CN_EP_RING_MAX_CNT = 999,
HF_BIT_NONE = 0,
HF_BIT_NORMAL = 1,
HF_BIT_CRITICAL = 2,
HF_BIT_STYLE_WIN = 4,
HF_BIT_STYLE_TIE = 8,
HF_BIT_STYLE_LOSE = 16,
SKIN_COLOR_MAX = 12,
HAIR_COLOR_MAX = 18,
EYE_COLOR_MAX = 5,
BODY_TYPE_MAX = 3,
HEIGHT_TYPE_MAX = 5,
CLASS_TYPE_MAX = 4,
CN_EP_RACE_MODE_PRACTICE = 0,
CN_EP_RACE_MODE_RECORD = 1,
CN_EP_SECOM_NPC_TYPE_NUM = 13,
CN_EP_EECOM_NPC_TYPE_NUM = 14,
CN_EP_SIZE_SMALL = 0,
CN_EP_SIZE_MIDDLE = 1,
CN_EP_SIZE_BIG = 2,
CN_EP_TICKET_ITEM_ID_SMALL = 115,
CN_EP_TICKET_ITEM_ID_MIDDLE = 116,
CN_EP_TICKET_ITEM_ID_BIG = 117,
CN_EP_TICKET_ITEM_ID_FREE = 118,
CN_EP_DISTANCE_ERROR_SAFE_RANGE = 1200,
CN_ACCOUNT_LEVEL__MASTER = 1,
CN_ACCOUNT_LEVEL__POWER_DEVELOPER = 10,
CN_ACCOUNT_LEVEL__QA = 20,
CN_ACCOUNT_LEVEL__GM = 30,
CN_ACCOUNT_LEVEL__CS = 40,
CN_ACCOUNT_LEVEL__FREE_USER = 48,
CN_ACCOUNT_LEVEL__PAY_USER = 49,
CN_ACCOUNT_LEVEL__DEVELOPER = 50,
CN_ACCOUNT_LEVEL__CLOSEBETA_USER = 80,
CN_ACCOUNT_LEVEL__OPENBETA_USER = 85,
CN_ACCOUNT_LEVEL__USER = 99,
CN_SPECIAL_STATE_FLAG__PRINT_GM = 1,
CN_SPECIAL_STATE_FLAG__INVISIBLE = 2,
CN_SPECIAL_STATE_FLAG__INVULNERABLE = 4,
CN_SPECIAL_STATE_FLAG__FULL_UI = 16,
CN_SPECIAL_STATE_FLAG__COMBAT = 32,
CN_SPECIAL_STATE_FLAG__MUTE_FREECHAT = 64,
CN_GM_SET_VALUE_TYPE__HP = 1,
CN_GM_SET_VALUE_TYPE__WEAPON_BATTERY = 2,
CN_GM_SET_VALUE_TYPE__NANO_BATTERY = 3,
CN_GM_SET_VALUE_TYPE__FUSION_MATTER = 4,
CN_GM_SET_VALUE_TYPE__CANDY = 5,
CN_GM_SET_VALUE_TYPE__SPEED = 6,
CN_GM_SET_VALUE_TYPE__JUMP = 7,
CN_GM_SET_VALUE_TYPE__END = 8,
HEIGHT_CLIMBABLE = 150,
CN_GROUP_WARP_CHECK_RANGE = 1000,
WYVERN_LOCATION_FLAG_SIZE = 2,
CN_PC_EVENT_ID_GET_NANO_QUEST = 1,
CN_PC_EVENT_ID_DEFEAT_FUSE_AND_GET_NANO = 2,
_dCN_STREETSTALL__ITEMLIST_COUNT_MAX = 5,
CSB_BIT_NONE = 0, // 0
CSB_BIT_UP_MOVE_SPEED = 0x1, // 1
CSB_BIT_UP_SWIM_SPEED = 0x2, // 2
CSB_BIT_UP_JUMP_HEIGHT = 0x4, // 4
CSB_BIT_UP_STEALTH = 0x8, // 8
CSB_BIT_PHOENIX = 0x10, // 16
CSB_BIT_PROTECT_BATTERY = 0x20, // 32
CSB_BIT_PROTECT_INFECTION = 0x40, // 64
CSB_BIT_DN_MOVE_SPEED = 0x80, // 128
CSB_BIT_DN_ATTACK_SPEED = 0x100, // 256
CSB_BIT_STUN = 0x200, // 512
CSB_BIT_MEZ = 0x400, // 1024
CSB_BIT_KNOCKDOWN = 0x800, // 2048
CSB_BIT_MINIMAP_ENEMY = 0x1000, // 4096
CSB_BIT_MINIMAP_TRESURE = 0x2000, // 8192
CSB_BIT_REWARD_BLOB = 0x4000, // 16384
CSB_BIT_REWARD_CASH = 0x8000, // 32768
CSB_BIT_INFECTION = 0x10000, // 65536
CSB_BIT_FREEDOM = 0x20000, // 131072
CSB_BIT_BOUNDINGBALL = 0x40000, // 262144
CSB_BIT_INVULNERABLE = 0x80000, // 524288
CSB_BIT_STIMPAKSLOT1 = 0x100000, // 1048576
CSB_BIT_STIMPAKSLOT2 = 0x200000, // 2097152
CSB_BIT_STIMPAKSLOT3 = 0x400000, // 4194304
CSB_BIT_HEAL = 0x800000, // 8388608
CSB_BIT_EXTRABANK = 0x1000000, // 16777216
TIME_BUFF_CONFIRM_KEY_MAX = 2000000000,
READPACKET_SUCC = 0,
READPACKET_FAIL = 1,
READPACKET_RETURN = 2,
BITMASK_FROM2TO = 0x7fffffff, // 2147483647
BITMASK_FROM = 0x7fffffff, // 2147483647
BITMASK_TO = 0xf000000, // 251658240
BITMASK_SENDBLOCK = 0x800000, // 8388608
BITMASK_AUTHED = 0x400000, // 4194304
BITMASK_U_ID = 0xfff, // 4095
CL2LS = 0x12000000,
CL2FE = 0x13000000,
LS2CL = 0x21000000,
LS2LS = 0x22000000,
LS2DBA = 0x27000000,
FE2CL = 0x31000000,
FE2FE = 0x33000000,
FE2GS = 0x34000000,
FE2EP = 0x36000000,
FE2MSG = 0x38000000,
GS2FE = 0x43000000,
GS2GS = 0x44000000,
GS2AI = 0x45000000,
GS2EP = 0x46000000,
GS2DBA = 0x47000000,
GS2MSG = 0x48000000,
GS2MGR = 0x4a000000,
AI2GS = 0x54000000,
EP2FE = 0x63000000,
EP2GS = 0x64000000,
DBA2GS = 0x74000000,
DBA2EP = 0x75000000,
MSG2FE = 0x7fffffff,
MSG2GS = 0x7fffffff,
MSG2CMSG = 0x7fffffff,
CMSG2MSG = 0x7fffffff,
MGR2SPY = 0x7fffffff,
SPY2MGR = 0x7fffffff,
MGR2SA = 0x7fffffff,
SA2MGR = 0x7fffffff,
SA2SPY = 0x7fffffff,
SPY2SA = 0x7fffffff,
SPY2SVR = 0x7fffffff,
SVR2SPY = 0x7fffffff,
SCH2SVR = 0x7fffffff,
SCH2LS = 0x7fffffff,
SCH2FE = 0x7fffffff,
SCH2GS = 0x7fffffff,
SCH2AI = 0x7fffffff,
SCH2EP = 0x7fffffff,
SCH2DBA = 0x7fffffff,
SCH2MSG = 0x7fffffff,
SCH2CMSG = 0x7fffffff,
CL2CDR = 0x1f000000,
SENDBLOCK = 0x800000, // 8388608
AUTHED_X = 0, // 0
AUTHED_O = 0x400000, // 4194304
SEND_SVR_FE = 1,
SEND_SVR_FE_ANY = 2,
SEND_SVR_FE_ALL = 3,
SEND_SVR_AI = 4,
SEND_SVR_AI_ANY = 5,
SEND_SVR_AI_ALL = 6,
SEND_SVR_FE_AI_ALL = 7,
SEND_SVR_DBA = 8,
SEND_SVR_GS = 9,
SEND_SVR_MSG = 10,
SEND_SVR_MSG_ANY = 11,
SEND_SVR_MSG_ALL = 12,
SEND_UNICAST = 1,
SEND_ANYCAST = 2,
SEND_ANYCAST_NEW = 3,
SEND_BROADCAST = 4,
CN_PACKET_BUFFER_SIZE = 4096,
P_CL2LS_REQ_LOGIN = 0x12000001, // 301989889
P_CL2LS_REQ_CHECK_CHAR_NAME = 0x12000002, // 301989890
P_CL2LS_REQ_SAVE_CHAR_NAME = 0x12000003, // 301989891
P_CL2LS_REQ_CHAR_CREATE = 0x12000004, // 301989892
P_CL2LS_REQ_CHAR_SELECT = 0x12000005, // 301989893
P_CL2LS_REQ_CHAR_DELETE = 0x12000006, // 301989894
P_CL2LS_REQ_SHARD_SELECT = 0x12000007, // 301989895
P_CL2LS_REQ_SHARD_LIST_INFO = 0x12000008, // 301989896
P_CL2LS_CHECK_NAME_LIST = 0x12000009, // 301989897
P_CL2LS_REQ_SAVE_CHAR_TUTOR = 0x1200000a, // 301989898
P_CL2LS_REQ_PC_EXIT_DUPLICATE = 0x1200000b, // 301989899
P_CL2LS_REP_LIVE_CHECK = 0x1200000c, // 301989900
P_CL2LS_REQ_CHANGE_CHAR_NAME = 0x1200000d, // 301989901
P_CL2LS_REQ_SERVER_SELECT = 0x1200000e, // 301989902
P_CL2FE_REQ_PC_ENTER = 0x13000001, // 318767105
P_CL2FE_REQ_PC_EXIT = 0x13000002, // 318767106
P_CL2FE_REQ_PC_MOVE = 0x13000003, // 318767107
P_CL2FE_REQ_PC_STOP = 0x13000004, // 318767108
P_CL2FE_REQ_PC_JUMP = 0x13000005, // 318767109
P_CL2FE_REQ_PC_ATTACK_NPCs = 0x13000006, // 318767110
P_CL2FE_REQ_SEND_FREECHAT_MESSAGE = 0x13000007, // 318767111
P_CL2FE_REQ_SEND_MENUCHAT_MESSAGE = 0x13000008, // 318767112
P_CL2FE_REQ_PC_REGEN = 0x13000009, // 318767113
P_CL2FE_REQ_ITEM_MOVE = 0x1300000a, // 318767114
P_CL2FE_REQ_PC_TASK_START = 0x1300000b, // 318767115
P_CL2FE_REQ_PC_TASK_END = 0x1300000c, // 318767116
P_CL2FE_REQ_NANO_EQUIP = 0x1300000d, // 318767117
P_CL2FE_REQ_NANO_UNEQUIP = 0x1300000e, // 318767118
P_CL2FE_REQ_NANO_ACTIVE = 0x1300000f, // 318767119
P_CL2FE_REQ_NANO_TUNE = 0x13000010, // 318767120
P_CL2FE_REQ_NANO_SKILL_USE = 0x13000011, // 318767121
P_CL2FE_REQ_PC_TASK_STOP = 0x13000012, // 318767122
P_CL2FE_REQ_PC_TASK_CONTINUE = 0x13000013, // 318767123
P_CL2FE_REQ_PC_GOTO = 0x13000014, // 318767124
P_CL2FE_REQ_CHARGE_NANO_STAMINA = 0x13000015, // 318767125
P_CL2FE_REQ_PC_KILL_QUEST_NPCs = 0x13000016, // 318767126
P_CL2FE_REQ_PC_VENDOR_ITEM_BUY = 0x13000017, // 318767127
P_CL2FE_REQ_PC_VENDOR_ITEM_SELL = 0x13000018, // 318767128
P_CL2FE_REQ_PC_ITEM_DELETE = 0x13000019, // 318767129
P_CL2FE_REQ_PC_GIVE_ITEM = 0x1300001a, // 318767130
P_CL2FE_REQ_PC_ROCKET_STYLE_READY = 0x1300001b, // 318767131
P_CL2FE_REQ_PC_ROCKET_STYLE_FIRE = 0x1300001c, // 318767132
P_CL2FE_REQ_PC_ROCKET_STYLE_HIT = 0x1300001d, // 318767133
P_CL2FE_REQ_PC_GRENADE_STYLE_READY = 0x1300001e, // 318767134
P_CL2FE_REQ_PC_GRENADE_STYLE_FIRE = 0x1300001f, // 318767135
P_CL2FE_REQ_PC_GRENADE_STYLE_HIT = 0x13000020, // 318767136
P_CL2FE_REQ_PC_NANO_CREATE = 0x13000021, // 318767137
P_CL2FE_REQ_PC_TRADE_OFFER = 0x13000022, // 318767138
P_CL2FE_REQ_PC_TRADE_OFFER_CANCEL = 0x13000023, // 318767139
P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT = 0x13000024, // 318767140
P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL = 0x13000025, // 318767141
P_CL2FE_REQ_PC_TRADE_OFFER_ABORT = 0x13000026, // 318767142
P_CL2FE_REQ_PC_TRADE_CONFIRM = 0x13000027, // 318767143
P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL = 0x13000028, // 318767144
P_CL2FE_REQ_PC_TRADE_CONFIRM_ABORT = 0x13000029, // 318767145
P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER = 0x1300002a, // 318767146
P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER = 0x1300002b, // 318767147
P_CL2FE_REQ_PC_TRADE_CASH_REGISTER = 0x1300002c, // 318767148
P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT = 0x1300002d, // 318767149
P_CL2FE_REQ_PC_BANK_OPEN = 0x1300002e, // 318767150
P_CL2FE_REQ_PC_BANK_CLOSE = 0x1300002f, // 318767151
P_CL2FE_REQ_PC_VENDOR_START = 0x13000030, // 318767152
P_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE = 0x13000031, // 318767153
P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY = 0x13000032, // 318767154
P_CL2FE_REQ_PC_COMBAT_BEGIN = 0x13000033, // 318767155
P_CL2FE_REQ_PC_COMBAT_END = 0x13000034, // 318767156
P_CL2FE_REQ_REQUEST_MAKE_BUDDY = 0x13000035, // 318767157
P_CL2FE_REQ_ACCEPT_MAKE_BUDDY = 0x13000036, // 318767158
P_CL2FE_REQ_SEND_BUDDY_FREECHAT_MESSAGE = 0x13000037, // 318767159
P_CL2FE_REQ_SEND_BUDDY_MENUCHAT_MESSAGE = 0x13000038, // 318767160
P_CL2FE_REQ_GET_BUDDY_STYLE = 0x13000039, // 318767161
P_CL2FE_REQ_SET_BUDDY_BLOCK = 0x1300003a, // 318767162
P_CL2FE_REQ_REMOVE_BUDDY = 0x1300003b, // 318767163
P_CL2FE_REQ_GET_BUDDY_STATE = 0x1300003c, // 318767164
P_CL2FE_REQ_PC_JUMPPAD = 0x1300003d, // 318767165
P_CL2FE_REQ_PC_LAUNCHER = 0x1300003e, // 318767166
P_CL2FE_REQ_PC_ZIPLINE = 0x1300003f, // 318767167
P_CL2FE_REQ_PC_MOVEPLATFORM = 0x13000040, // 318767168
P_CL2FE_REQ_PC_SLOPE = 0x13000041, // 318767169
P_CL2FE_REQ_PC_STATE_CHANGE = 0x13000042, // 318767170
P_CL2FE_REQ_PC_MAP_WARP = 0x13000043, // 318767171
P_CL2FE_REQ_PC_GIVE_NANO = 0x13000044, // 318767172
P_CL2FE_REQ_NPC_SUMMON = 0x13000045, // 318767173
P_CL2FE_REQ_NPC_UNSUMMON = 0x13000046, // 318767174
P_CL2FE_REQ_ITEM_CHEST_OPEN = 0x13000047, // 318767175
P_CL2FE_REQ_PC_GIVE_NANO_SKILL = 0x13000048, // 318767176
P_CL2FE_DOT_DAMAGE_ONOFF = 0x13000049, // 318767177
P_CL2FE_REQ_PC_VENDOR_BATTERY_BUY = 0x1300004a, // 318767178
P_CL2FE_REQ_PC_WARP_USE_NPC = 0x1300004b, // 318767179
P_CL2FE_REQ_PC_GROUP_INVITE = 0x1300004c, // 318767180
P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE = 0x1300004d, // 318767181
P_CL2FE_REQ_PC_GROUP_JOIN = 0x1300004e, // 318767182
P_CL2FE_REQ_PC_GROUP_LEAVE = 0x1300004f, // 318767183
P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT = 0x13000050, // 318767184
P_CL2FE_REQ_PC_BUDDY_WARP = 0x13000051, // 318767185
P_CL2FE_REQ_GET_MEMBER_STYLE = 0x13000052, // 318767186
P_CL2FE_REQ_GET_GROUP_STYLE = 0x13000053, // 318767187
P_CL2FE_REQ_PC_CHANGE_MENTOR = 0x13000054, // 318767188
P_CL2FE_REQ_GET_BUDDY_LOCATION = 0x13000055, // 318767189
P_CL2FE_REQ_NPC_GROUP_SUMMON = 0x13000056, // 318767190
P_CL2FE_REQ_PC_WARP_TO_PC = 0x13000057, // 318767191
P_CL2FE_REQ_EP_RANK_GET_LIST = 0x13000058, // 318767192
P_CL2FE_REQ_EP_RANK_GET_DETAIL = 0x13000059, // 318767193
P_CL2FE_REQ_EP_RANK_GET_PC_INFO = 0x1300005a, // 318767194
P_CL2FE_REQ_EP_RACE_START = 0x1300005b, // 318767195
P_CL2FE_REQ_EP_RACE_END = 0x1300005c, // 318767196
P_CL2FE_REQ_EP_RACE_CANCEL = 0x1300005d, // 318767197
P_CL2FE_REQ_EP_GET_RING = 0x1300005e, // 318767198
P_CL2FE_REQ_IM_CHANGE_SWITCH_STATUS = 0x1300005f, // 318767199
P_CL2FE_REQ_SHINY_PICKUP = 0x13000060, // 318767200
P_CL2FE_REQ_SHINY_SUMMON = 0x13000061, // 318767201
P_CL2FE_REQ_PC_MOVETRANSPORTATION = 0x13000062, // 318767202
P_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE = 0x13000063, // 318767203
P_CL2FE_REQ_SEND_ANY_GROUP_FREECHAT_MESSAGE = 0x13000064, // 318767204
P_CL2FE_REQ_BARKER = 0x13000065, // 318767205
P_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE = 0x13000066, // 318767206
P_CL2FE_REQ_SEND_ANY_GROUP_MENUCHAT_MESSAGE = 0x13000067, // 318767207
P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION = 0x13000068, // 318767208
P_CL2FE_REQ_PC_WARP_USE_TRANSPORTATION = 0x13000069, // 318767209
P_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH = 0x1300006a, // 318767210
P_CL2FE_GM_REQ_PC_SET_VALUE = 0x1300006b, // 318767211
P_CL2FE_GM_REQ_KICK_PLAYER = 0x1300006c, // 318767212
P_CL2FE_GM_REQ_TARGET_PC_TELEPORT = 0x1300006d, // 318767213
P_CL2FE_GM_REQ_PC_LOCATION = 0x1300006e, // 318767214
P_CL2FE_GM_REQ_PC_ANNOUNCE = 0x1300006f, // 318767215
P_CL2FE_REQ_SET_PC_BLOCK = 0x13000070, // 318767216
P_CL2FE_REQ_REGIST_RXCOM = 0x13000071, // 318767217
P_CL2FE_GM_REQ_PC_MOTD_REGISTER = 0x13000072, // 318767218
P_CL2FE_REQ_ITEM_USE = 0x13000073, // 318767219
P_CL2FE_REQ_WARP_USE_RECALL = 0x13000074, // 318767220
P_CL2FE_REP_LIVE_CHECK = 0x13000075, // 318767221
P_CL2FE_REQ_PC_MISSION_COMPLETE = 0x13000076, // 318767222
P_CL2FE_REQ_PC_TASK_COMPLETE = 0x13000077, // 318767223
P_CL2FE_REQ_NPC_INTERACTION = 0x13000078, // 318767224
P_CL2FE_DOT_HEAL_ONOFF = 0x13000079, // 318767225
P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH = 0x1300007a, // 318767226
P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK = 0x1300007b, // 318767227
P_CL2FE_REQ_PC_READ_EMAIL = 0x1300007c, // 318767228
P_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST = 0x1300007d, // 318767229
P_CL2FE_REQ_PC_DELETE_EMAIL = 0x1300007e, // 318767230
P_CL2FE_REQ_PC_SEND_EMAIL = 0x1300007f, // 318767231
P_CL2FE_REQ_PC_RECV_EMAIL_ITEM = 0x13000080, // 318767232
P_CL2FE_REQ_PC_RECV_EMAIL_CANDY = 0x13000081, // 318767233
P_CL2FE_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF = 0x13000082, // 318767234
P_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID = 0x13000083, // 318767235
P_CL2FE_REQ_NPC_GROUP_INVITE = 0x13000084, // 318767236
P_CL2FE_REQ_NPC_GROUP_KICK = 0x13000085, // 318767237
P_CL2FE_REQ_PC_FIRST_USE_FLAG_SET = 0x13000086, // 318767238
P_CL2FE_REQ_PC_TRANSPORT_WARP = 0x13000087, // 318767239
P_CL2FE_REQ_PC_TIME_TO_GO_WARP = 0x13000088, // 318767240
P_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL = 0x13000089, // 318767241
P_CL2FE_REQ_CHANNEL_INFO = 0x1300008a, // 318767242
P_CL2FE_REQ_PC_CHANNEL_NUM = 0x1300008b, // 318767243
P_CL2FE_REQ_PC_WARP_CHANNEL = 0x1300008c, // 318767244
P_CL2FE_REQ_PC_LOADING_COMPLETE = 0x1300008d, // 318767245
P_CL2FE_REQ_PC_FIND_NAME_MAKE_BUDDY = 0x1300008e, // 318767246
P_CL2FE_REQ_PC_FIND_NAME_ACCEPT_BUDDY = 0x1300008f, // 318767247
P_CL2FE_REQ_PC_ATTACK_CHARs = 0x13000090, // 318767248
P_CL2FE_PC_STREETSTALL_REQ_READY = 0x13000091, // 318767249
P_CL2FE_PC_STREETSTALL_REQ_CANCEL = 0x13000092, // 318767250
P_CL2FE_PC_STREETSTALL_REQ_REGIST_ITEM = 0x13000093, // 318767251
P_CL2FE_PC_STREETSTALL_REQ_UNREGIST_ITEM = 0x13000094, // 318767252
P_CL2FE_PC_STREETSTALL_REQ_SALE_START = 0x13000095, // 318767253
P_CL2FE_PC_STREETSTALL_REQ_ITEM_LIST = 0x13000096, // 318767254
P_CL2FE_PC_STREETSTALL_REQ_ITEM_BUY = 0x13000097, // 318767255
P_CL2FE_REQ_PC_ITEM_COMBINATION = 0x13000098, // 318767256
P_CL2FE_GM_REQ_SET_PC_SKILL = 0x13000099, // 318767257
P_CL2FE_REQ_PC_SKILL_ADD = 0x1300009a, // 318767258
P_CL2FE_REQ_PC_SKILL_DEL = 0x1300009b, // 318767259
P_CL2FE_REQ_PC_SKILL_USE = 0x1300009c, // 318767260
P_CL2FE_REQ_PC_ROPE = 0x1300009d, // 318767261
P_CL2FE_REQ_PC_BELT = 0x1300009e, // 318767262
P_CL2FE_REQ_PC_VEHICLE_ON = 0x1300009f, // 318767263
P_CL2FE_REQ_PC_VEHICLE_OFF = 0x130000a0, // 318767264
P_CL2FE_REQ_PC_REGIST_QUICK_SLOT = 0x130000a1, // 318767265
P_CL2FE_REQ_PC_DISASSEMBLE_ITEM = 0x130000a2, // 318767266
P_CL2FE_GM_REQ_REWARD_RATE = 0x130000a3, // 318767267
P_CL2FE_REQ_PC_ITEM_ENCHANT = 0x130000a4, // 318767268
P_FE2CL_ERROR = 0x31000000, // 822083584
P_FE2CL_REP_PC_ENTER_FAIL = 0x31000001, // 822083585
P_FE2CL_REP_PC_ENTER_SUCC = 0x31000002, // 822083586
P_FE2CL_PC_NEW = 0x31000003, // 822083587
P_FE2CL_REP_PC_EXIT_FAIL = 0x31000004, // 822083588
P_FE2CL_REP_PC_EXIT_SUCC = 0x31000005, // 822083589
P_FE2CL_PC_EXIT = 0x31000006, // 822083590
P_FE2CL_PC_AROUND = 0x31000007, // 822083591
P_FE2CL_PC_MOVE = 0x31000008, // 822083592
P_FE2CL_PC_STOP = 0x31000009, // 822083593
P_FE2CL_PC_JUMP = 0x3100000a, // 822083594
P_FE2CL_NPC_ENTER = 0x3100000b, // 822083595
P_FE2CL_NPC_EXIT = 0x3100000c, // 822083596
P_FE2CL_NPC_MOVE = 0x3100000d, // 822083597
P_FE2CL_NPC_NEW = 0x3100000e, // 822083598
P_FE2CL_NPC_AROUND = 0x3100000f, // 822083599
P_FE2CL_AROUND_DEL_PC = 0x31000010, // 822083600
P_FE2CL_AROUND_DEL_NPC = 0x31000011, // 822083601
P_FE2CL_REP_SEND_FREECHAT_MESSAGE_SUCC = 0x31000012, // 822083602
P_FE2CL_REP_SEND_FREECHAT_MESSAGE_FAIL = 0x31000013, // 822083603
P_FE2CL_PC_ATTACK_NPCs_SUCC = 0x31000014, // 822083604
P_FE2CL_PC_ATTACK_NPCs = 0x31000015, // 822083605
P_FE2CL_NPC_ATTACK_PCs = 0x31000016, // 822083606
P_FE2CL_REP_PC_REGEN_SUCC = 0x31000017, // 822083607
P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC = 0x31000018, // 822083608
P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_FAIL = 0x31000019, // 822083609
P_FE2CL_PC_ITEM_MOVE_SUCC = 0x3100001a, // 822083610
P_FE2CL_PC_EQUIP_CHANGE = 0x3100001b, // 822083611
P_FE2CL_REP_PC_TASK_START_SUCC = 0x3100001c, // 822083612
P_FE2CL_REP_PC_TASK_START_FAIL = 0x3100001d, // 822083613
P_FE2CL_REP_PC_TASK_END_SUCC = 0x3100001e, // 822083614
P_FE2CL_REP_PC_TASK_END_FAIL = 0x3100001f, // 822083615
P_FE2CL_NPC_SKILL_READY = 0x31000020, // 822083616
P_FE2CL_NPC_SKILL_FIRE = 0x31000021, // 822083617
P_FE2CL_NPC_SKILL_HIT = 0x31000022, // 822083618
P_FE2CL_NPC_SKILL_CORRUPTION_READY = 0x31000023, // 822083619
P_FE2CL_NPC_SKILL_CORRUPTION_HIT = 0x31000024, // 822083620
P_FE2CL_NPC_SKILL_CANCEL = 0x31000025, // 822083621
P_FE2CL_REP_NANO_EQUIP_SUCC = 0x31000026, // 822083622
P_FE2CL_REP_NANO_UNEQUIP_SUCC = 0x31000027, // 822083623
P_FE2CL_REP_NANO_ACTIVE_SUCC = 0x31000028, // 822083624
P_FE2CL_REP_NANO_TUNE_SUCC = 0x31000029, // 822083625
P_FE2CL_NANO_ACTIVE = 0x3100002a, // 822083626
P_FE2CL_NANO_SKILL_USE_SUCC = 0x3100002b, // 822083627
P_FE2CL_NANO_SKILL_USE = 0x3100002c, // 822083628
P_FE2CL_REP_PC_TASK_STOP_SUCC = 0x3100002d, // 822083629
P_FE2CL_REP_PC_TASK_STOP_FAIL = 0x3100002e, // 822083630
P_FE2CL_REP_PC_TASK_CONTINUE_SUCC = 0x3100002f, // 822083631
P_FE2CL_REP_PC_TASK_CONTINUE_FAIL = 0x31000030, // 822083632
P_FE2CL_REP_PC_GOTO_SUCC = 0x31000031, // 822083633
P_FE2CL_REP_CHARGE_NANO_STAMINA = 0x31000032, // 822083634
P_FE2CL_REP_PC_TICK = 0x31000033, // 822083635
P_FE2CL_REP_PC_KILL_QUEST_NPCs_SUCC = 0x31000034, // 822083636
P_FE2CL_REP_PC_VENDOR_ITEM_BUY_SUCC = 0x31000035, // 822083637
P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL = 0x31000036, // 822083638
P_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC = 0x31000037, // 822083639
P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL = 0x31000038, // 822083640
P_FE2CL_REP_PC_ITEM_DELETE_SUCC = 0x31000039, // 822083641
P_FE2CL_PC_ROCKET_STYLE_READY = 0x3100003a, // 822083642
P_FE2CL_REP_PC_ROCKET_STYLE_FIRE_SUCC = 0x3100003b, // 822083643
P_FE2CL_PC_ROCKET_STYLE_FIRE = 0x3100003c, // 822083644
P_FE2CL_PC_ROCKET_STYLE_HIT = 0x3100003d, // 822083645
P_FE2CL_PC_GRENADE_STYLE_READY = 0x3100003e, // 822083646
P_FE2CL_REP_PC_GRENADE_STYLE_FIRE_SUCC = 0x3100003f, // 822083647
P_FE2CL_PC_GRENADE_STYLE_FIRE = 0x31000040, // 822083648
P_FE2CL_PC_GRENADE_STYLE_HIT = 0x31000041, // 822083649
P_FE2CL_REP_PC_TRADE_OFFER = 0x31000042, // 822083650
P_FE2CL_REP_PC_TRADE_OFFER_CANCEL = 0x31000043, // 822083651
P_FE2CL_REP_PC_TRADE_OFFER_SUCC = 0x31000044, // 822083652
P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL = 0x31000045, // 822083653
P_FE2CL_REP_PC_TRADE_OFFER_ABORT = 0x31000046, // 822083654
P_FE2CL_REP_PC_TRADE_CONFIRM = 0x31000047, // 822083655
P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL = 0x31000048, // 822083656
P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT = 0x31000049, // 822083657
P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC = 0x3100004a, // 822083658
P_FE2CL_REP_PC_TRADE_CONFIRM_FAIL = 0x3100004b, // 822083659
P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC = 0x3100004c, // 822083660
P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_FAIL = 0x3100004d, // 822083661
P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC = 0x3100004e, // 822083662
P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_FAIL = 0x3100004f, // 822083663
P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC = 0x31000050, // 822083664
P_FE2CL_REP_PC_TRADE_CASH_REGISTER_FAIL = 0x31000051, // 822083665
P_FE2CL_REP_PC_TRADE_EMOTES_CHAT = 0x31000052, // 822083666
P_FE2CL_REP_PC_NANO_CREATE_SUCC = 0x31000053, // 822083667
P_FE2CL_REP_PC_NANO_CREATE_FAIL = 0x31000054, // 822083668
P_FE2CL_REP_NANO_TUNE_FAIL = 0x31000055, // 822083669
P_FE2CL_REP_PC_BANK_OPEN_SUCC = 0x31000056, // 822083670
P_FE2CL_REP_PC_BANK_OPEN_FAIL = 0x31000057, // 822083671
P_FE2CL_REP_PC_BANK_CLOSE_SUCC = 0x31000058, // 822083672
P_FE2CL_REP_PC_BANK_CLOSE_FAIL = 0x31000059, // 822083673
P_FE2CL_REP_PC_VENDOR_START_SUCC = 0x3100005a, // 822083674
P_FE2CL_REP_PC_VENDOR_START_FAIL = 0x3100005b, // 822083675
P_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC = 0x3100005c, // 822083676
P_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_FAIL = 0x3100005d, // 822083677
P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC = 0x3100005e, // 822083678
P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL = 0x3100005f, // 822083679
P_FE2CL_CHAR_TIME_BUFF_TIME_OUT = 0x31000060, // 822083680
P_FE2CL_REP_PC_GIVE_ITEM_SUCC = 0x31000061, // 822083681
P_FE2CL_REP_PC_GIVE_ITEM_FAIL = 0x31000062, // 822083682
P_FE2CL_REP_PC_BUDDYLIST_INFO_SUCC = 0x31000063, // 822083683
P_FE2CL_REP_PC_BUDDYLIST_INFO_FAIL = 0x31000064, // 822083684
P_FE2CL_REP_REQUEST_MAKE_BUDDY_SUCC = 0x7fffffff, // 2147483647
P_FE2CL_REP_REQUEST_MAKE_BUDDY_FAIL = 0x31000066, // 822083686
P_FE2CL_REP_ACCEPT_MAKE_BUDDY_SUCC = 0x31000067, // 822083687
P_FE2CL_REP_ACCEPT_MAKE_BUDDY_FAIL = 0x31000068, // 822083688
P_FE2CL_REP_SEND_BUDDY_FREECHAT_MESSAGE_SUCC = 0x31000069, // 822083689
P_FE2CL_REP_SEND_BUDDY_FREECHAT_MESSAGE_FAIL = 0x3100006a, // 822083690
P_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_SUCC = 0x3100006b, // 822083691
P_FE2CL_REP_SEND_BUDDY_MENUCHAT_MESSAGE_FAIL = 0x3100006c, // 822083692
P_FE2CL_REP_GET_BUDDY_STYLE_SUCC = 0x3100006d, // 822083693
P_FE2CL_REP_GET_BUDDY_STYLE_FAIL = 0x3100006e, // 822083694
P_FE2CL_REP_GET_BUDDY_STATE_SUCC = 0x3100006f, // 822083695
P_FE2CL_REP_GET_BUDDY_STATE_FAIL = 0x31000070, // 822083696
P_FE2CL_REP_SET_BUDDY_BLOCK_SUCC = 0x31000071, // 822083697
P_FE2CL_REP_SET_BUDDY_BLOCK_FAIL = 0x31000072, // 822083698
P_FE2CL_REP_REMOVE_BUDDY_SUCC = 0x31000073, // 822083699
P_FE2CL_REP_REMOVE_BUDDY_FAIL = 0x31000074, // 822083700
P_FE2CL_PC_JUMPPAD = 0x31000075, // 822083701
P_FE2CL_PC_LAUNCHER = 0x31000076, // 822083702
P_FE2CL_PC_ZIPLINE = 0x31000077, // 822083703
P_FE2CL_PC_MOVEPLATFORM = 0x31000078, // 822083704
P_FE2CL_PC_SLOPE = 0x31000079, // 822083705
P_FE2CL_PC_STATE_CHANGE = 0x3100007a, // 822083706
P_FE2CL_REP_REQUEST_MAKE_BUDDY_SUCC_TO_ACCEPTER = 0x3100007b, // 822083707
P_FE2CL_REP_REWARD_ITEM = 0x3100007c, // 822083708
P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC = 0x3100007d, // 822083709
P_FE2CL_REP_ITEM_CHEST_OPEN_FAIL = 0x3100007e, // 822083710
P_FE2CL_CHAR_TIME_BUFF_TIME_TICK = 0x3100007f, // 822083711
P_FE2CL_REP_PC_VENDOR_BATTERY_BUY_SUCC = 0x31000080, // 822083712
P_FE2CL_REP_PC_VENDOR_BATTERY_BUY_FAIL = 0x31000081, // 822083713
P_FE2CL_NPC_ROCKET_STYLE_FIRE = 0x31000082, // 822083714
P_FE2CL_NPC_GRENADE_STYLE_FIRE = 0x31000083, // 822083715
P_FE2CL_NPC_BULLET_STYLE_HIT = 0x31000084, // 822083716
P_FE2CL_CHARACTER_ATTACK_CHARACTERs = 0x31000085, // 822083717
P_FE2CL_PC_GROUP_INVITE = 0x31000086, // 822083718
P_FE2CL_PC_GROUP_INVITE_FAIL = 0x31000087, // 822083719
P_FE2CL_PC_GROUP_INVITE_REFUSE = 0x31000088, // 822083720
P_FE2CL_PC_GROUP_JOIN = 0x31000089, // 822083721
P_FE2CL_PC_GROUP_JOIN_FAIL = 0x3100008a, // 822083722
P_FE2CL_PC_GROUP_JOIN_SUCC = 0x3100008b, // 822083723
P_FE2CL_PC_GROUP_LEAVE = 0x3100008c, // 822083724
P_FE2CL_PC_GROUP_LEAVE_FAIL = 0x3100008d, // 822083725
P_FE2CL_PC_GROUP_LEAVE_SUCC = 0x3100008e, // 822083726
P_FE2CL_PC_GROUP_MEMBER_INFO = 0x3100008f, // 822083727
P_FE2CL_REP_PC_WARP_USE_NPC_SUCC = 0x31000090, // 822083728
P_FE2CL_REP_PC_WARP_USE_NPC_FAIL = 0x31000091, // 822083729
P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT = 0x31000092, // 822083730
P_FE2CL_REP_PC_CHANGE_MENTOR_SUCC = 0x31000093, // 822083731
P_FE2CL_REP_PC_CHANGE_MENTOR_FAIL = 0x31000094, // 822083732
P_FE2CL_REP_GET_MEMBER_STYLE_FAIL = 0x31000095, // 822083733
P_FE2CL_REP_GET_MEMBER_STYLE_SUCC = 0x31000096, // 822083734
P_FE2CL_REP_GET_GROUP_STYLE_FAIL = 0x31000097, // 822083735
P_FE2CL_REP_GET_GROUP_STYLE_SUCC = 0x31000098, // 822083736
P_FE2CL_PC_REGEN = 0x31000099, // 822083737
P_FE2CL_INSTANCE_MAP_INFO = 0x3100009a, // 822083738
P_FE2CL_TRANSPORTATION_ENTER = 0x3100009b, // 822083739
P_FE2CL_TRANSPORTATION_EXIT = 0x3100009c, // 822083740
P_FE2CL_TRANSPORTATION_MOVE = 0x3100009d, // 822083741
P_FE2CL_TRANSPORTATION_NEW = 0x3100009e, // 822083742
P_FE2CL_TRANSPORTATION_AROUND = 0x3100009f, // 822083743
P_FE2CL_AROUND_DEL_TRANSPORTATION = 0x310000a0, // 822083744
P_FE2CL_REP_EP_RANK_LIST = 0x310000a1, // 822083745
P_FE2CL_REP_EP_RANK_DETAIL = 0x310000a2, // 822083746
P_FE2CL_REP_EP_RANK_PC_INFO = 0x310000a3, // 822083747
P_FE2CL_REP_EP_RACE_START_SUCC = 0x310000a4, // 822083748
P_FE2CL_REP_EP_RACE_START_FAIL = 0x310000a5, // 822083749
P_FE2CL_REP_EP_RACE_END_SUCC = 0x310000a6, // 822083750
P_FE2CL_REP_EP_RACE_END_FAIL = 0x310000a7, // 822083751
P_FE2CL_REP_EP_RACE_CANCEL_SUCC = 0x310000a8, // 822083752
P_FE2CL_REP_EP_RACE_CANCEL_FAIL = 0x310000a9, // 822083753
P_FE2CL_REP_EP_GET_RING_SUCC = 0x310000aa, // 822083754
P_FE2CL_REP_EP_GET_RING_FAIL = 0x310000ab, // 822083755
P_FE2CL_REP_IM_CHANGE_SWITCH_STATUS = 0x310000ac, // 822083756
P_FE2CL_SHINY_ENTER = 0x310000ad, // 822083757
P_FE2CL_SHINY_EXIT = 0x310000ae, // 822083758
P_FE2CL_SHINY_NEW = 0x310000af, // 822083759
P_FE2CL_SHINY_AROUND = 0x310000b0, // 822083760
P_FE2CL_AROUND_DEL_SHINY = 0x310000b1, // 822083761
P_FE2CL_REP_SHINY_PICKUP_FAIL = 0x310000b2, // 822083762
P_FE2CL_REP_SHINY_PICKUP_SUCC = 0x310000b3, // 822083763
P_FE2CL_PC_MOVETRANSPORTATION = 0x310000b4, // 822083764
P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC = 0x310000b5, // 822083765
P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_FAIL = 0x310000b6, // 822083766
P_FE2CL_REP_SEND_ANY_GROUP_FREECHAT_MESSAGE_SUCC = 0x310000b7, // 822083767
P_FE2CL_REP_SEND_ANY_GROUP_FREECHAT_MESSAGE_FAIL = 0x310000b8, // 822083768
P_FE2CL_REP_BARKER = 0x310000b9, // 822083769
P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC = 0x310000ba, // 822083770
P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_FAIL = 0x310000bb, // 822083771
P_FE2CL_REP_SEND_ANY_GROUP_MENUCHAT_MESSAGE_SUCC = 0x310000bc, // 822083772
P_FE2CL_REP_SEND_ANY_GROUP_MENUCHAT_MESSAGE_FAIL = 0x310000bd, // 822083773
P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_FAIL = 0x310000be, // 822083774
P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC = 0x310000bf, // 822083775
P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_FAIL = 0x310000c0, // 822083776
P_FE2CL_REP_PC_WARP_USE_TRANSPORTATION_SUCC = 0x310000c1, // 822083777
P_FE2CL_ANNOUNCE_MSG = 0x310000c2, // 822083778
P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC = 0x310000c3, // 822083779
P_FE2CL_PC_SPECIAL_STATE_CHANGE = 0x310000c4, // 822083780
P_FE2CL_GM_REP_PC_SET_VALUE = 0x310000c5, // 822083781
P_FE2CL_GM_PC_CHANGE_VALUE = 0x310000c6, // 822083782
P_FE2CL_GM_REP_PC_LOCATION = 0x310000c7, // 822083783
P_FE2CL_GM_REP_PC_ANNOUNCE = 0x310000c8, // 822083784
P_FE2CL_REP_PC_BUDDY_WARP_FAIL = 0x310000c9, // 822083785
P_FE2CL_REP_PC_CHANGE_LEVEL = 0x310000ca, // 822083786
P_FE2CL_REP_SET_PC_BLOCK_SUCC = 0x310000cb, // 822083787
P_FE2CL_REP_SET_PC_BLOCK_FAIL = 0x310000cc, // 822083788
P_FE2CL_REP_REGIST_RXCOM = 0x310000cd, // 822083789
P_FE2CL_REP_REGIST_RXCOM_FAIL = 0x310000ce, // 822083790
P_FE2CL_PC_INVEN_FULL_MSG = 0x310000cf, // 822083791
P_FE2CL_REQ_LIVE_CHECK = 0x310000d0, // 822083792
P_FE2CL_PC_MOTD_LOGIN = 0x310000d1, // 822083793
P_FE2CL_REP_PC_ITEM_USE_FAIL = 0x310000d2, // 822083794
P_FE2CL_REP_PC_ITEM_USE_SUCC = 0x310000d3, // 822083795
P_FE2CL_PC_ITEM_USE = 0x310000d4, // 822083796
P_FE2CL_REP_GET_BUDDY_LOCATION_SUCC = 0x310000d5, // 822083797
P_FE2CL_REP_GET_BUDDY_LOCATION_FAIL = 0x310000d6, // 822083798
P_FE2CL_REP_PC_RIDING_FAIL = 0x310000d7, // 822083799
P_FE2CL_REP_PC_RIDING_SUCC = 0x310000d8, // 822083800
P_FE2CL_PC_RIDING = 0x310000d9, // 822083801
P_FE2CL_PC_BROOMSTICK_MOVE = 0x310000da, // 822083802
P_FE2CL_REP_PC_BUDDY_WARP_OTHER_SHARD_SUCC = 0x310000db, // 822083803
P_FE2CL_REP_WARP_USE_RECALL_FAIL = 0x310000dc, // 822083804
P_FE2CL_REP_PC_EXIT_DUPLICATE = 0x310000dd, // 822083805
P_FE2CL_REP_PC_MISSION_COMPLETE_SUCC = 0x310000de, // 822083806
P_FE2CL_PC_BUFF_UPDATE = 0x310000df, // 822083807
P_FE2CL_REP_PC_NEW_EMAIL = 0x310000e0, // 822083808
P_FE2CL_REP_PC_READ_EMAIL_SUCC = 0x310000e1, // 822083809
P_FE2CL_REP_PC_READ_EMAIL_FAIL = 0x310000e2, // 822083810
P_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC = 0x310000e3, // 822083811
P_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_FAIL = 0x310000e4, // 822083812
P_FE2CL_REP_PC_DELETE_EMAIL_SUCC = 0x310000e5, // 822083813
P_FE2CL_REP_PC_DELETE_EMAIL_FAIL = 0x310000e6, // 822083814
P_FE2CL_REP_PC_SEND_EMAIL_SUCC = 0x310000e7, // 822083815
P_FE2CL_REP_PC_SEND_EMAIL_FAIL = 0x310000e8, // 822083816
P_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC = 0x310000e9, // 822083817
P_FE2CL_REP_PC_RECV_EMAIL_ITEM_FAIL = 0x310000ea, // 822083818
P_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC = 0x310000eb, // 822083819
P_FE2CL_REP_PC_RECV_EMAIL_CANDY_FAIL = 0x310000ec, // 822083820
P_FE2CL_PC_SUDDEN_DEAD = 0x310000ed, // 822083821
P_FE2CL_REP_GM_REQ_TARGET_PC_SPECIAL_STATE_ONOFF_SUCC = 0x310000ee, // 822083822
P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID = 0x310000ef, // 822083823
P_FE2CL_REP_NPC_GROUP_INVITE_FAIL = 0x310000f0, // 822083824
P_FE2CL_REP_NPC_GROUP_INVITE_SUCC = 0x310000f1, // 822083825
P_FE2CL_REP_NPC_GROUP_KICK_FAIL = 0x310000f2, // 822083826
P_FE2CL_REP_NPC_GROUP_KICK_SUCC = 0x310000f3, // 822083827
P_FE2CL_PC_EVENT = 0x310000f4, // 822083828
P_FE2CL_REP_PC_TRANSPORT_WARP_SUCC = 0x310000f5, // 822083829
P_FE2CL_REP_PC_TRADE_EMOTES_CHAT_FAIL = 0x310000f6, // 822083830
P_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC = 0x310000f7, // 822083831
P_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_FAIL = 0x310000f8, // 822083832
P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC = 0x310000f9, // 822083833
P_FE2CL_REP_CHANNEL_INFO = 0x310000fa, // 822083834
P_FE2CL_REP_PC_CHANNEL_NUM = 0x310000fb, // 822083835
P_FE2CL_REP_PC_WARP_CHANNEL_FAIL = 0x310000fc, // 822083836
P_FE2CL_REP_PC_WARP_CHANNEL_SUCC = 0x310000fd, // 822083837
P_FE2CL_REP_PC_FIND_NAME_MAKE_BUDDY_SUCC = 0x310000fe, // 822083838
P_FE2CL_REP_PC_FIND_NAME_MAKE_BUDDY_FAIL = 0x310000ff, // 822083839
P_FE2CL_REP_PC_FIND_NAME_ACCEPT_BUDDY_FAIL = 0x31000100, // 822083840
P_FE2CL_REP_PC_BUDDY_WARP_SAME_SHARD_SUCC = 0x31000101, // 822083841
P_FE2CL_PC_ATTACK_CHARs_SUCC = 0x31000102, // 822083842
P_FE2CL_PC_ATTACK_CHARs = 0x31000103, // 822083843
P_FE2CL_NPC_ATTACK_CHARs = 0x31000104, // 822083844
P_FE2CL_REP_PC_CHANGE_LEVEL_SUCC = 0x31000105, // 822083845
P_FE2CL_REP_PC_NANO_CREATE = 0x31000106, // 822083846
P_FE2CL_PC_STREETSTALL_REP_READY_SUCC = 0x31000107, // 822083847
P_FE2CL_PC_STREETSTALL_REP_READY_FAIL = 0x31000108, // 822083848
P_FE2CL_PC_STREETSTALL_REP_CANCEL_SUCC = 0x31000109, // 822083849
P_FE2CL_PC_STREETSTALL_REP_CANCEL_FAIL = 0x3100010a, // 822083850
P_FE2CL_PC_STREETSTALL_REP_REGIST_ITEM_SUCC = 0x3100010b, // 822083851
P_FE2CL_PC_STREETSTALL_REP_REGIST_ITEM_FAIL = 0x3100010c, // 822083852
P_FE2CL_PC_STREETSTALL_REP_UNREGIST_ITEM_SUCC = 0x3100010d, // 822083853
P_FE2CL_PC_STREETSTALL_REP_UNREGIST_ITEM_FAIL = 0x3100010e, // 822083854
P_FE2CL_PC_STREETSTALL_REP_SALE_START_SUCC = 0x3100010f, // 822083855
P_FE2CL_PC_STREETSTALL_REP_SALE_START_FAIL = 0x31000110, // 822083856
P_FE2CL_PC_STREETSTALL_REP_ITEM_LIST = 0x31000111, // 822083857
P_FE2CL_PC_STREETSTALL_REP_ITEM_LIST_FAIL = 0x31000112, // 822083858
P_FE2CL_PC_STREETSTALL_REP_ITEM_BUY_SUCC_BUYER = 0x31000113, // 822083859
P_FE2CL_PC_STREETSTALL_REP_ITEM_BUY_SUCC_SELLER = 0x31000114, // 822083860
P_FE2CL_PC_STREETSTALL_REP_ITEM_BUY_FAIL = 0x31000115, // 822083861
P_FE2CL_REP_PC_ITEM_COMBINATION_SUCC = 0x31000116, // 822083862
P_FE2CL_REP_PC_ITEM_COMBINATION_FAIL = 0x31000117, // 822083863
P_FE2CL_PC_CASH_BUFF_UPDATE = 0x31000118, // 822083864
P_FE2CL_REP_PC_SKILL_ADD_SUCC = 0x31000119, // 822083865
P_FE2CL_REP_PC_SKILL_ADD_FAIL = 0x3100011a, // 822083866
P_FE2CL_REP_PC_SKILL_DEL_SUCC = 0x3100011b, // 822083867
P_FE2CL_REP_PC_SKILL_DEL_FAIL = 0x3100011c, // 822083868
P_FE2CL_REP_PC_SKILL_USE_SUCC = 0x3100011d, // 822083869
P_FE2CL_REP_PC_SKILL_USE_FAIL = 0x3100011e, // 822083870
P_FE2CL_PC_SKILL_USE = 0x3100011f, // 822083871
P_FE2CL_PC_ROPE = 0x31000120, // 822083872
P_FE2CL_PC_BELT = 0x31000121, // 822083873
P_FE2CL_PC_VEHICLE_ON_SUCC = 0x31000122, // 822083874
P_FE2CL_PC_VEHICLE_ON_FAIL = 0x31000123, // 822083875
P_FE2CL_PC_VEHICLE_OFF_SUCC = 0x31000124, // 822083876
P_FE2CL_PC_VEHICLE_OFF_FAIL = 0x31000125, // 822083877
P_FE2CL_PC_QUICK_SLOT_INFO = 0x31000126, // 822083878
P_FE2CL_REP_PC_REGIST_QUICK_SLOT_FAIL = 0x31000127, // 822083879
P_FE2CL_REP_PC_REGIST_QUICK_SLOT_SUCC = 0x31000128, // 822083880
P_FE2CL_PC_DELETE_TIME_LIMIT_ITEM = 0x31000129, // 822083881
P_FE2CL_REP_PC_DISASSEMBLE_ITEM_SUCC = 0x3100012a, // 822083882
P_FE2CL_REP_PC_DISASSEMBLE_ITEM_FAIL = 0x3100012b, // 822083883
P_FE2CL_GM_REP_REWARD_RATE_SUCC = 0x3100012c, // 822083884
P_FE2CL_REP_PC_ITEM_ENCHANT_SUCC = 0x3100012d, // 822083885
P_FE2CL_REP_PC_ITEM_ENCHANT_FAIL = 0x3100012e, // 822083886
P_LS2CL_REP_LOGIN_SUCC = 0x21000001, // 553648129
P_LS2CL_REP_LOGIN_FAIL = 0x21000002, // 553648130
P_LS2CL_REP_CHAR_INFO = 0x21000003, // 553648131
P_LS2CL_REP_CHECK_CHAR_NAME_SUCC = 0x21000005, // 553648133
P_LS2CL_REP_CHECK_CHAR_NAME_FAIL = 0x21000006, // 553648134
P_LS2CL_REP_SAVE_CHAR_NAME_SUCC = 0x21000007, // 553648135
P_LS2CL_REP_SAVE_CHAR_NAME_FAIL = 0x21000008, // 553648136
P_LS2CL_REP_CHAR_CREATE_SUCC = 0x21000009, // 553648137
P_LS2CL_REP_CHAR_CREATE_FAIL = 0x2100000a, // 553648138
P_LS2CL_REP_CHAR_SELECT_SUCC = 0x2100000b, // 553648139
P_LS2CL_REP_CHAR_SELECT_FAIL = 0x2100000c, // 553648140
P_LS2CL_REP_CHAR_DELETE_SUCC = 0x2100000d, // 553648141
P_LS2CL_REP_CHAR_DELETE_FAIL = 0x2100000e, // 553648142
P_LS2CL_REP_SHARD_SELECT_SUCC = 0x2100000f, // 553648143
P_LS2CL_REP_SHARD_SELECT_FAIL = 0x21000010, // 553648144
P_LS2CL_REP_VERSION_CHECK_SUCC = 0x21000011, // 553648145
P_LS2CL_REP_VERSION_CHECK_FAIL = 0x21000012, // 553648146
P_LS2CL_REP_CHECK_NAME_LIST_SUCC = 0x21000013, // 553648147
P_LS2CL_REP_CHECK_NAME_LIST_FAIL = 0x21000014, // 553648148
P_LS2CL_REP_PC_EXIT_DUPLICATE = 0x21000015, // 553648149
P_LS2CL_REQ_LIVE_CHECK = 0x21000016, // 553648150
P_LS2CL_REP_CHANGE_CHAR_NAME_SUCC = 0x21000017, // 553648151
P_LS2CL_REP_CHANGE_CHAR_NAME_FAIL = 0x21000018, // 553648152
P_LS2CL_REP_SHARD_LIST_INFO_SUCC = 0x21000019, // 553648153
};
/*
* Numbers of packets by type.
* Each is the last packet - the upper bits + 1
*/
enum {
N_CL2LS = 0xf,
N_CL2FE = 0xa5,
N_FE2CL = 0x12f,
N_LS2CL = 0x1a,
N_PACKETS = N_CL2LS + N_CL2FE + N_FE2CL + N_LS2CL
};
namespace Defines {
std::string p2str(int type, int val);
}

731
src/ItemManager.cpp Normal file
View File

@@ -0,0 +1,731 @@
#include "CNShardServer.hpp"
#include "CNStructs.hpp"
#include "ItemManager.hpp"
#include "PlayerManager.hpp"
#include "Player.hpp"
#include <string.h> // for memset() and memcmp()
#include <assert.h>
void ItemManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_ITEM, itemGMGiveHandler);
//Trade handlers
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, itemTradeOfferHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, itemTradeOfferAcceptHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, itemTradeOfferRefusalHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, itemTradeConfirmHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, itemTradeConfirmCancelHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, itemTradeRegisterItemHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, itemTradeUnregisterItemHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, itemTradeRegisterCashHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, itemTradeChatHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler);
}
void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_MOVE))
return; // ignore the malformed packet
sP_CL2FE_REQ_ITEM_MOVE* itemmove = (sP_CL2FE_REQ_ITEM_MOVE*)data->buf;
INITSTRUCT(sP_FE2CL_PC_ITEM_MOVE_SUCC, resp);
PlayerView& plr = PlayerManager::players[sock];
if (plr.plr->Equip[itemmove->iFromSlotNum].iType != 0 && itemmove->eFrom == 0 && itemmove->eTo == 0) {
// this packet should never happen unless it is a weapon, tell the client to do nothing and do nothing ourself
resp.eTo = itemmove->eFrom;
resp.iToSlotNum = itemmove->iFromSlotNum;
resp.ToSlotItem = plr.plr->Equip[itemmove->iToSlotNum];
resp.eFrom = itemmove->eTo;
resp.iFromSlotNum = itemmove->iToSlotNum;
resp.FromSlotItem = plr.plr->Equip[itemmove->iFromSlotNum];
sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC));
return;
}
if (itemmove->iToSlotNum > AINVEN_COUNT || itemmove->iToSlotNum < 0)
return; // sanity checks
sItemBase fromItem;
sItemBase toItem;
// eFrom 0 means from equip
if (itemmove->eFrom == 0) {
// unequiping an item
fromItem = plr.plr->Equip[itemmove->iFromSlotNum];
} else {
fromItem = plr.plr->Inven[itemmove->iFromSlotNum];
}
// eTo 0 means to equip
if (itemmove->eTo == 0) {
// equiping an item
toItem = plr.plr->Equip[itemmove->iToSlotNum];
plr.plr->Equip[itemmove->iToSlotNum] = fromItem;
} else {
toItem = plr.plr->Inven[itemmove->iToSlotNum];
plr.plr->Inven[itemmove->iToSlotNum] = fromItem;
}
if (itemmove->eFrom == 0) {
plr.plr->Equip[itemmove->iFromSlotNum] = toItem;
} else {
plr.plr->Inven[itemmove->iFromSlotNum] = toItem;
}
if (itemmove->eFrom == 0 || itemmove->eTo == 0) {
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, equipChange);
equipChange.iPC_ID = plr.plr->iID;
if (itemmove->eFrom == 0) {
equipChange.iEquipSlotNum = itemmove->iFromSlotNum;
equipChange.EquipSlotItem = toItem;
} else {
equipChange.iEquipSlotNum = itemmove->iToSlotNum;
equipChange.EquipSlotItem = fromItem;
}
// unequip vehicle if equip slot 8 is 0
if (plr.plr->Equip[8].iID == 0)
plr.plr->iPCState = 0;
// send equip event to other players
for (CNSocket* otherSock : plr.viewable) {
otherSock->sendPacket((void*)&equipChange, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
}
}
resp.eTo = itemmove->eFrom;
resp.iToSlotNum = itemmove->iFromSlotNum;
resp.ToSlotItem = toItem;
resp.eFrom = itemmove->eTo;
resp.iFromSlotNum = itemmove->iToSlotNum;
resp.FromSlotItem = fromItem;
sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC));
}
void ItemManager::itemDeleteHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_DELETE))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_ITEM_DELETE* itemdel = (sP_CL2FE_REQ_PC_ITEM_DELETE*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC, resp);
PlayerView& plr = PlayerManager::players[sock];
resp.eIL = itemdel->eIL;
resp.iSlotNum = itemdel->iSlotNum;
// so, im not sure what this eIL thing does since you always delete items in inventory and not equips
plr.plr->Inven[itemdel->iSlotNum].iID = 0;
plr.plr->Inven[itemdel->iSlotNum].iType = 0;
plr.plr->Inven[itemdel->iSlotNum].iOpt = 0;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_ITEM_DELETE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC));
}
void ItemManager::itemGMGiveHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_ITEM))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_GIVE_ITEM* itemreq = (sP_CL2FE_REQ_PC_GIVE_ITEM*)data->buf;
PlayerView& plr = PlayerManager::players[sock];
// Commented and disabled for future use
//if (!plr.plr->IsGM) {
// TODO: send fail packet
// return;
//}
if (itemreq->eIL == 2) {
// Quest item, not a real item, handle this later, stubbed for now
// sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_GIVE_ITEM_FAIL, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_FAIL), sock->getFEKey()));
} else if (itemreq->eIL == 1 && itemreq->Item.iType >= 0 && itemreq->Item.iType <= 10) {
INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp);
resp.eIL = itemreq->eIL;
resp.iSlotNum = itemreq->iSlotNum;
resp.Item = itemreq->Item;
plr.plr->Inven[itemreq->iSlotNum] = itemreq->Item;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC));
}
}
void ItemManager::itemTradeOfferHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_OFFER* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER*)data->buf;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
PlayerView& plr = PlayerManager::players[otherSock];
if (plr.plr->isTrading) {
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
resp.iID_Request = pacdat->iID_To;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
return; //prevent trading with a player already trading
}
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER));
}
void ItemManager::itemTradeOfferAcceptHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT*)data->buf;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
// Clearing up trade slots
PlayerView& plr = PlayerManager::players[sock];
PlayerView& plr2 = PlayerManager::players[otherSock];
if (plr2.plr->isTrading) {
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
resp.iID_Request = pacdat->iID_To;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
return; //prevent trading with a player already trading
}
plr.plr->isTrading = true;
plr2.plr->isTrading = true;
plr.plr->isTradeConfirm = false;
plr2.plr->isTradeConfirm = false;
for (int i = 0; i < 5; i++) {
plr.plr->Trade[i].iID = 0;
plr.plr->Trade[i].iType = 0;
plr.plr->Trade[i].iOpt = 0;
plr.plr->Trade[i].iInvenNum = 0;
plr.plr->Trade[i].iSlotNum = 0;
}
for (int i = 0; i < 5; i++) {
plr2.plr->Trade[i].iID = 0;
plr2.plr->Trade[i].iType = 0;
plr2.plr->Trade[i].iOpt = 0;
plr2.plr->Trade[i].iInvenNum = 0;
plr2.plr->Trade[i].iSlotNum = 0;
}
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC));
}
void ItemManager::itemTradeOfferRefusalHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL));
}
void ItemManager::itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_CONFIRM* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM*)data->buf;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
PlayerView& plr = PlayerManager::players[sock];
PlayerView& plr2 = PlayerManager::players[otherSock];
if (plr2.plr->isTradeConfirm) {
plr.plr->isTrading = false;
plr2.plr->isTrading = false;
plr.plr->isTradeConfirm = false;
plr2.plr->isTradeConfirm = false;
// Check if we have enough free slots
int freeSlots = 0;
int freeSlotsNeeded = 0;
int freeSlots2 = 0;
int freeSlotsNeeded2 = 0;
for (int i = 0; i < AINVEN_COUNT; i++) {
if (plr.plr->Inven[i].iID == 0)
freeSlots++;
}
for (int i = 0; i < 5; i++) {
if (plr.plr->Trade[i].iID != 0)
freeSlotsNeeded++;
}
for (int i = 0; i < AINVEN_COUNT; i++) {
if (plr2.plr->Inven[i].iID == 0)
freeSlots2++;
}
for (int i = 0; i < 5; i++) {
if (plr2.plr->Trade[i].iID != 0)
freeSlotsNeeded2++;
}
if (freeSlotsNeeded2 - freeSlotsNeeded > freeSlots) {
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT));
return; // Fail trade because of the lack of slots
}
if (freeSlotsNeeded - freeSlotsNeeded2 > freeSlots2) {
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
return; // Fail trade because of the lack of slots
}
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
// ^^ this is a must have or else the player won't accept a succ packet for some reason
for (int i = 0; i < freeSlotsNeeded; i++) {
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iID = 0;
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iType = 0;
plr.plr->Inven[plr.plr->Trade[i].iInvenNum].iOpt = 0;
}
for (int i = 0; i < freeSlotsNeeded2; i++) {
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iID = 0;
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iType = 0;
plr2.plr->Inven[plr2.plr->Trade[i].iInvenNum].iOpt = 0;
}
for (int i = 0; i < AINVEN_COUNT; i++) {
if (freeSlotsNeeded <= 0)
break;
if (plr2.plr->Inven[i].iID == 0) {
plr2.plr->Inven[i].iID = plr.plr->Trade[freeSlotsNeeded - 1].iID;
plr2.plr->Inven[i].iType = plr.plr->Trade[freeSlotsNeeded - 1].iType;
plr2.plr->Inven[i].iOpt = plr.plr->Trade[freeSlotsNeeded - 1].iOpt;
plr.plr->Trade[freeSlotsNeeded - 1].iInvenNum = i;
freeSlotsNeeded--;
}
}
for (int i = 0; i < AINVEN_COUNT; i++) {
if (freeSlotsNeeded2 <= 0)
break;
if (plr.plr->Inven[i].iID == 0) {
plr.plr->Inven[i].iID = plr2.plr->Trade[freeSlotsNeeded2 - 1].iID;
plr.plr->Inven[i].iType = plr2.plr->Trade[freeSlotsNeeded2 - 1].iType;
plr.plr->Inven[i].iOpt = plr2.plr->Trade[freeSlotsNeeded2 - 1].iOpt;
plr2.plr->Trade[freeSlotsNeeded2 - 1].iInvenNum = i;
freeSlotsNeeded2--;
}
}
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, resp2);
resp2.iID_Request = pacdat->iID_Request;
resp2.iID_From = pacdat->iID_From;
resp2.iID_To = pacdat->iID_To;
plr.plr->money = plr.plr->money + plr2.plr->moneyInTrade - plr.plr->moneyInTrade;
resp2.iCandy = plr.plr->money;
memcpy(resp2.Item, plr2.plr->Trade, sizeof(plr2.plr->Trade));
memcpy(resp2.ItemStay, plr.plr->Trade, sizeof(plr.plr->Trade));
sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC));
plr2.plr->money = plr2.plr->money + plr.plr->moneyInTrade - plr2.plr->moneyInTrade;
resp2.iCandy = plr2.plr->money;
memcpy(resp2.Item, plr.plr->Trade, sizeof(plr.plr->Trade));
memcpy(resp2.ItemStay, plr2.plr->Trade, sizeof(plr2.plr->Trade));
otherSock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC));
} else {
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
plr.plr->isTradeConfirm = true;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM));
}
}
void ItemManager::itemTradeConfirmCancelHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
PlayerView& plr = PlayerManager::players[sock];
PlayerView& plr2 = PlayerManager::players[otherSock];
plr.plr->isTrading = false;
plr.plr->isTradeConfirm = false;
plr2.plr->isTrading = false;
plr2.plr->isTradeConfirm = false;
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL));
}
void ItemManager::itemTradeRegisterItemHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER*)data->buf;
if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4)
return; // sanity check
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
resp.TradeItem = pacdat->Item;
resp.InvenItem = pacdat->Item;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
PlayerView& plr = PlayerManager::players[sock];
plr.plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item;
plr.plr->isTradeConfirm = false;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC));
}
void ItemManager::itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
resp.TradeItem = pacdat->Item;
PlayerView& plr = PlayerManager::players[sock];
resp.InvenItem = plr.plr->Trade[pacdat->Item.iSlotNum];
plr.plr->isTradeConfirm = false;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
int temp_num = pacdat->Item.iSlotNum;
if (temp_num >= 0 && temp_num <= 4) {
plr.plr->Trade[temp_num].iID = 0;
plr.plr->Trade[temp_num].iType = 0;
plr.plr->Trade[temp_num].iOpt = 0;
plr.plr->Trade[temp_num].iInvenNum = 0;
plr.plr->Trade[temp_num].iSlotNum = 0;
}
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC));
}
void ItemManager::itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER*)data->buf;
PlayerView& plr = PlayerManager::players[sock];
if (pacdat->iCandy < 0 || pacdat->iCandy > plr.plr->money)
return; // famous glitch, begone
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
resp.iCandy = pacdat->iCandy;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
plr.plr->moneyInTrade = pacdat->iCandy;
plr.plr->isTradeConfirm = false;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC));
}
void ItemManager::itemTradeChatHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT))
return; // malformed packet
sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT* pacdat = (sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp);
resp.iID_Request = pacdat->iID_Request;
resp.iID_From = pacdat->iID_From;
resp.iID_To = pacdat->iID_To;
memcpy(resp.szFreeChat, pacdat->szFreeChat, sizeof(pacdat->szFreeChat));
resp.iEmoteCode = pacdat->iEmoteCode;
int iID_Check;
if (pacdat->iID_Request == pacdat->iID_From) {
iID_Check = pacdat->iID_To;
} else {
iID_Check = pacdat->iID_From;
}
CNSocket* otherSock = sock;
for (auto pair : PlayerManager::players) {
if (pair.second.plr->iID == iID_Check) {
otherSock = pair.first;
}
}
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
}
void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN))
return; // ignore the malformed packet
sP_CL2FE_REQ_ITEM_CHEST_OPEN *pkt = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// item giving packet
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
assert(resplen < CN_PACKET_BUFFER_SIZE);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[resplen]; // not a variable length array, don't worry
sP_FE2CL_REP_REWARD_ITEM *reward = (sP_FE2CL_REP_REWARD_ITEM *)respbuf;
sItemReward *item = (sItemReward *)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
// don't forget to zero the buffer!
memset(respbuf, 0, resplen);
// simple rewards
reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this
// item reward
item->sItem.iType = 0;
item->sItem.iID = 96;
item->iSlotNum = pkt->iSlotNum;
item->eIL = pkt->eIL;
// update player
plr->Inven[pkt->iSlotNum] = item->sItem;
// transmit item
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
// chest opening acknowledgement packet
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
resp.iSlotNum = pkt->iSlotNum;
std::cout << "opening chest..." << std::endl;
sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC));
}
// TODO: use this in cleaned up ItemManager
int ItemManager::findFreeSlot(Player *plr) {
int i;
sItemBase free;
memset((void*)&free, 0, sizeof(sItemBase));
for (i = 0; i < AINVEN_COUNT; i++)
if (memcmp((void*)&plr->Inven[i], (void*)&free, sizeof(sItemBase)) == 0)
return i;
// not found
return -1;
}

25
src/ItemManager.hpp Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "CNShardServer.hpp"
#include "Player.hpp"
namespace ItemManager {
void init();
void itemMoveHandler(CNSocket* sock, CNPacketData* data);
void itemDeleteHandler(CNSocket* sock, CNPacketData* data);
void itemGMGiveHandler(CNSocket* sock, CNPacketData* data);
void itemTradeOfferHandler(CNSocket* sock, CNPacketData* data);
//void itemTradeOfferCancel(CNSocket* sock, CNPacketData* data);
void itemTradeOfferAcceptHandler(CNSocket* sock, CNPacketData* data);
void itemTradeOfferRefusalHandler(CNSocket* sock, CNPacketData* data);
void itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data);
void itemTradeConfirmCancelHandler(CNSocket* sock, CNPacketData* data);
void itemTradeRegisterItemHandler(CNSocket* sock, CNPacketData* data);
void itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data);
void itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data);
void itemTradeChatHandler(CNSocket* sock, CNPacketData* data);
void chestOpenHandler(CNSocket* sock, CNPacketData* data);
int findFreeSlot(Player *plr);
}

55
src/MissionManager.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include "CNShardServer.hpp"
#include "CNStructs.hpp"
#include "MissionManager.hpp"
#include "PlayerManager.hpp"
void MissionManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_START, acceptMission);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_END, completeMission);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID, setMission);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TASK_STOP, quitMission);
}
void MissionManager::acceptMission(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_START))
return; // malformed packet
sP_CL2FE_REQ_PC_TASK_START* missionData = (sP_CL2FE_REQ_PC_TASK_START*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TASK_START_SUCC, response);
response.iTaskNum = missionData->iTaskNum;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC));
}
void MissionManager::completeMission(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_END))
return; // malformed packet
sP_CL2FE_REQ_PC_TASK_END* missionData = (sP_CL2FE_REQ_PC_TASK_END*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response);
response.iTaskNum = missionData->iTaskNum;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC));
}
void MissionManager::setMission(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID))
return; // malformed packet
sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID* missionData = (sP_CL2FE_REQ_PC_SET_CURRENT_MISSION_ID*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, response);
response.iCurrentMissionID = missionData->iCurrentMissionID;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SET_CURRENT_MISSION_ID, sizeof(sP_FE2CL_REP_PC_SET_CURRENT_MISSION_ID));
}
void MissionManager::quitMission(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_TASK_STOP))
return; // malformed packet
sP_CL2FE_REQ_PC_TASK_STOP* missionData = (sP_CL2FE_REQ_PC_TASK_STOP*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_TASK_STOP_SUCC, response);
response.iTaskNum = missionData->iTaskNum;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_STOP_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_STOP_SUCC));
}

12
src/MissionManager.hpp Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include "CNShardServer.hpp"
namespace MissionManager {
void init();
void acceptMission(CNSocket* sock, CNPacketData* data);
void completeMission(CNSocket* sock, CNPacketData* data);
void setMission(CNSocket* sock, CNPacketData* data);
void quitMission(CNSocket* sock, CNPacketData* data);
}

37
src/NPC.hpp Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include "CNStructs.hpp"
class BaseNPC {
public:
sNPCAppearanceData appearanceData;
BaseNPC() {};
BaseNPC(int x, int y, int z, int type) {
appearanceData.iX = x;
appearanceData.iY = y;
appearanceData.iZ = z;
appearanceData.iNPCType = type;
appearanceData.iHP = 400;
appearanceData.iAngle = 0;
appearanceData.iConditionBitFlag = 0;
appearanceData.iBarkerType = 0;
// hopefully no collisions happen :eyes:
appearanceData.iNPC_ID = (int32_t)rand();
};
BaseNPC(int x, int y, int z, int type, int hp, int cond, int angle, int barker) {
appearanceData.iX = x;
appearanceData.iY = y;
appearanceData.iZ = z;
appearanceData.iNPCType = type;
appearanceData.iHP = hp;
appearanceData.iAngle = angle;
appearanceData.iConditionBitFlag = cond;
appearanceData.iBarkerType = barker;
// hopefully no collisions happen :eyes:
appearanceData.iNPC_ID = (int32_t)rand();
}
};

193
src/NPCManager.cpp Normal file
View File

@@ -0,0 +1,193 @@
#include "NPCManager.hpp"
#include "settings.hpp"
#include <cmath>
#include <algorithm>
#include <list>
#include <fstream>
#include <vector>
#include "contrib/JSON.hpp"
std::map<int32_t, BaseNPC> NPCManager::NPCs;
std::map<int32_t, WarpLocation> NPCManager::Warps;
std::vector<WarpLocation> NPCManager::RespawnPoints;
void NPCManager::init() {
// load NPCs from NPCs.json into our NPC manager
// Temporary fix, IDs will be pulled from json later
int i = 0;
try {
std::ifstream inFile(settings::NPCJSON);
nlohmann::json npcData;
// read file into json
inFile >> npcData;
for (nlohmann::json::iterator npc = npcData.begin(); npc != npcData.end(); npc++) {
BaseNPC tmp(npc.value()["x"], npc.value()["y"], npc.value()["z"], npc.value()["id"]);
// Temporary fix, IDs will be pulled from json later
tmp.appearanceData.iNPC_ID = i;
i++;
NPCs[tmp.appearanceData.iNPC_ID] = tmp;
if (npc.value()["id"] == 641 || npc.value()["id"] == 642)
RespawnPoints.push_back({ npc.value()["x"], npc.value()["y"], ((int)npc.value()["z"]) + RESURRECT_HEIGHT });
}
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed NPCs.json file! Reason:" << err.what() << std::endl;
}
// load temporary mob dump
try {
std::ifstream inFile(settings::MOBJSON); // not in settings, since it's temp
nlohmann::json npcData;
// read file into json
inFile >> npcData;
for (nlohmann::json::iterator npc = npcData.begin(); npc != npcData.end(); npc++) {
BaseNPC tmp(npc.value()["iX"], npc.value()["iY"], npc.value()["iZ"], npc.value()["iNPCType"],
npc.value()["iHP"], npc.value()["iConditionBitFlag"], npc.value()["iAngle"], npc.value()["iBarkerType"]);
// Temporary fix, IDs will be pulled from json later
tmp.appearanceData.iNPC_ID = i;
i++;
NPCs[tmp.appearanceData.iNPC_ID] = tmp;
}
std::cout << "[INFO] populated " << NPCs.size() << " NPCs" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed mobs.json file! Reason:" << err.what() << std::endl;
}
try {
std::ifstream infile(settings::WARPJSON);
nlohmann::json warpData;
// read file into json
infile >> warpData;
for (nlohmann::json::iterator warp = warpData.begin(); warp != warpData.end(); warp++) {
WarpLocation warpLoc = { warp.value()["m_iToX"], warp.value()["m_iToY"], warp.value()["m_iToZ"] };
int warpID = atoi(warp.key().c_str());
Warps[warpID] = warpLoc;
}
std::cout << "[INFO] populated " << Warps.size() << " Warps" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed warps.json file! Reason:" << err.what() << std::endl;
}
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler);
}
void NPCManager::updatePlayerNPCS(CNSocket* sock, PlayerView& view) {
std::list<int32_t> yesView;
std::list<int32_t> noView;
for (auto& pair : NPCs) {
int diffX = abs(view.plr->x - pair.second.appearanceData.iX);
int diffY = abs(view.plr->y - pair.second.appearanceData.iY);
if (diffX < settings::NPCDISTANCE && diffY < settings::NPCDISTANCE) {
yesView.push_back(pair.first);
}
else {
noView.push_back(pair.first);
}
}
INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData);
std::list<int32_t>::iterator i = view.viewableNPCs.begin();
while (i != view.viewableNPCs.end()) {
int32_t id = *i;
if (std::find(noView.begin(), noView.end(), id) != noView.end()) {
// it shouldn't be visible, send NPC_EXIT
exitData.iNPC_ID = id;
sock->sendPacket((void*)&exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT));
// remove from view
view.viewableNPCs.erase(i++);
}
else {
i++;
}
}
INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData);
for (int32_t id : yesView) {
if (std::find(view.viewableNPCs.begin(), view.viewableNPCs.end(), id) == view.viewableNPCs.end()) {
// needs to be added to viewableNPCs! send NPC_ENTER
enterData.NPCAppearanceData = NPCs[id].appearanceData;
sock->sendPacket((void*)&enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER));
// add to viewable
view.viewableNPCs.push_back(id);
}
}
PlayerManager::players[sock].viewableNPCs = view.viewableNPCs;
}
void NPCManager::npcBarkHandler(CNSocket* sock, CNPacketData* data) {} // stubbed for now
void NPCManager::npcSummonHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NPC_SUMMON))
return; // malformed packet
sP_CL2FE_REQ_NPC_SUMMON* req = (sP_CL2FE_REQ_NPC_SUMMON*)data->buf;
INITSTRUCT(sP_FE2CL_NPC_ENTER, resp);
Player* plr = PlayerManager::getPlayer(sock);
// permission & sanity check
if (!plr->IsGM || req->iNPCType >= 3314)
return;
resp.NPCAppearanceData.iNPC_ID = rand(); // cpunch-style
resp.NPCAppearanceData.iNPCType = req->iNPCType;
resp.NPCAppearanceData.iHP = 1000; // TODO: placeholder
resp.NPCAppearanceData.iX = plr->x;
resp.NPCAppearanceData.iY = plr->y;
resp.NPCAppearanceData.iZ = plr->z;
sock->sendPacket((void*)&resp, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER));
}
void NPCManager::npcWarpHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_WARP_USE_NPC))
return; // malformed packet
sP_CL2FE_REQ_PC_WARP_USE_NPC* warpNpc = (sP_CL2FE_REQ_PC_WARP_USE_NPC*)data->buf;
PlayerView& plrv = PlayerManager::players[sock];
// sanity check
if (Warps.find(warpNpc->iWarpID) == Warps.end())
return;
// send to client
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp);
resp.iX = Warps[warpNpc->iWarpID].x;
resp.iY = Warps[warpNpc->iWarpID].y;
resp.iZ = Warps[warpNpc->iWarpID].z;
// force player & NPC reload
plrv.viewable.clear();
plrv.viewableNPCs.clear();
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_NPC_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC));
}

28
src/NPCManager.hpp Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include "CNProtocol.hpp"
#include "PlayerManager.hpp"
#include "NPC.hpp"
#include <map>
#include <vector>
#define RESURRECT_HEIGHT 400
// this should really be called vec3 or something...
struct WarpLocation {
int x, y, z;
};
namespace NPCManager {
extern std::map<int32_t, BaseNPC> NPCs;
extern std::map<int32_t, WarpLocation> Warps;
extern std::vector<WarpLocation> RespawnPoints;
void init();
void npcBarkHandler(CNSocket* sock, CNPacketData* data);
void npcSummonHandler(CNSocket* sock, CNPacketData* data);
void npcWarpHandler(CNSocket* sock, CNPacketData* data);
void updatePlayerNPCS(CNSocket* sock, PlayerView& plr);
}

210
src/NanoManager.cpp Normal file
View File

@@ -0,0 +1,210 @@
#include "CNShardServer.hpp"
#include "CNStructs.hpp"
#include "NanoManager.hpp"
#include "PlayerManager.hpp"
void NanoManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_EQUIP, nanoEquipHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_UNEQUIP, nanoUnEquipHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GIVE_NANO, nanoGMGiveHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_TUNE, nanoSkillSetHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_SKILL_USE, nanoSkillUseHandler);
}
void NanoManager::nanoEquipHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NANO_EQUIP))
return; // malformed packet
sP_CL2FE_REQ_NANO_EQUIP* nano = (sP_CL2FE_REQ_NANO_EQUIP*)data->buf;
INITSTRUCT(sP_FE2CL_REP_NANO_EQUIP_SUCC, resp);
Player *plr = PlayerManager::getPlayer(sock);
// sanity check
if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
return;
resp.iNanoID = nano->iNanoID;
resp.iNanoSlotNum = nano->iNanoSlotNum;
// Update player
plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID;
// unsummon nano if replaced
if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum])
summonNano(sock, -1);
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_EQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_EQUIP_SUCC));
}
void NanoManager::nanoUnEquipHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NANO_UNEQUIP))
return; // malformed packet
sP_CL2FE_REQ_NANO_UNEQUIP* nano = (sP_CL2FE_REQ_NANO_UNEQUIP*)data->buf;
INITSTRUCT(sP_FE2CL_REP_NANO_UNEQUIP_SUCC, resp);
Player *plr = PlayerManager::getPlayer(sock);
// sanity check
if (nano->iNanoSlotNum > 2 || nano->iNanoSlotNum < 0)
return;
resp.iNanoSlotNum = nano->iNanoSlotNum;
// unsummon nano if removed
if (plr->equippedNanos[nano->iNanoSlotNum] == plr->activeNano)
summonNano(sock, -1);
// update player
plr->equippedNanos[nano->iNanoSlotNum] = 0;
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_UNEQUIP_SUCC, sizeof(sP_FE2CL_REP_NANO_UNEQUIP_SUCC));
}
void NanoManager::nanoGMGiveHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_NANO))
return; // ignore the malformed packet
// Cmd: /nano <nanoId>
sP_CL2FE_REQ_PC_GIVE_NANO* nano = (sP_CL2FE_REQ_PC_GIVE_NANO*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// Add nano to player
addNano(sock, nano->iNanoID, 0);
DEBUGLOG(
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " requested to add nano id: " << nano->iNanoID << std::endl;
)
}
void NanoManager::nanoSummonHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NANO_ACTIVE))
return; // malformed packet
sP_CL2FE_REQ_NANO_ACTIVE* pkt = (sP_CL2FE_REQ_NANO_ACTIVE*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
summonNano(sock, pkt->iNanoSlotNum);
// Send to client
DEBUGLOG(
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " requested to summon nano slot: " << pkt->iNanoSlotNum << std::endl;
)
}
void NanoManager::nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NANO_SKILL_USE))
return; // malformed packet
sP_CL2FE_REQ_NANO_SKILL_USE* skill = (sP_CL2FE_REQ_NANO_SKILL_USE*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// Send to client
INITSTRUCT(sP_FE2CL_NANO_SKILL_USE_SUCC, resp);
resp.iArg1 = skill->iArg1;
resp.iArg2 = skill->iArg2;
resp.iArg3 = skill->iArg3;
resp.iBulletID = skill->iBulletID;
resp.iTargetCnt = skill->iTargetCnt;
resp.iPC_ID = plr->iID;
resp.iNanoStamina = 150; // Hardcoded for now
sock->sendPacket((void*)&resp, P_FE2CL_NANO_SKILL_USE_SUCC, sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC));
DEBUGLOG(
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " requested to summon nano skill " << std::endl;
)
}
void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_NANO_TUNE))
return; // malformed packet
sP_CL2FE_REQ_NANO_TUNE* skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
setNanoSkill(sock, skill->iNanoID, skill->iTuneID);
}
#pragma region Helper methods
void NanoManager::addNano(CNSocket* sock, int16_t nanoId, int16_t slot) {
if (nanoId > 36)
return;
Player *plr = PlayerManager::getPlayer(sock);
// Send to client
INITSTRUCT(sP_FE2CL_REP_PC_NANO_CREATE_SUCC, resp);
resp.Nano.iID = nanoId;
resp.Nano.iStamina = 150;
resp.iQuestItemSlotNum = slot;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_NANO_CREATE_SUCC, sizeof(sP_FE2CL_REP_PC_NANO_CREATE_SUCC));
// Update player
plr->Nanos[nanoId] = resp.Nano;
}
void NanoManager::summonNano(CNSocket *sock, int slot) {
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
resp.iActiveNanoSlotNum = slot;
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_ACTIVE_SUCC, sizeof(sP_FE2CL_REP_NANO_ACTIVE_SUCC));
Player *plr = PlayerManager::getPlayer(sock);
std::cout << "summon nano\n";
if (slot > 2 || slot < 0)
return; //sanity check
int nanoId = plr->equippedNanos[slot];
if (nanoId > 36 || nanoId < 0)
return; // sanity check
sNano nano = plr->Nanos[nanoId];
// Send to other players
INITSTRUCT(sP_FE2CL_NANO_ACTIVE, pkt1);
pkt1.iPC_ID = plr->iID;
pkt1.Nano = nano;
for (CNSocket* s : PlayerManager::players[sock].viewable)
s->sendPacket((void*)&pkt1, P_FE2CL_NANO_ACTIVE, sizeof(sP_FE2CL_NANO_ACTIVE));
// update player
plr->activeNano = nanoId;
}
void NanoManager::setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId) {
if (nanoId > 36)
return;
Player *plr = PlayerManager::getPlayer(sock);
sNano nano = plr->Nanos[nanoId];
nano.iSkillID = skillId;
plr->Nanos[nanoId] = nano;
// Send to client
INITSTRUCT(sP_FE2CL_REP_NANO_TUNE_SUCC, resp);
resp.iNanoID = nanoId;
resp.iSkillID = skillId;
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
DEBUGLOG(
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " set skill id " << skillId << " for nano: " << nanoId << std::endl;
)
}
void NanoManager::resetNanoSkill(CNSocket* sock, int16_t nanoId) {
if (nanoId > 36)
return;
Player *plr = PlayerManager::getPlayer(sock);
sNano nano = plr->Nanos[nanoId];
// 0 is reset
nano.iSkillID = 0;
plr->Nanos[nanoId] = nano;
}
#pragma endregion

19
src/NanoManager.hpp Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "CNShardServer.hpp"
namespace NanoManager {
void init();
void nanoSummonHandler(CNSocket* sock, CNPacketData* data);
void nanoEquipHandler(CNSocket* sock, CNPacketData* data);
void nanoUnEquipHandler(CNSocket* sock, CNPacketData* data);
void nanoGMGiveHandler(CNSocket* sock, CNPacketData* data);
void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data);
void nanoSkillSetHandler(CNSocket* sock, CNPacketData* data);
// Helper methods
void addNano(CNSocket* sock, int16_t nanoId, int16_t slot);
void summonNano(CNSocket* sock, int slot);
void setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId);
void resetNanoSkill(CNSocket* sock, int16_t nanoId);
}

View File

@@ -1,25 +1,35 @@
#pragma once
#include <string>
#include <cstring>
#ifndef _PLR_HPP
#define _PLR_HPP
#include "CNProtocol.hpp"
#include "CNStructs.hpp"
struct Player {
int accountId;
int64_t SerialKey;
int32_t iID;
uint64_t FEKey;
int level;
int HP;
int slot;
int slot; // player slot, not nano slot
int32_t money;
int32_t fusionmatter;
sPCStyle PCStyle;
sPCStyle2 PCStyle2;
sNano Nanos[37]; // acquired nanos
int equippedNanos[3];
int activeNano; // active nano (index into Nanos)
int8_t iPCState;
int x, y, z, angle;
sItemBase Equip[AEQUIP_COUNT];
sItemBase Inven[AINVEN_COUNT];
sItemTrade Trade[12];
int32_t moneyInTrade;
bool isTrading;
bool isTradeConfirm;
bool IsGM;
};
#endif

View File

@@ -1,5 +1,6 @@
#include "CNProtocol.hpp"
#include "PlayerManager.hpp"
#include "NPCManager.hpp"
#include "CNShardServer.hpp"
#include "CNShared.hpp"
@@ -18,15 +19,34 @@ void PlayerManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_MOVE, PlayerManager::movePlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_STOP, PlayerManager::stopPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_JUMP, PlayerManager::jumpPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_JUMPPAD, PlayerManager::jumppadPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_LAUNCHER, PlayerManager::launchPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ZIPLINE, PlayerManager::ziplinePlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_MOVEPLATFORM, PlayerManager::movePlatformPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SLOPE, PlayerManager::moveSlopePlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GOTO, PlayerManager::gotoPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_SET_VALUE, PlayerManager::setSpecialPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REP_LIVE_CHECK, PlayerManager::heartbeatPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_REGEN, PlayerManager::revivePlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EXIT, PlayerManager::exitGame);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, PlayerManager::setSpecialSwitchPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, PlayerManager::enterPlayerVehicle);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, PlayerManager::exitPlayerVehicle);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, PlayerManager::changePlayerGuide);
}
void PlayerManager::addPlayer(CNSocket* key, Player plr) {
Player *p = new Player();
memcpy(p, &plr, sizeof(Player));
players[key] = PlayerView();
players[key].viewable = std::list<CNSocket*>();
players[key].plr = plr;
players[key].plr = p;
players[key].lastHeartbeat = 0;
std::cout << U16toU8(plr.PCStyle.szFirstName) << " " << U16toU8(plr.PCStyle.szLastName) << " has joined!" << std::endl;
std::cout << players.size() << " players" << std::endl;
}
void PlayerManager::removePlayer(CNSocket* key) {
@@ -36,24 +56,24 @@ void PlayerManager::removePlayer(CNSocket* key) {
for (CNSocket* otherSock : players[key].viewable) {
players[otherSock].viewable.remove(key); // gone
// now sent PC_EXIT packet
sP_FE2CL_PC_EXIT* exitPacket = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
exitPacket->iID = players[key].plr.iID;
// now send PC_EXIT packet
sP_FE2CL_PC_EXIT exitPacket;
exitPacket.iID = players[key].plr->iID;
otherSock->sendPacket(new CNPacketData((void*)exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), otherSock->getFEKey()));
otherSock->sendPacket((void*)&exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT));
}
std::cout << U16toU8(cachedView.plr->PCStyle.szFirstName) << " " << U16toU8(cachedView.plr->PCStyle.szLastName) << " has left!" << std::endl;
std::cout << players.size() << " players" << std::endl;
delete cachedView.plr;
players.erase(key);
}
Player PlayerManager::getPlayer(CNSocket* key) {
return players[key].plr;
}
void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z) {
players[sock].plr.x = X;
players[sock].plr.y = Y;
players[sock].plr.z = Z;
players[sock].plr->x = X;
players[sock].plr->y = Y;
players[sock].plr->z = Z;
std::vector<CNSocket*> noView;
std::vector<CNSocket*> yesView;
@@ -63,82 +83,103 @@ void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z) {
if (pair.first == sock)
continue; // ignore our own connection
int diffX = abs(pair.second.plr.x - X); // the map is like a grid, X and Y are your position on the map, Z is the height. very different from other games...
int diffY = abs(pair.second.plr.y - Y);
int diffX = abs(pair.second.plr->x - X); // the map is like a grid, X and Y are your position on the map, Z is the height. very different from other games...
int diffY = abs(pair.second.plr->y - Y);
double dist = sqrt(pow(diffX, 2) + pow(diffY, 2));
if (dist > settings::VIEWDISTANCE) {
noView.push_back(pair.first);
} else {
if (diffX < settings::PLAYERDISTANCE && diffY < settings::PLAYERDISTANCE) {
yesView.push_back(pair.first);
}
}
std::list<CNSocket*> cachedview(players[sock].viewable); // copies the viewable
for (CNSocket* otherSock : cachedview) {
if (std::find(noView.begin(), noView.end(), otherSock) != noView.end()) {
// sock shouldn't be visible, send PC_EXIT packet & remove them
sP_FE2CL_PC_EXIT* exitPacket = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
sP_FE2CL_PC_EXIT* exitPacketOther = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
exitPacket->iID = players[sock].plr.iID;
exitPacketOther->iID = players[otherSock].plr.iID;
otherSock->sendPacket(new CNPacketData((void*)exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), otherSock->getFEKey()));
sock->sendPacket(new CNPacketData((void*)exitPacketOther, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), sock->getFEKey()));
players[sock].viewable.remove(otherSock);
players[otherSock].viewable.remove(sock);
else {
noView.push_back(pair.first);
}
}
cachedview = players[sock].viewable;
INITSTRUCT(sP_FE2CL_PC_EXIT, exitPacket);
std::list<CNSocket*>::iterator i = players[sock].viewable.begin();
while (i != players[sock].viewable.end()) {
CNSocket* otherSock = *i;
if (std::find(noView.begin(), noView.end(), otherSock) != noView.end()) {
// sock shouldn't be visible, send PC_EXIT packet
exitPacket.iID = players[sock].plr->iID;
otherSock->sendPacket((void*)&exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT));
exitPacket.iID = players[otherSock].plr->iID;
sock->sendPacket((void*)&exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT));
// remove them from the viewable list
players[sock].viewable.erase(i++);
players[otherSock].viewable.remove(sock);
continue;
}
++i;
}
INITSTRUCT(sP_FE2CL_PC_NEW, newPlayer);
for (CNSocket* otherSock : yesView) {
if (std::find(cachedview.begin(), cachedview.end(), otherSock) == cachedview.end()) {
if (std::find(players[sock].viewable.begin(), players[sock].viewable.end(), otherSock) == players[sock].viewable.end()) {
// this needs to be added to the viewable players, send PC_ENTER
sP_FE2CL_PC_NEW* newPlayer = (sP_FE2CL_PC_NEW*)xmalloc(sizeof(sP_FE2CL_PC_NEW)); // current connection to other player
sP_FE2CL_PC_NEW* newOtherPlayer = (sP_FE2CL_PC_NEW*)xmalloc(sizeof(sP_FE2CL_PC_NEW)); // other player to current connection
Player *otherPlr = players[otherSock].plr;
Player *plr = players[sock].plr;
Player otherPlr = players[otherSock].plr;
Player plr = players[sock].plr;
newPlayer.PCAppearanceData.iID = plr->iID;
newPlayer.PCAppearanceData.iHP = plr->HP;
newPlayer.PCAppearanceData.iLv = plr->level;
newPlayer.PCAppearanceData.iX = plr->x;
newPlayer.PCAppearanceData.iY = plr->y;
newPlayer.PCAppearanceData.iZ = plr->z;
newPlayer.PCAppearanceData.iAngle = plr->angle;
newPlayer.PCAppearanceData.PCStyle = plr->PCStyle;
newPlayer.PCAppearanceData.Nano = plr->Nanos[plr->activeNano];
newPlayer.PCAppearanceData.iPCState = plr->iPCState;
memcpy(newPlayer.PCAppearanceData.ItemEquip, plr->Equip, sizeof(sItemBase) * AEQUIP_COUNT);
newPlayer->PCAppearanceData.iID = plr.iID;
newPlayer->PCAppearanceData.iHP = plr.HP;
newPlayer->PCAppearanceData.iLv = plr.level;
newPlayer->PCAppearanceData.iX = plr.x;
newPlayer->PCAppearanceData.iY = plr.y;
newPlayer->PCAppearanceData.iZ = plr.z;
newPlayer->PCAppearanceData.iAngle = plr.angle;
newPlayer->PCAppearanceData.PCStyle = plr.PCStyle;
memcpy(newPlayer->PCAppearanceData.ItemEquip, plr.Equip, sizeof(sItemBase) * AEQUIP_COUNT);
otherSock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW));
newOtherPlayer->PCAppearanceData.iID = otherPlr.iID;
newOtherPlayer->PCAppearanceData.iHP = otherPlr.HP;
newOtherPlayer->PCAppearanceData.iLv = otherPlr.level;
newOtherPlayer->PCAppearanceData.iX = otherPlr.x;
newOtherPlayer->PCAppearanceData.iY = otherPlr.y;
newOtherPlayer->PCAppearanceData.iZ = otherPlr.z;
newOtherPlayer->PCAppearanceData.iAngle = otherPlr.angle;
newOtherPlayer->PCAppearanceData.PCStyle = otherPlr.PCStyle;
memcpy(newOtherPlayer->PCAppearanceData.ItemEquip, otherPlr.Equip, sizeof(sItemBase) * AEQUIP_COUNT);
newPlayer.PCAppearanceData.iID = otherPlr->iID;
newPlayer.PCAppearanceData.iHP = otherPlr->HP;
newPlayer.PCAppearanceData.iLv = otherPlr->level;
newPlayer.PCAppearanceData.iX = otherPlr->x;
newPlayer.PCAppearanceData.iY = otherPlr->y;
newPlayer.PCAppearanceData.iZ = otherPlr->z;
newPlayer.PCAppearanceData.iAngle = otherPlr->angle;
newPlayer.PCAppearanceData.PCStyle = otherPlr->PCStyle;
newPlayer.PCAppearanceData.Nano = otherPlr->Nanos[otherPlr->activeNano];
newPlayer.PCAppearanceData.iPCState = otherPlr->iPCState;
memcpy(newPlayer.PCAppearanceData.ItemEquip, otherPlr->Equip, sizeof(sItemBase) * AEQUIP_COUNT);
sock->sendPacket(new CNPacketData((void*)newOtherPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW), sock->getFEKey()));
otherSock->sendPacket(new CNPacketData((void*)newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW), otherSock->getFEKey()));
sock->sendPacket((void*)&newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW));
players[sock].viewable.push_back(otherSock);
players[otherSock].viewable.push_back(sock);
}
}
NPCManager::updatePlayerNPCS(sock, players[sock]);
}
std::list<CNSocket*> PlayerManager::getNearbyPlayers(int x, int y, int dist) {
std::list<CNSocket*> plrs;
for (auto pair : players) {
int diffX = abs(pair.second.plr->x - x);
int diffY = abs(pair.second.plr->x - x);
if (diffX < dist && diffY < dist)
plrs.push_back(pair.first);
}
return plrs;
}
void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_ENTER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_ENTER* enter = (sP_CL2FE_REQ_PC_ENTER*)data->buf;
sP_FE2CL_REP_PC_ENTER_SUCC* response = (sP_FE2CL_REP_PC_ENTER_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_ENTER_SUCC));
INITSTRUCT(sP_FE2CL_REP_PC_ENTER_SUCC, response);
INITSTRUCT(sP_FE2CL_PC_MOTD_LOGIN, motd);
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
Player plr = CNSharedData::getPlayer(enter->iEnterSerialKey);
@@ -151,185 +192,342 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
std::cout << "\tPC_UID: " << plr.PCStyle.iPC_UID << std::endl;
)
response->iID = rand();
response->uiSvrTime = getTime();
response->PCLoadData2CL.iUserLevel = 1;
response->PCLoadData2CL.iHP = 1000 * plr.level;
response->PCLoadData2CL.iLevel = plr.level;
response->PCLoadData2CL.iMentor = 1;
response->PCLoadData2CL.iMentorCount = 4;
response->PCLoadData2CL.iMapNum = 0;
response->PCLoadData2CL.iX = plr.x;
response->PCLoadData2CL.iY = plr.y;
response->PCLoadData2CL.iZ = plr.z;
response->PCLoadData2CL.iActiveNanoSlotNum = -1;
response->PCLoadData2CL.iFatigue = 50;
response->PCLoadData2CL.PCStyle = plr.PCStyle;
response->PCLoadData2CL.PCStyle2 = plr.PCStyle2;
response.iID = rand();
response.uiSvrTime = getTime();
response.PCLoadData2CL.iUserLevel = 1;
response.PCLoadData2CL.iHP = 3625; //TODO: Check player levelupdata and get this right
response.PCLoadData2CL.iLevel = plr.level;
response.PCLoadData2CL.iCandy = plr.money;
response.PCLoadData2CL.iMentor = 1;
response.PCLoadData2CL.iMentorCount = 4;
response.PCLoadData2CL.iMapNum = 0;
response.PCLoadData2CL.iX = plr.x;
response.PCLoadData2CL.iY = plr.y;
response.PCLoadData2CL.iZ = plr.z;
response.PCLoadData2CL.iAngle = 130;
response.PCLoadData2CL.iActiveNanoSlotNum = -1;
response.PCLoadData2CL.iFatigue = 50;
response.PCLoadData2CL.PCStyle = plr.PCStyle;
response.PCLoadData2CL.PCStyle2 = plr.PCStyle2;
for (int i = 0; i < AEQUIP_COUNT; i++)
response->PCLoadData2CL.aEquip[i] = plr.Equip[i];
response.PCLoadData2CL.aEquip[i] = plr.Equip[i];
for (int i = 0; i < AINVEN_COUNT; i++)
response.PCLoadData2CL.aInven[i] = plr.Inven[i];
// don't ask..
for (int i = 1; i < 37; i++) {
response->PCLoadData2CL.aNanoBank[i].iID = i;
response->PCLoadData2CL.aNanoBank[i].iSkillID = 1;
response->PCLoadData2CL.aNanoBank[i].iStamina = 150;
response.PCLoadData2CL.aNanoBank[i].iID = i;
response.PCLoadData2CL.aNanoBank[i].iSkillID = 1;
response.PCLoadData2CL.aNanoBank[i].iStamina = 150;
}
response->PCLoadData2CL.aNanoSlots[0] = 1;
response->PCLoadData2CL.aNanoSlots[1] = 2;
response->PCLoadData2CL.aNanoSlots[2] = 3;
// temporarily not add nanos for nano add test through commands
//response.PCLoadData2CL.aNanoSlots[0] = 1;
//response.PCLoadData2CL.aNanoSlots[1] = 2;
//response.PCLoadData2CL.aNanoSlots[2] = 3;
response->PCLoadData2CL.aQuestFlag[0] = -1;
response.PCLoadData2CL.aQuestFlag[0] = -1;
plr.iID = response->iID;
// shut computress up
response.PCLoadData2CL.iFirstUseFlag1 = UINT64_MAX;
response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX;
plr.iID = response.iID;
plr.SerialKey = enter->iEnterSerialKey;
plr.HP = response->PCLoadData2CL.iHP;
plr.HP = response.PCLoadData2CL.iHP;
sock->setEKey(CNSocketEncryption::createNewKey(response->uiSvrTime, response->iID + 1, response->PCLoadData2CL.iFusionMatter + 1));
motd.iType = 1;
U8toU16(settings::MOTDSTRING, (char16_t*)motd.szSystemMsg);
sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1));
sock->setFEKey(plr.FEKey);
sock->setActiveKey(SOCKETKEY_FE); // send all packets using the FE key from now on
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_ENTER_SUCC, sizeof(sP_FE2CL_REP_PC_ENTER_SUCC), sock->getFEKey()));
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_ENTER_SUCC, sizeof(sP_FE2CL_REP_PC_ENTER_SUCC));
// transmit MOTD after entering the game, so the client hopefully changes modes on time
sock->sendPacket((void*)&motd, P_FE2CL_PC_MOTD_LOGIN, sizeof(sP_FE2CL_PC_MOTD_LOGIN));
addPlayer(sock, plr);
}
void PlayerManager::loadPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_LOADING_COMPLETE))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_LOADING_COMPLETE* complete = (sP_CL2FE_REQ_PC_LOADING_COMPLETE*)data->buf;
sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC* response = (sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC));
INITSTRUCT(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, response);
Player *plr = getPlayer(sock);
DEBUGLOG(
std::cout << "P_CL2FE_REQ_PC_LOADING_COMPLETE:" << std::endl;
std::cout << "\tPC_ID: " << complete->iPC_ID << std::endl;
std::cout << "\tPC_ID: " << complete->iPC_ID << std::endl;
)
response->iPC_ID = complete->iPC_ID;
response.iPC_ID = complete->iPC_ID;
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC), sock->getFEKey()));
// reload players & NPCs
updatePlayerPosition(sock, plr->x, plr->y, plr->z);
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC));
}
void PlayerManager::movePlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_MOVE))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_MOVE* moveData = (sP_CL2FE_REQ_PC_MOVE*)data->buf;
updatePlayerPosition(sock, moveData->iX, moveData->iY, moveData->iZ);
players[sock].plr.angle = moveData->iAngle;
players[sock].plr->angle = moveData->iAngle;
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_MOVE, moveResponse);
moveResponse.iID = players[sock].plr->iID;
moveResponse.cKeyValue = moveData->cKeyValue;
moveResponse.iX = moveData->iX;
moveResponse.iY = moveData->iY;
moveResponse.iZ = moveData->iZ;
moveResponse.iAngle = moveData->iAngle;
moveResponse.fVX = moveData->fVX;
moveResponse.fVY = moveData->fVY;
moveResponse.fVZ = moveData->fVZ;
moveResponse.iSpeed = moveData->iSpeed;
moveResponse.iCliTime = moveData->iCliTime; // maybe don't send this??? seems unneeded...
moveResponse.iSvrTime = tm;
for (CNSocket* otherSock : players[sock].viewable) {
sP_FE2CL_PC_MOVE* moveResponse = (sP_FE2CL_PC_MOVE*)xmalloc(sizeof(sP_FE2CL_PC_MOVE));
moveResponse->iID = players[sock].plr.iID;
moveResponse->cKeyValue = moveData->cKeyValue;
moveResponse->iX = moveData->iX;
moveResponse->iY = moveData->iY;
moveResponse->iZ = moveData->iZ;
moveResponse->iAngle = moveData->iAngle;
moveResponse->fVX = moveData->fVX;
moveResponse->fVY = moveData->fVY;
moveResponse->fVZ = moveData->fVZ;
moveResponse->iSpeed = moveData->iSpeed;
moveResponse->iCliTime = moveData->iCliTime; // maybe don't send this??? seems unneeded...
moveResponse->iSvrTime = tm;
otherSock->sendPacket(new CNPacketData((void*)moveResponse, P_FE2CL_PC_MOVE, sizeof(sP_FE2CL_PC_MOVE), otherSock->getFEKey()));
otherSock->sendPacket((void*)&moveResponse, P_FE2CL_PC_MOVE, sizeof(sP_FE2CL_PC_MOVE));
}
}
void PlayerManager::stopPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_STOP))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_STOP* stopData = (sP_CL2FE_REQ_PC_STOP*)data->buf;
updatePlayerPosition(sock, stopData->iX, stopData->iY, stopData->iZ);
DEBUGLOG(
std::cout << "P_CL2FE_REQ_PC_STOP:" << std::endl;
std::cout << "\tX: " << stopData->iX << std::endl;
std::cout << "\tY: " << stopData->iY << std::endl;
std::cout << "\tZ: " << stopData->iZ << std::endl;
)
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_STOP, stopResponse);
stopResponse.iID = players[sock].plr->iID;
stopResponse.iX = stopData->iX;
stopResponse.iY = stopData->iY;
stopResponse.iZ = stopData->iZ;
stopResponse.iCliTime = stopData->iCliTime; // maybe don't send this??? seems unneeded...
stopResponse.iSvrTime = tm;
for (CNSocket* otherSock : players[sock].viewable) {
sP_FE2CL_PC_STOP* stopResponse = (sP_FE2CL_PC_STOP*)xmalloc(sizeof(sP_FE2CL_PC_STOP));
stopResponse->iID = players[sock].plr.iID;
stopResponse->iX = stopData->iX;
stopResponse->iY = stopData->iY;
stopResponse->iZ = stopData->iZ;
stopResponse->iCliTime = stopData->iCliTime; // maybe don't send this??? seems unneeded...
stopResponse->iSvrTime = tm;
otherSock->sendPacket(new CNPacketData((void*)stopResponse, P_FE2CL_PC_STOP, sizeof(sP_FE2CL_PC_STOP), otherSock->getFEKey()));
otherSock->sendPacket((void*)&stopResponse, P_FE2CL_PC_STOP, sizeof(sP_FE2CL_PC_STOP));
}
}
void PlayerManager::jumpPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_JUMP))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_JUMP* jumpData = (sP_CL2FE_REQ_PC_JUMP*)data->buf;
updatePlayerPosition(sock, jumpData->iX, jumpData->iY, jumpData->iZ);
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_JUMP, jumpResponse);
jumpResponse.iID = players[sock].plr->iID;
jumpResponse.cKeyValue = jumpData->cKeyValue;
jumpResponse.iX = jumpData->iX;
jumpResponse.iY = jumpData->iY;
jumpResponse.iZ = jumpData->iZ;
jumpResponse.iAngle = jumpData->iAngle;
jumpResponse.iVX = jumpData->iVX;
jumpResponse.iVY = jumpData->iVY;
jumpResponse.iVZ = jumpData->iVZ;
jumpResponse.iSpeed = jumpData->iSpeed;
jumpResponse.iCliTime = jumpData->iCliTime; // maybe don't send this??? seems unneeded...
jumpResponse.iSvrTime = tm;
for (CNSocket* otherSock : players[sock].viewable) {
sP_FE2CL_PC_JUMP* jumpResponse = (sP_FE2CL_PC_JUMP*)xmalloc(sizeof(sP_FE2CL_PC_JUMP));
otherSock->sendPacket((void*)&jumpResponse, P_FE2CL_PC_JUMP, sizeof(sP_FE2CL_PC_JUMP));
}
}
jumpResponse->iID = players[sock].plr.iID;
jumpResponse->cKeyValue = jumpData->cKeyValue;
void PlayerManager::jumppadPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_JUMPPAD))
return; // ignore the malformed packet
jumpResponse->iX = jumpData->iX;
jumpResponse->iY = jumpData->iY;
jumpResponse->iZ = jumpData->iZ;
jumpResponse->iAngle = jumpData->iAngle;
jumpResponse->iVX = jumpData->iVX;
jumpResponse->iVY = jumpData->iVY;
jumpResponse->iVZ = jumpData->iVZ;
jumpResponse->iSpeed = jumpData->iSpeed;
jumpResponse->iCliTime = jumpData->iCliTime; // maybe don't send this??? seems unneeded...
jumpResponse->iSvrTime = tm;
sP_CL2FE_REQ_PC_JUMPPAD* jumppadData = (sP_CL2FE_REQ_PC_JUMPPAD*)data->buf;
updatePlayerPosition(sock, jumppadData->iX, jumppadData->iY, jumppadData->iZ);
otherSock->sendPacket(new CNPacketData((void*)jumpResponse, P_FE2CL_PC_JUMP, sizeof(sP_FE2CL_PC_JUMP), otherSock->getFEKey()));
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_JUMPPAD, jumppadResponse);
jumppadResponse.iPC_ID = players[sock].plr->iID;
jumppadResponse.cKeyValue = jumppadData->cKeyValue;
jumppadResponse.iX = jumppadData->iX;
jumppadResponse.iY = jumppadData->iY;
jumppadResponse.iZ = jumppadData->iZ;
jumppadResponse.iVX = jumppadData->iVX;
jumppadResponse.iVY = jumppadData->iVY;
jumppadResponse.iVZ = jumppadData->iVZ;
jumppadResponse.iCliTime = jumppadData->iCliTime;
jumppadResponse.iSvrTime = tm;
for (CNSocket* otherSock : players[sock].viewable) {
otherSock->sendPacket((void*)&jumppadResponse, P_FE2CL_PC_JUMPPAD, sizeof(sP_FE2CL_PC_JUMPPAD));
}
}
void PlayerManager::launchPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_LAUNCHER))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_LAUNCHER* launchData = (sP_CL2FE_REQ_PC_LAUNCHER*)data->buf;
updatePlayerPosition(sock, launchData->iX, launchData->iY, launchData->iZ);
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_LAUNCHER, launchResponse);
launchResponse.iPC_ID = players[sock].plr->iID;
launchResponse.iX = launchData->iX;
launchResponse.iY = launchData->iY;
launchResponse.iZ = launchData->iZ;
launchResponse.iVX = launchData->iVX;
launchResponse.iVY = launchData->iVY;
launchResponse.iVZ = launchData->iVZ;
launchResponse.iSpeed = launchData->iSpeed;
launchResponse.iAngle = launchData->iAngle;
launchResponse.iCliTime = launchData->iCliTime;
launchResponse.iSvrTime = tm;
for (CNSocket* otherSock : players[sock].viewable) {
otherSock->sendPacket((void*)&launchResponse, P_FE2CL_PC_LAUNCHER, sizeof(sP_FE2CL_PC_LAUNCHER));
}
}
void PlayerManager::ziplinePlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_ZIPLINE))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_ZIPLINE* ziplineData = (sP_CL2FE_REQ_PC_ZIPLINE*)data->buf;
updatePlayerPosition(sock, ziplineData->iX, ziplineData->iY, ziplineData->iZ);
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_ZIPLINE, ziplineResponse);
ziplineResponse.iPC_ID = players[sock].plr->iID;
ziplineResponse.iCliTime = ziplineData->iCliTime;
ziplineResponse.iSvrTime = tm;
ziplineResponse.iX = ziplineData->iX;
ziplineResponse.iY = ziplineData->iY;
ziplineResponse.iZ = ziplineData->iZ;
ziplineResponse.fVX = ziplineData->fVX;
ziplineResponse.fVY = ziplineData->fVY;
ziplineResponse.fVZ = ziplineData->fVZ;
ziplineResponse.fMovDistance = ziplineData->fMovDistance;
ziplineResponse.fMaxDistance = ziplineData->fMaxDistance;
ziplineResponse.fDummy = ziplineData->fDummy; //wtf is this for?
ziplineResponse.iStX = ziplineData->iStX;
ziplineResponse.iStY = ziplineData->iStY;
ziplineResponse.iStZ = ziplineData->iStZ;
ziplineResponse.bDown = ziplineData->bDown;
ziplineResponse.iSpeed = ziplineData->iSpeed;
ziplineResponse.iAngle = ziplineData->iAngle;
ziplineResponse.iRollMax = ziplineData->iRollMax;
ziplineResponse.iRoll = ziplineData->iRoll;
for (CNSocket* otherSock : players[sock].viewable) {
otherSock->sendPacket((void*)&ziplineResponse, P_FE2CL_PC_ZIPLINE, sizeof(sP_FE2CL_PC_ZIPLINE));
}
}
void PlayerManager::movePlatformPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_MOVEPLATFORM))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_MOVEPLATFORM* platformData = (sP_CL2FE_REQ_PC_MOVEPLATFORM*)data->buf;
updatePlayerPosition(sock, platformData->iX, platformData->iY, platformData->iZ);
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_MOVEPLATFORM, platResponse);
platResponse.iPC_ID = players[sock].plr->iID;
platResponse.iCliTime = platformData->iCliTime;
platResponse.iSvrTime = tm;
platResponse.iX = platformData->iX;
platResponse.iY = platformData->iY;
platResponse.iZ = platformData->iZ;
platResponse.iAngle = platformData->iAngle;
platResponse.fVX = platformData->fVX;
platResponse.fVY = platformData->fVY;
platResponse.fVZ = platformData->fVZ;
platResponse.iLcX = platformData->iLcX;
platResponse.iLcY = platformData->iLcY;
platResponse.iLcZ = platformData->iLcZ;
platResponse.iSpeed = platformData->iSpeed;
platResponse.bDown = platformData->bDown;
platResponse.cKeyValue = platformData->cKeyValue;
platResponse.iPlatformID = platformData->iPlatformID;
for (CNSocket* otherSock : players[sock].viewable) {
otherSock->sendPacket((void*)&platResponse, P_FE2CL_PC_MOVEPLATFORM, sizeof(sP_FE2CL_PC_MOVEPLATFORM));
}
}
sP_FE2CL_PC_MOVEPLATFORM* platResponse = (sP_FE2CL_PC_MOVEPLATFORM*)xmalloc(sizeof(sP_FE2CL_PC_MOVEPLATFORM));
void PlayerManager::moveSlopePlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_SLOPE))
return; // ignore the malformed packet
platResponse->iPC_ID = players[sock].plr.iID;
platResponse->iCliTime = platformData->iCliTime;
platResponse->iSvrTime = tm;
platResponse->iX = platformData->iX;
platResponse->iY = platformData->iY;
platResponse->iZ = platformData->iZ;
platResponse->iAngle = platformData->iAngle;
platResponse->fVX = platformData->fVX;
platResponse->fVY = platformData->fVY;
platResponse->fVZ = platformData->fVZ;
platResponse->iLcX = platformData->iLcX;
platResponse->iLcY = platformData->iLcY;
platResponse->iLcZ = platformData->iLcZ;
platResponse->iSpeed = platformData->iSpeed;
platResponse->bDown = platformData->bDown;
platResponse->cKeyValue = platformData->cKeyValue;
platResponse->iPlatformID = platformData->iPlatformID;
sP_CL2FE_REQ_PC_SLOPE* slopeData = (sP_CL2FE_REQ_PC_SLOPE*)data->buf;
updatePlayerPosition(sock, slopeData->iX, slopeData->iY, slopeData->iZ);
otherSock->sendPacket(new CNPacketData((void*)platResponse, P_FE2CL_PC_MOVEPLATFORM, sizeof(sP_FE2CL_PC_MOVEPLATFORM), otherSock->getFEKey()));
uint64_t tm = getTime();
INITSTRUCT(sP_FE2CL_PC_SLOPE, slopeResponse);
slopeResponse.iPC_ID = players[sock].plr->iID;
slopeResponse.iCliTime = slopeData->iCliTime;
slopeResponse.iSvrTime = tm;
slopeResponse.iX = slopeData->iX;
slopeResponse.iY = slopeData->iY;
slopeResponse.iZ = slopeData->iZ;
slopeResponse.iAngle = slopeData->iAngle;
slopeResponse.fVX = slopeData->fVX;
slopeResponse.fVY = slopeData->fVY;
slopeResponse.fVZ = slopeData->fVZ;
slopeResponse.iSpeed = slopeData->iSpeed;
slopeResponse.cKeyValue = slopeData->cKeyValue;
slopeResponse.iSlopeID = slopeData->iSlopeID;
for (CNSocket* otherSock : players[sock].viewable) {
otherSock->sendPacket((void*)&slopeResponse, P_FE2CL_PC_SLOPE, sizeof(sP_FE2CL_PC_SLOPE));
}
}
void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_GOTO))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_GOTO* gotoData = (sP_CL2FE_REQ_PC_GOTO*)data->buf;
sP_FE2CL_REP_PC_GOTO_SUCC* response = (sP_FE2CL_REP_PC_GOTO_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_GOTO_SUCC));
INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, response);
DEBUGLOG(
std::cout << "P_CL2FE_REQ_PC_GOTO:" << std::endl;
@@ -338,16 +536,19 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) {
std::cout << "\tZ: " << gotoData->iToZ << std::endl;
)
response->iX = gotoData->iToX;
response->iY = gotoData->iToY;
response->iZ = gotoData->iToZ;
response.iX = gotoData->iToX;
response.iY = gotoData->iToY;
response.iZ = gotoData->iToZ;
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_GOTO_SUCC, sizeof(sP_FE2CL_REP_PC_GOTO_SUCC), sock->getFEKey()));
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_GOTO_SUCC, sizeof(sP_FE2CL_REP_PC_GOTO_SUCC));
}
void PlayerManager::setSpecialPlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_GM_REQ_PC_SET_VALUE))
return; // ignore the malformed packet
sP_CL2FE_GM_REQ_PC_SET_VALUE* setData = (sP_CL2FE_GM_REQ_PC_SET_VALUE*)data->buf;
sP_FE2CL_GM_REP_PC_SET_VALUE* response = (sP_FE2CL_GM_REP_PC_SET_VALUE*)xmalloc(sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE));
INITSTRUCT(sP_FE2CL_GM_REP_PC_SET_VALUE, response);
DEBUGLOG(
std::cout << "P_CL2FE_GM_REQ_PC_SET_VALUE:" << std::endl;
@@ -356,9 +557,157 @@ void PlayerManager::setSpecialPlayer(CNSocket* sock, CNPacketData* data) {
std::cout << "\tSetValue: " << setData->iSetValue << std::endl;
)
response->iPC_ID = setData->iPC_ID;
response->iSetValue = setData->iSetValue;
response->iSetValueType = setData->iSetValueType;
response.iPC_ID = setData->iPC_ID;
response.iSetValue = setData->iSetValue;
response.iSetValueType = setData->iSetValueType;
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_GM_REP_PC_SET_VALUE, sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE), sock->getFEKey()));
}
sock->sendPacket((void*)&response, P_FE2CL_GM_REP_PC_SET_VALUE, sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE));
}
void PlayerManager::heartbeatPlayer(CNSocket* sock, CNPacketData* data) {
players[sock].lastHeartbeat = getTime();
}
void PlayerManager::exitGame(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_EXIT))
return;
sP_CL2FE_REQ_PC_EXIT* exitData = (sP_CL2FE_REQ_PC_EXIT*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_EXIT_SUCC, response);
response.iID = exitData->iID;
response.iExitCode = 1;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_EXIT_SUCC, sizeof(sP_FE2CL_REP_PC_EXIT_SUCC));
}
void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_REGEN))
return;
Player *plr = PlayerManager::getPlayer(sock);
WarpLocation target = PlayerManager::getRespawnPoint(plr);
// players respawn at same spot they died at for now...
sP_CL2FE_REQ_PC_REGEN* reviveData = (sP_CL2FE_REQ_PC_REGEN*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_REGEN_SUCC, response);
response.bMoveLocation = reviveData->eIL;
response.PCRegenData.iMapNum = reviveData->iIndex;
response.PCRegenData.iHP = 1000 * plr->level;
response.PCRegenData.iX = target.x;
response.PCRegenData.iY = target.y;
response.PCRegenData.iZ = target.z;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_REGEN_SUCC, sizeof(sP_FE2CL_REP_PC_REGEN_SUCC));
}
void PlayerManager::enterPlayerVehicle(CNSocket* sock, CNPacketData* data) {
PlayerView& plr = PlayerManager::players[sock];
if (plr.plr->Equip[8].iID > 0) {
INITSTRUCT(sP_FE2CL_PC_VEHICLE_ON_SUCC, response);
sock->sendPacket((void*)&response, P_FE2CL_PC_VEHICLE_ON_SUCC, sizeof(sP_FE2CL_PC_VEHICLE_ON_SUCC));
//send to other players
plr.plr->iPCState = 8;
INITSTRUCT(sP_FE2CL_PC_STATE_CHANGE, response2);
response2.iPC_ID = plr.plr->iID;
response2.iState = 8;
for (CNSocket* otherSock : plr.viewable) {
otherSock->sendPacket((void*)&response2, P_FE2CL_PC_STATE_CHANGE, sizeof(sP_FE2CL_PC_STATE_CHANGE));
}
} else {
INITSTRUCT(sP_FE2CL_PC_VEHICLE_ON_FAIL, response);
sock->sendPacket((void*)&response, P_FE2CL_PC_VEHICLE_ON_FAIL, sizeof(sP_FE2CL_PC_VEHICLE_ON_FAIL));
}
}
void PlayerManager::exitPlayerVehicle(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_PC_VEHICLE_OFF_SUCC, response);
sock->sendPacket((void*)&response, P_FE2CL_PC_VEHICLE_OFF_SUCC, sizeof(sP_FE2CL_PC_VEHICLE_OFF_SUCC));
PlayerView plr = PlayerManager::players[sock];
//send to other players
plr.plr->iPCState = 0;
INITSTRUCT(sP_FE2CL_PC_STATE_CHANGE, response2);
response2.iPC_ID = plr.plr->iID;
response2.iState = 0;
for (CNSocket* otherSock : plr.viewable) {
otherSock->sendPacket((void*)&response2, P_FE2CL_PC_STATE_CHANGE, sizeof(sP_FE2CL_PC_STATE_CHANGE));
}
}
void PlayerManager::setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH* specialData = (sP_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, response);
response.iPC_ID = specialData->iPC_ID;
response.iReqSpecialStateFlag = specialData->iSpecialStateFlag;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, sizeof(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC));
}
void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_CHANGE_MENTOR))
return;
sP_CL2FE_REQ_PC_CHANGE_MENTOR *pkt = (sP_CL2FE_REQ_PC_CHANGE_MENTOR*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, resp);
Player *plr = getPlayer(sock);
resp.iMentor = pkt->iMentor;
resp.iMentorCnt = 1;
resp.iFusionMatter = plr->fusionmatter; // no cost
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, sizeof(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC));
}
#pragma region Helper methods
Player *PlayerManager::getPlayer(CNSocket* key) {
return players[key].plr;
}
WarpLocation PlayerManager::getRespawnPoint(Player *plr) {
WarpLocation best;
uint32_t curDist, bestDist = UINT32_MAX;
for (auto targ : NPCManager::RespawnPoints) {
curDist = sqrt(pow(plr->x - targ.x, 2) + pow(plr->y - targ.y, 2));
if (curDist < bestDist) {
best = targ;
bestDist = curDist;
}
}
return best;
}
bool PlayerManager::isAccountInUse(int accountId) {
std::map<CNSocket*, PlayerView>::iterator it;
for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++)
{
if (it->second.plr->accountId == accountId)
return true;
}
return false;
}
void PlayerManager::exitDuplicate(int accountId) {
std::map<CNSocket*, PlayerView>::iterator it;
for (it = PlayerManager::players.begin(); it != PlayerManager::players.end(); it++)
{
if (it->second.plr->accountId == accountId)
{
CNSocket* sock = it->first;
INITSTRUCT(sP_FE2CL_REP_PC_EXIT_DUPLICATE, resp);
resp.iErrorCode = 0;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_EXIT_DUPLICATE, sizeof(sP_FE2CL_REP_PC_EXIT_DUPLICATE));
sock->kill();
}
}
}
#pragma endregion

View File

@@ -1,5 +1,4 @@
#ifndef _PM_HPP
#define _PM_HPP
#pragma once
#include "Player.hpp"
#include "CNProtocol.hpp"
@@ -9,9 +8,13 @@
#include <map>
#include <list>
struct WarpLocation;
struct PlayerView {
std::list<CNSocket*> viewable;
Player plr;
std::list<int32_t> viewableNPCs;
Player *plr;
uint64_t lastHeartbeat;
};
@@ -21,19 +24,35 @@ namespace PlayerManager {
void addPlayer(CNSocket* key, Player plr);
void removePlayer(CNSocket* key);
Player getPlayer(CNSocket* key);
void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z);
std::list<CNSocket*> getNearbyPlayers(int X, int Y, int dist);
void enterPlayer(CNSocket* sock, CNPacketData* data);
void loadPlayer(CNSocket* sock, CNPacketData* data);
void movePlayer(CNSocket* sock, CNPacketData* data);
void stopPlayer(CNSocket* sock, CNPacketData* data);
void jumpPlayer(CNSocket* sock, CNPacketData* data);
void jumppadPlayer(CNSocket* sock, CNPacketData* data);
void launchPlayer(CNSocket* sock, CNPacketData* data);
void ziplinePlayer(CNSocket* sock, CNPacketData* data);
void movePlatformPlayer(CNSocket* sock, CNPacketData* data);
void moveSlopePlayer(CNSocket* sock, CNPacketData* data);
void gotoPlayer(CNSocket* sock, CNPacketData* data);
void setSpecialPlayer(CNSocket* sock, CNPacketData* data);
}
void heartbeatPlayer(CNSocket* sock, CNPacketData* data);
void revivePlayer(CNSocket* sock, CNPacketData* data);
void exitGame(CNSocket* sock, CNPacketData* data);
#endif
void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data);
void changePlayerGuide(CNSocket *sock, CNPacketData *data);
void enterPlayerVehicle(CNSocket* sock, CNPacketData* data);
void exitPlayerVehicle(CNSocket* sock, CNPacketData* data);
Player *getPlayer(CNSocket* key);
WarpLocation getRespawnPoint(Player *plr);
bool isAccountInUse(int accountId);
void exitDuplicate(int accountId);
}

18
src/TransportManager.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include "CNShardServer.hpp"
#include "CNStructs.hpp"
#include "PlayerManager.hpp"
#include "TransportManager.hpp"
void TransportManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION, transportRegisterLocationHandler);
}
void TransportManager::transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION* transport = (sP_CL2FE_REQ_REGIST_TRANSPORTATION_LOCATION*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, resp);
resp.eTT = transport->eTT;
resp.iLocationID = transport->iLocationID;
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC, sizeof(sP_FE2CL_REP_PC_REGIST_TRANSPORTATION_LOCATION_SUCC));
}

9
src/TransportManager.hpp Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include "CNShardServer.hpp"
namespace TransportManager {
void init();
void transportRegisterLocationHandler(CNSocket* sock, CNPacketData* data);
}

25447
src/contrib/JSON.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
#ifndef __BCRYPT__
#define __BCRYPT__
#ifdef _WIN32
#include "winbcrypt.h"
#else
#include "bcrypt.h"
#include <string>
#include <stdexcept>
class BCrypt {
public:
static std::string generateHash(const std::string & password, int workload = 12){
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int ret;
ret = bcrypt_gensalt(workload, salt);
if(ret != 0)throw std::runtime_error{"bcrypt: can not generate salt"};
ret = bcrypt_hashpw(password.c_str(), salt, hash);
if(ret != 0)throw std::runtime_error{"bcrypt: can not generate hash"};
return std::string{hash};
}
static bool validatePassword(const std::string & password, const std::string & hash){
return (bcrypt_checkpw(password.c_str(), hash.c_str()) == 0);
}
};
#endif
#endif // __BCRYPT__

230
src/contrib/bcrypt/bcrypt.c Normal file
View File

@@ -0,0 +1,230 @@
/*
* bcrypt wrapper library
*
* Written in 2011, 2013, 2014, 2015 by Ricardo Garcia <r@rg3.name>
*
* To the extent possible under law, the author(s) have dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along
* with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
#elif _WIN64
#else
#include <unistd.h>
#endif
#include <errno.h>
#ifdef _WIN32 || _WIN64
// On windows we need to generate random bytes differently.
typedef __int64 ssize_t;
#define BCRYPT_HASHSIZE 60
#include "bcrypt.h"
#include <windows.h>
#include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
#else
#include "bcrypt.h"
#include "ow-crypt.h"
#endif
#define RANDBYTES (16)
static int try_close(int fd)
{
int ret;
for (;;) {
errno = 0;
ret = close(fd);
if (ret == -1 && errno == EINTR)
continue;
break;
}
return ret;
}
static int try_read(int fd, char *out, size_t count)
{
size_t total;
ssize_t partial;
total = 0;
while (total < count)
{
for (;;) {
errno = 0;
partial = read(fd, out + total, count - total);
if (partial == -1 && errno == EINTR)
continue;
break;
}
if (partial < 1)
return -1;
total += partial;
}
return 0;
}
/*
* This is a best effort implementation. Nothing prevents a compiler from
* optimizing this function and making it vulnerable to timing attacks, but
* this method is commonly used in crypto libraries like NaCl.
*
* Return value is zero if both strings are equal and nonzero otherwise.
*/
static int timing_safe_strcmp(const char *str1, const char *str2)
{
const unsigned char *u1;
const unsigned char *u2;
int ret;
int i;
int len1 = strlen(str1);
int len2 = strlen(str2);
/* In our context both strings should always have the same length
* because they will be hashed passwords. */
if (len1 != len2)
return 1;
/* Force unsigned for bitwise operations. */
u1 = (const unsigned char *)str1;
u2 = (const unsigned char *)str2;
ret = 0;
for (i = 0; i < len1; ++i)
ret |= (u1[i] ^ u2[i]);
return ret;
}
int bcrypt_gensalt(int factor, char salt[BCRYPT_HASHSIZE])
{
int fd;
char input[RANDBYTES];
int workf;
char *aux;
// Note: Windows does not have /dev/urandom sadly.
#ifdef _WIN32 || _WIN64
HCRYPTPROV p;
ULONG i;
// Acquire a crypt context for generating random bytes.
if (CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) {
return 1;
}
if (CryptGenRandom(p, RANDBYTES, (BYTE*)input) == FALSE) {
return 2;
}
if (CryptReleaseContext(p, 0) == FALSE) {
return 3;
}
#else
// Get random bytes on Unix/Linux.
fd = open("/dev/urandom", O_RDONLY);
if (fd == -1)
return 1;
if (try_read(fd, input, RANDBYTES) != 0) {
if (try_close(fd) != 0)
return 4;
return 2;
}
if (try_close(fd) != 0)
return 3;
#endif
/* Generate salt. */
workf = (factor < 4 || factor > 31)?12:factor;
aux = crypt_gensalt_rn("$2a$", workf, input, RANDBYTES,
salt, BCRYPT_HASHSIZE);
return (aux == NULL)?5:0;
}
int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE])
{
char *aux;
aux = crypt_rn(passwd, salt, hash, BCRYPT_HASHSIZE);
return (aux == NULL)?1:0;
}
int bcrypt_checkpw(const char *passwd, const char hash[BCRYPT_HASHSIZE])
{
int ret;
char outhash[BCRYPT_HASHSIZE];
ret = bcrypt_hashpw(passwd, hash, outhash);
if (ret != 0)
return -1;
return timing_safe_strcmp(hash, outhash);
}
#ifdef TEST_BCRYPT
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void)
{
clock_t before;
clock_t after;
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int ret;
const char pass[] = "hi,mom";
const char hash1[] = "$2a$10$VEVmGHy4F4XQMJ3eOZJAUeb.MedU0W10pTPCuf53eHdKJPiSE8sMK";
const char hash2[] = "$2a$10$3F0BVk5t8/aoS.3ddaB3l.fxg5qvafQ9NybxcpXLzMeAt.nVWn.NO";
ret = bcrypt_gensalt(12, salt);
assert(ret == 0);
printf("Generated salt: %s\n", salt);
before = clock();
ret = bcrypt_hashpw("testtesttest", salt, hash);
assert(ret == 0);
after = clock();
printf("Hashed password: %s\n", hash);
printf("Time taken: %f seconds\n",
(double)(after - before) / CLOCKS_PER_SEC);
ret = bcrypt_hashpw(pass, hash1, hash);
assert(ret == 0);
printf("First hash check: %s\n", (strcmp(hash1, hash) == 0)?"OK":"FAIL");
ret = bcrypt_hashpw(pass, hash2, hash);
assert(ret == 0);
printf("Second hash check: %s\n", (strcmp(hash2, hash) == 0)?"OK":"FAIL");
before = clock();
ret = (bcrypt_checkpw(pass, hash1) == 0);
after = clock();
printf("First hash check with bcrypt_checkpw: %s\n", ret?"OK":"FAIL");
printf("Time taken: %f seconds\n",
(double)(after - before) / CLOCKS_PER_SEC);
before = clock();
ret = (bcrypt_checkpw(pass, hash2) == 0);
after = clock();
printf("Second hash check with bcrypt_checkpw: %s\n", ret?"OK":"FAIL");
printf("Time taken: %f seconds\n",
(double)(after - before) / CLOCKS_PER_SEC);
return 0;
}
#endif

View File

@@ -0,0 +1,97 @@
#ifndef BCRYPT_H_
#define BCRYPT_H_
/*
* bcrypt wrapper library
*
* Written in 2011, 2013, 2014, 2015 by Ricardo Garcia <r@rg3.name>
*
* To the extent possible under law, the author(s) have dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along
* with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#define BCRYPT_HASHSIZE (64)
#ifdef __cplusplus
extern "C" {
#endif
/*
* This function expects a work factor between 4 and 31 and a char array to
* store the resulting generated salt. The char array should typically have
* BCRYPT_HASHSIZE bytes at least. If the provided work factor is not in the
* previous range, it will default to 12.
*
* The return value is zero if the salt could be correctly generated and
* nonzero otherwise.
*
*/
int bcrypt_gensalt(int workfactor, char salt[BCRYPT_HASHSIZE]);
/*
* This function expects a password to be hashed, a salt to hash the password
* with and a char array to leave the result. Both the salt and the hash
* parameters should have room for BCRYPT_HASHSIZE characters at least.
*
* It can also be used to verify a hashed password. In that case, provide the
* expected hash in the salt parameter and verify the output hash is the same
* as the input hash. However, to avoid timing attacks, it's better to use
* bcrypt_checkpw when verifying a password.
*
* The return value is zero if the password could be hashed and nonzero
* otherwise.
*/
int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE],
char hash[BCRYPT_HASHSIZE]);
/*
* This function expects a password and a hash to verify the password against.
* The internal implementation is tuned to avoid timing attacks.
*
* The return value will be -1 in case of errors, zero if the provided password
* matches the given hash and greater than zero if no errors are found and the
* passwords don't match.
*
*/
int bcrypt_checkpw(const char *passwd, const char hash[BCRYPT_HASHSIZE]);
/*
* Brief Example
* -------------
*
* Hashing a password:
*
* char salt[BCRYPT_HASHSIZE];
* char hash[BCRYPT_HASHSIZE];
* int ret;
*
* ret = bcrypt_gensalt(12, salt);
* assert(ret == 0);
* ret = bcrypt_hashpw("thepassword", salt, hash);
* assert(ret == 0);
*
*
* Verifying a password:
*
* int ret;
*
* ret = bcrypt_checkpw("thepassword", "expectedhash");
* assert(ret != -1);
*
* if (ret == 0) {
* printf("The password matches\n");
* } else {
* printf("The password does NOT match\n");
* }
*
*/
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,24 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2002.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2002 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
//#include <gnu-crypt.h>
#if defined(_OW_SOURCE) || defined(__USE_OW)
#define __SKIP_GNU
#undef __SKIP_OW
#include <ow-crypt.h>
#undef __SKIP_GNU
#endif

View File

@@ -0,0 +1,911 @@
/*
* The crypt_blowfish homepage is:
*
* http://www.openwall.com/crypt/
*
* This code comes from John the Ripper password cracker, with reentrant
* and crypt(3) interfaces added, but optimizations specific to password
* cracking removed.
*
* Written by Solar Designer <solar at openwall.com> in 1998-2014.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* It is my intent that you should be able to use this on your system,
* as part of a software package, or anywhere else to improve security,
* ensure compatibility, or for any other purpose. I would appreciate
* it if you give credit where it is due and keep your modifications in
* the public domain as well, but I don't require that in order to let
* you place this code and any modifications you make under a license
* of your choice.
*
* This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
* "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
* some of his ideas. The password hashing algorithm was designed by David
* Mazieres <dm at lcs.mit.edu>. For information on the level of
* compatibility for bcrypt hash prefixes other than "$2b$", please refer to
* the comments in BF_set_key() below and to the included crypt(3) man page.
*
* There's a paper on the algorithm that explains its design decisions:
*
* http://www.usenix.org/events/usenix99/provos.html
*
* Some of the tricks in BF_ROUND might be inspired by Eric Young's
* Blowfish library (I can't be sure if I would think of something if I
* hadn't seen his code).
*/
#include <string.h>
#include <errno.h>
#ifndef __set_errno
#define __set_errno(val) errno = (val)
#endif
/* Just to make sure the prototypes match the actual definitions */
#ifdef _WIN32 || _WIN64
#include "crypt_blowfish.h"
#else
#include "crypt_blowfish.h"
#endif
#ifdef __i386__
#define BF_ASM 1
#define BF_SCALE 1
#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
#define BF_ASM 0
#define BF_SCALE 1
#else
#define BF_ASM 0
#define BF_SCALE 0
#endif
typedef unsigned int BF_word;
typedef signed int BF_word_signed;
/* Number of Blowfish rounds, this is also hardcoded into a few places */
#define BF_N 16
typedef BF_word BF_key[BF_N + 2];
typedef struct {
BF_word S[4][0x100];
BF_key P;
} BF_ctx;
/*
* Magic IV for 64 Blowfish encryptions that we do at the end.
* The string is "OrpheanBeholderScryDoubt" on big-endian.
*/
static BF_word BF_magic_w[6] = {
0x4F727068, 0x65616E42, 0x65686F6C,
0x64657253, 0x63727944, 0x6F756274
};
/*
* P-box and S-box tables initialized with digits of Pi.
*/
static BF_ctx BF_init_state = {
{
{
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
}, {
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
}, {
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
}, {
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
}
}, {
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
}
};
static unsigned char BF_itoa64[64 + 1] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static unsigned char BF_atoi64[0x60] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
};
#define BF_safe_atoi64(dst, src) \
{ \
tmp = (unsigned char)(src); \
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
tmp = BF_atoi64[tmp]; \
if (tmp > 63) return -1; \
(dst) = tmp; \
}
static int BF_decode(BF_word *dst, const char *src, int size)
{
unsigned char *dptr = (unsigned char *)dst;
unsigned char *end = dptr + size;
const unsigned char *sptr = (const unsigned char *)src;
unsigned int tmp, c1, c2, c3, c4;
do {
BF_safe_atoi64(c1, *sptr++);
BF_safe_atoi64(c2, *sptr++);
*dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
if (dptr >= end) break;
BF_safe_atoi64(c3, *sptr++);
*dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
if (dptr >= end) break;
BF_safe_atoi64(c4, *sptr++);
*dptr++ = ((c3 & 0x03) << 6) | c4;
} while (dptr < end);
return 0;
}
static void BF_encode(char *dst, const BF_word *src, int size)
{
const unsigned char *sptr = (const unsigned char *)src;
const unsigned char *end = sptr + size;
unsigned char *dptr = (unsigned char *)dst;
unsigned int c1, c2;
do {
c1 = *sptr++;
*dptr++ = BF_itoa64[c1 >> 2];
c1 = (c1 & 0x03) << 4;
if (sptr >= end) {
*dptr++ = BF_itoa64[c1];
break;
}
c2 = *sptr++;
c1 |= c2 >> 4;
*dptr++ = BF_itoa64[c1];
c1 = (c2 & 0x0f) << 2;
if (sptr >= end) {
*dptr++ = BF_itoa64[c1];
break;
}
c2 = *sptr++;
c1 |= c2 >> 6;
*dptr++ = BF_itoa64[c1];
*dptr++ = BF_itoa64[c2 & 0x3f];
} while (sptr < end);
}
static void BF_swap(BF_word *x, int count)
{
static int endianness_check = 1;
char *is_little_endian = (char *)&endianness_check;
BF_word tmp;
if (*is_little_endian)
do {
tmp = *x;
tmp = (tmp << 16) | (tmp >> 16);
*x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
} while (--count);
}
#if BF_SCALE
/* Architectures which can shift addresses left by 2 bits with no extra cost */
#define BF_ROUND(L, R, N) \
tmp1 = L & 0xFF; \
tmp2 = L >> 8; \
tmp2 &= 0xFF; \
tmp3 = L >> 16; \
tmp3 &= 0xFF; \
tmp4 = L >> 24; \
tmp1 = data.ctx.S[3][tmp1]; \
tmp2 = data.ctx.S[2][tmp2]; \
tmp3 = data.ctx.S[1][tmp3]; \
tmp3 += data.ctx.S[0][tmp4]; \
tmp3 ^= tmp2; \
R ^= data.ctx.P[N + 1]; \
tmp3 += tmp1; \
R ^= tmp3;
#else
/* Architectures with no complicated addressing modes supported */
#define BF_INDEX(S, i) \
(*((BF_word *)(((unsigned char *)S) + (i))))
#define BF_ROUND(L, R, N) \
tmp1 = L & 0xFF; \
tmp1 <<= 2; \
tmp2 = L >> 6; \
tmp2 &= 0x3FC; \
tmp3 = L >> 14; \
tmp3 &= 0x3FC; \
tmp4 = L >> 22; \
tmp4 &= 0x3FC; \
tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
tmp3 ^= tmp2; \
R ^= data.ctx.P[N + 1]; \
tmp3 += tmp1; \
R ^= tmp3;
#endif
/*
* Encrypt one block, BF_N is hardcoded here.
*/
#define BF_ENCRYPT \
L ^= data.ctx.P[0]; \
BF_ROUND(L, R, 0); \
BF_ROUND(R, L, 1); \
BF_ROUND(L, R, 2); \
BF_ROUND(R, L, 3); \
BF_ROUND(L, R, 4); \
BF_ROUND(R, L, 5); \
BF_ROUND(L, R, 6); \
BF_ROUND(R, L, 7); \
BF_ROUND(L, R, 8); \
BF_ROUND(R, L, 9); \
BF_ROUND(L, R, 10); \
BF_ROUND(R, L, 11); \
BF_ROUND(L, R, 12); \
BF_ROUND(R, L, 13); \
BF_ROUND(L, R, 14); \
BF_ROUND(R, L, 15); \
tmp4 = R; \
R = L; \
L = tmp4 ^ data.ctx.P[BF_N + 1];
#if BF_ASM
#define BF_body() \
_BF_body_r(&data.ctx);
#else
#define BF_body() \
L = R = 0; \
ptr = data.ctx.P; \
do { \
ptr += 2; \
BF_ENCRYPT; \
*(ptr - 2) = L; \
*(ptr - 1) = R; \
} while (ptr < &data.ctx.P[BF_N + 2]); \
\
ptr = data.ctx.S[0]; \
do { \
ptr += 2; \
BF_ENCRYPT; \
*(ptr - 2) = L; \
*(ptr - 1) = R; \
} while (ptr < &data.ctx.S[3][0xFF]);
#endif
static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
unsigned char flags)
{
const char *ptr = key;
unsigned int bug, i, j;
BF_word safety, sign, diff, tmp[2];
/*
* There was a sign extension bug in older revisions of this function. While
* we would have liked to simply fix the bug and move on, we have to provide
* a backwards compatibility feature (essentially the bug) for some systems and
* a safety measure for some others. The latter is needed because for certain
* multiple inputs to the buggy algorithm there exist easily found inputs to
* the correct algorithm that produce the same hash. Thus, we optionally
* deviate from the correct algorithm just enough to avoid such collisions.
* While the bug itself affected the majority of passwords containing
* characters with the 8th bit set (although only a percentage of those in a
* collision-producing way), the anti-collision safety measure affects
* only a subset of passwords containing the '\xff' character (not even all of
* those passwords, just some of them). This character is not found in valid
* UTF-8 sequences and is rarely used in popular 8-bit character encodings.
* Thus, the safety measure is unlikely to cause much annoyance, and is a
* reasonable tradeoff to use when authenticating against existing hashes that
* are not reliably known to have been computed with the correct algorithm.
*
* We use an approach that tries to minimize side-channel leaks of password
* information - that is, we mostly use fixed-cost bitwise operations instead
* of branches or table lookups. (One conditional branch based on password
* length remains. It is not part of the bug aftermath, though, and is
* difficult and possibly unreasonable to avoid given the use of C strings by
* the caller, which results in similar timing leaks anyway.)
*
* For actual implementation, we set an array index in the variable "bug"
* (0 means no bug, 1 means sign extension bug emulation) and a flag in the
* variable "safety" (bit 16 is set when the safety measure is requested).
* Valid combinations of settings are:
*
* Prefix "$2a$": bug = 0, safety = 0x10000
* Prefix "$2b$": bug = 0, safety = 0
* Prefix "$2x$": bug = 1, safety = 0
* Prefix "$2y$": bug = 0, safety = 0
*/
bug = (unsigned int)flags & 1;
safety = ((BF_word)flags & 2) << 15;
sign = diff = 0;
for (i = 0; i < BF_N + 2; i++) {
tmp[0] = tmp[1] = 0;
for (j = 0; j < 4; j++) {
tmp[0] <<= 8;
tmp[0] |= (unsigned char)*ptr; /* correct */
tmp[1] <<= 8;
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
/*
* Sign extension in the first char has no effect - nothing to overwrite yet,
* and those extra 24 bits will be fully shifted out of the 32-bit word. For
* chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
* extension in tmp[1] occurs. Once this flag is set, it remains set.
*/
if (j)
sign |= tmp[1] & 0x80;
if (!*ptr)
ptr = key;
else
ptr++;
}
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
expanded[i] = tmp[bug];
initial[i] = BF_init_state.P[i] ^ tmp[bug];
}
/*
* At this point, "diff" is zero iff the correct and buggy algorithms produced
* exactly the same result. If so and if "sign" is non-zero, which indicates
* that there was a non-benign sign extension, this means that we have a
* collision between the correctly computed hash for this password and a set of
* passwords that could be supplied to the buggy algorithm. Our safety measure
* is meant to protect from such many-buggy to one-correct collisions, by
* deviating from the correct algorithm in such cases. Let's check for this.
*/
diff |= diff >> 16; /* still zero iff exact match */
diff &= 0xffff; /* ditto */
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
sign &= ~diff & safety; /* action needed? */
/*
* If we have determined that we need to deviate from the correct algorithm,
* flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
* let's stick to it now. It came out of the approach we used above, and it's
* not any worse than any other choice we could make.)
*
* It is crucial that we don't do the same to the expanded key used in the main
* Eksblowfish loop. By doing it to only one of these two, we deviate from a
* state that could be directly specified by a password to the buggy algorithm
* (and to the fully correct one as well, but that's a side-effect).
*/
initial[0] ^= sign;
}
static const unsigned char flags_by_subtype[26] =
{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
static char *BF_crypt(const char *key, const char *setting,
char *output, int size,
BF_word min)
{
#if BF_ASM
extern void _BF_body_r(BF_ctx *ctx);
#endif
struct {
BF_ctx ctx;
BF_key expanded_key;
union {
BF_word salt[4];
BF_word output[6];
} binary;
} data;
BF_word L, R;
BF_word tmp1, tmp2, tmp3, tmp4;
BF_word *ptr;
BF_word count;
int i;
if (size < 7 + 22 + 31 + 1) {
__set_errno(ERANGE);
return NULL;
}
if (setting[0] != '$' ||
setting[1] != '2' ||
setting[2] < 'a' || setting[2] > 'z' ||
!flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] ||
setting[3] != '$' ||
setting[4] < '0' || setting[4] > '3' ||
setting[5] < '0' || setting[5] > '9' ||
(setting[4] == '3' && setting[5] > '1') ||
setting[6] != '$') {
__set_errno(EINVAL);
return NULL;
}
count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) {
__set_errno(EINVAL);
return NULL;
}
BF_swap(data.binary.salt, 4);
BF_set_key(key, data.expanded_key, data.ctx.P,
flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
L = R = 0;
for (i = 0; i < BF_N + 2; i += 2) {
L ^= data.binary.salt[i & 2];
R ^= data.binary.salt[(i & 2) + 1];
BF_ENCRYPT;
data.ctx.P[i] = L;
data.ctx.P[i + 1] = R;
}
ptr = data.ctx.S[0];
do {
ptr += 4;
L ^= data.binary.salt[(BF_N + 2) & 3];
R ^= data.binary.salt[(BF_N + 3) & 3];
BF_ENCRYPT;
*(ptr - 4) = L;
*(ptr - 3) = R;
L ^= data.binary.salt[(BF_N + 4) & 3];
R ^= data.binary.salt[(BF_N + 5) & 3];
BF_ENCRYPT;
*(ptr - 2) = L;
*(ptr - 1) = R;
} while (ptr < &data.ctx.S[3][0xFF]);
do {
int done;
for (i = 0; i < BF_N + 2; i += 2) {
data.ctx.P[i] ^= data.expanded_key[i];
data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
}
done = 0;
do {
BF_body();
if (done)
break;
done = 1;
tmp1 = data.binary.salt[0];
tmp2 = data.binary.salt[1];
tmp3 = data.binary.salt[2];
tmp4 = data.binary.salt[3];
for (i = 0; i < BF_N; i += 4) {
data.ctx.P[i] ^= tmp1;
data.ctx.P[i + 1] ^= tmp2;
data.ctx.P[i + 2] ^= tmp3;
data.ctx.P[i + 3] ^= tmp4;
}
data.ctx.P[16] ^= tmp1;
data.ctx.P[17] ^= tmp2;
} while (1);
} while (--count);
for (i = 0; i < 6; i += 2) {
L = BF_magic_w[i];
R = BF_magic_w[i + 1];
count = 64;
do {
BF_ENCRYPT;
} while (--count);
data.binary.output[i] = L;
data.binary.output[i + 1] = R;
}
memcpy(output, setting, 7 + 22 - 1);
output[7 + 22 - 1] = BF_itoa64[(int)
BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30];
/* This has to be bug-compatible with the original implementation, so
* only encode 23 of the 24 bytes. :-) */
BF_swap(data.binary.output, 6);
BF_encode(&output[7 + 22], data.binary.output, 23);
output[7 + 22 + 31] = '\0';
return output;
}
int _crypt_output_magic(const char *setting, char *output, int size)
{
if (size < 3)
return -1;
output[0] = '*';
output[1] = '0';
output[2] = '\0';
if (setting[0] == '*' && setting[1] == '0')
output[1] = '1';
return 0;
}
/*
* Please preserve the runtime self-test. It serves two purposes at once:
*
* 1. We really can't afford the risk of producing incompatible hashes e.g.
* when there's something like gcc bug 26587 again, whereas an application or
* library integrating this code might not also integrate our external tests or
* it might not run them after every build. Even if it does, the miscompile
* might only occur on the production build, but not on a testing build (such
* as because of different optimization settings). It is painful to recover
* from incorrectly-computed hashes - merely fixing whatever broke is not
* enough. Thus, a proactive measure like this self-test is needed.
*
* 2. We don't want to leave sensitive data from our actual password hash
* computation on the stack or in registers. Previous revisions of the code
* would do explicit cleanups, but simply running the self-test after hash
* computation is more reliable.
*
* The performance cost of this quick self-test is around 0.6% at the "$2a$08"
* setting.
*/
char *_crypt_blowfish_rn(const char *key, const char *setting,
char *output, int size)
{
const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
static const char * const test_hashes[2] =
{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */
"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */
const char *test_hash = test_hashes[0];
char *retval;
const char *p;
int save_errno, ok;
struct {
char s[7 + 22 + 1];
char o[7 + 22 + 31 + 1 + 1 + 1];
} buf;
/* Hash the supplied password */
_crypt_output_magic(setting, output, size);
retval = BF_crypt(key, setting, output, size, 16);
save_errno = errno;
/*
* Do a quick self-test. It is important that we make both calls to BF_crypt()
* from the same scope such that they likely use the same stack locations,
* which makes the second call overwrite the first call's sensitive data on the
* stack and makes it more likely that any alignment related issues would be
* detected by the self-test.
*/
memcpy(buf.s, test_setting, sizeof(buf.s));
if (retval) {
unsigned int flags = flags_by_subtype[
(unsigned int)(unsigned char)setting[2] - 'a'];
test_hash = test_hashes[flags & 1];
buf.s[2] = setting[2];
}
memset(buf.o, 0x55, sizeof(buf.o));
buf.o[sizeof(buf.o) - 1] = 0;
p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
ok = (p == buf.o &&
!memcmp(p, buf.s, 7 + 22) &&
!memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
{
const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
BF_key ae, ai, ye, yi;
BF_set_key(k, ae, ai, 2); /* $2a$ */
BF_set_key(k, ye, yi, 4); /* $2y$ */
ai[0] ^= 0x10000; /* undo the safety (for comparison) */
ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
!memcmp(ae, ye, sizeof(ae)) &&
!memcmp(ai, yi, sizeof(ai));
}
__set_errno(save_errno);
if (ok)
return retval;
/* Should not happen */
_crypt_output_magic(setting, output, size);
__set_errno(EINVAL); /* pretend we don't support this hash type */
return NULL;
}
char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
if (size < 16 || output_size < 7 + 22 + 1 ||
(count && (count < 4 || count > 31)) ||
prefix[0] != '$' || prefix[1] != '2' ||
(prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) {
if (output_size > 0) output[0] = '\0';
__set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL);
return NULL;
}
if (!count) count = 5;
output[0] = '$';
output[1] = '2';
output[2] = prefix[2];
output[3] = '$';
output[4] = '0' + count / 10;
output[5] = '0' + count % 10;
output[6] = '$';
BF_encode(&output[7], (const BF_word *)input, 16);
output[7 + 22] = '\0';
return output;
}

View File

@@ -0,0 +1,27 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#ifndef _CRYPT_BLOWFISH_H
#define _CRYPT_BLOWFISH_H
extern int _crypt_output_magic(const char *setting, char *output, int size);
extern char *_crypt_blowfish_rn(const char *key, const char *setting,
char *output, int size);
extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
unsigned long count,
const char *input, int size, char *output, int output_size);
#endif

View File

@@ -0,0 +1,128 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*
* This file contains salt generation functions for the traditional and
* other common crypt(3) algorithms, except for bcrypt which is defined
* entirely in crypt_blowfish.c.
*/
#include <string.h>
#include <errno.h>
#ifndef __set_errno
#define __set_errno(val) errno = (val)
#endif
/* Just to make sure the prototypes match the actual definitions */
#ifdef _WIN32
#include "crypt_gensalt.h"
#else
#include "crypt_gensalt.h"
#endif
unsigned char _crypt_itoa64[64 + 1] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
char *_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
(void) prefix;
if (size < 2 || output_size < 2 + 1 || (count && count != 25)) {
if (output_size > 0) output[0] = '\0';
__set_errno((output_size < 2 + 1) ? ERANGE : EINVAL);
return NULL;
}
output[0] = _crypt_itoa64[(unsigned int)input[0] & 0x3f];
output[1] = _crypt_itoa64[(unsigned int)input[1] & 0x3f];
output[2] = '\0';
return output;
}
char *_crypt_gensalt_extended_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
unsigned long value;
(void) prefix;
/* Even iteration counts make it easier to detect weak DES keys from a look
* at the hash, so they should be avoided */
if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
(count && (count > 0xffffff || !(count & 1)))) {
if (output_size > 0) output[0] = '\0';
__set_errno((output_size < 1 + 4 + 4 + 1) ? ERANGE : EINVAL);
return NULL;
}
if (!count) count = 725;
output[0] = '_';
output[1] = _crypt_itoa64[count & 0x3f];
output[2] = _crypt_itoa64[(count >> 6) & 0x3f];
output[3] = _crypt_itoa64[(count >> 12) & 0x3f];
output[4] = _crypt_itoa64[(count >> 18) & 0x3f];
value = (unsigned long)(unsigned char)input[0] |
((unsigned long)(unsigned char)input[1] << 8) |
((unsigned long)(unsigned char)input[2] << 16);
output[5] = _crypt_itoa64[value & 0x3f];
output[6] = _crypt_itoa64[(value >> 6) & 0x3f];
output[7] = _crypt_itoa64[(value >> 12) & 0x3f];
output[8] = _crypt_itoa64[(value >> 18) & 0x3f];
output[9] = '\0';
return output;
}
char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
unsigned long value;
(void) prefix;
if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000)) {
if (output_size > 0) output[0] = '\0';
__set_errno((output_size < 3 + 4 + 1) ? ERANGE : EINVAL);
return NULL;
}
output[0] = '$';
output[1] = '1';
output[2] = '$';
value = (unsigned long)(unsigned char)input[0] |
((unsigned long)(unsigned char)input[1] << 8) |
((unsigned long)(unsigned char)input[2] << 16);
output[3] = _crypt_itoa64[value & 0x3f];
output[4] = _crypt_itoa64[(value >> 6) & 0x3f];
output[5] = _crypt_itoa64[(value >> 12) & 0x3f];
output[6] = _crypt_itoa64[(value >> 18) & 0x3f];
output[7] = '\0';
if (size >= 6 && output_size >= 3 + 4 + 4 + 1) {
value = (unsigned long)(unsigned char)input[3] |
((unsigned long)(unsigned char)input[4] << 8) |
((unsigned long)(unsigned char)input[5] << 16);
output[7] = _crypt_itoa64[value & 0x3f];
output[8] = _crypt_itoa64[(value >> 6) & 0x3f];
output[9] = _crypt_itoa64[(value >> 12) & 0x3f];
output[10] = _crypt_itoa64[(value >> 18) & 0x3f];
output[11] = '\0';
}
return output;
}

View File

@@ -0,0 +1,30 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#ifndef _CRYPT_GENSALT_H
#define _CRYPT_GENSALT_H
extern unsigned char _crypt_itoa64[];
extern char *_crypt_gensalt_traditional_rn(const char *prefix,
unsigned long count,
const char *input, int size, char *output, int output_size);
extern char *_crypt_gensalt_extended_rn(const char *prefix,
unsigned long count,
const char *input, int size, char *output, int output_size);
extern char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size);
#endif

View File

@@ -0,0 +1,43 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2011.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#ifndef _OW_CRYPT_H
#define _OW_CRYPT_H
#ifndef __GNUC__
#undef __const
#define __const const
#endif
#ifndef __SKIP_GNU
extern char *crypt(__const char *key, __const char *setting);
extern char *crypt_r(__const char *key, __const char *setting, void *data);
#endif
#ifndef __SKIP_OW
extern char *crypt_rn(__const char *key, __const char *setting,
void *data, int size);
extern char *crypt_ra(__const char *key, __const char *setting,
void **data, int *size);
extern char *crypt_gensalt(__const char *prefix, unsigned long count,
__const char *input, int size);
extern char *crypt_gensalt_rn(__const char *prefix, unsigned long count,
__const char *input, int size, char *output, int output_size);
extern char *crypt_gensalt_ra(__const char *prefix, unsigned long count,
__const char *input, int size);
#endif
#endif

View File

@@ -0,0 +1,27 @@
#ifndef __WIN_BCRYPT__H
#define __WIN_BCRYPT__H
#include <iostream>
#include "crypt_blowfish.h"
#include "bcrypt.h"
class BCrypt {
public:
static std::string generateHash(const std::string & password, int workload = 12) {
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int ret;
ret = bcrypt_gensalt(workload, salt);
if (ret != 0)throw std::runtime_error{ "bcrypt: can not generate salt" };
ret = bcrypt_hashpw(password.c_str(), salt, hash);
if (ret != 0)throw std::runtime_error{ "bcrypt: can not generate hash" };
return std::string{ hash };
}
static bool validatePassword(const std::string & password, const std::string & hash) {
return (bcrypt_checkpw(password.c_str(), hash.c_str()) == 0);
}
};
#endif

View File

@@ -0,0 +1,563 @@
/*
* Written by Solar Designer <solar at openwall.com> in 2000-2014.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 2000-2014 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifndef __set_errno
#define __set_errno(val) errno = (val)
#endif
#ifdef TEST
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
#ifdef TEST_THREADS
#include <pthread.h>
#endif
#endif
#define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1)
#define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1)
#if defined(__GLIBC__) && defined(_LIBC)
#define __SKIP_GNU
#endif
#ifdef _WIN32 | _WIN64
#include "ow-crypt.h"
#include "crypt_blowfish.h"
#include "crypt_gensalt.h"
#else
#include "ow-crypt.h"
#include "crypt_blowfish.h"
#include "crypt_gensalt.h"
#endif
#if defined(__GLIBC__) && defined(_LIBC)
/* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
#include "crypt.h"
extern char *__md5_crypt_r(const char *key, const char *salt,
char *buffer, int buflen);
/* crypt-entry.c needs to be patched to define __des_crypt_r rather than
* __crypt_r, and not define crypt_r and crypt at all */
extern char *__des_crypt_r(const char *key, const char *salt,
struct crypt_data *data);
extern struct crypt_data _ufc_foobar;
#endif
static int _crypt_data_alloc(void **data, int *size, int need)
{
void *updated;
if (*data && *size >= need) return 0;
updated = realloc(*data, need);
if (!updated) {
#ifndef __GLIBC__
/* realloc(3) on glibc sets errno, so we don't need to bother */
__set_errno(ENOMEM);
#endif
return -1;
}
#if defined(__GLIBC__) && defined(_LIBC)
if (need >= sizeof(struct crypt_data))
((struct crypt_data *)updated)->initialized = 0;
#endif
*data = updated;
*size = need;
return 0;
}
static char *_crypt_retval_magic(char *retval, const char *setting,
char *output, int size)
{
if (retval)
return retval;
if (_crypt_output_magic(setting, output, size))
return NULL; /* shouldn't happen */
return output;
}
#if defined(__GLIBC__) && defined(_LIBC)
/*
* Applications may re-use the same instance of struct crypt_data without
* resetting the initialized field in order to let crypt_r() skip some of
* its initialization code. Thus, it is important that our multiple hashing
* algorithms either don't conflict with each other in their use of the
* data area or reset the initialized field themselves whenever required.
* Currently, the hashing algorithms simply have no conflicts: the first
* field of struct crypt_data is the 128-byte large DES key schedule which
* __des_crypt_r() calculates each time it is called while the two other
* hashing algorithms use less than 128 bytes of the data area.
*/
char *__crypt_rn(__const char *key, __const char *setting,
void *data, int size)
{
if (setting[0] == '$' && setting[1] == '2')
return _crypt_blowfish_rn(key, setting, (char *)data, size);
if (setting[0] == '$' && setting[1] == '1')
return __md5_crypt_r(key, setting, (char *)data, size);
if (setting[0] == '$' || setting[0] == '_') {
__set_errno(EINVAL);
return NULL;
}
if (size >= sizeof(struct crypt_data))
return __des_crypt_r(key, setting, (struct crypt_data *)data);
__set_errno(ERANGE);
return NULL;
}
char *__crypt_ra(__const char *key, __const char *setting,
void **data, int *size)
{
if (setting[0] == '$' && setting[1] == '2') {
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
return NULL;
return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
}
if (setting[0] == '$' && setting[1] == '1') {
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
return NULL;
return __md5_crypt_r(key, setting, (char *)*data, *size);
}
if (setting[0] == '$' || setting[0] == '_') {
__set_errno(EINVAL);
return NULL;
}
if (_crypt_data_alloc(data, size, sizeof(struct crypt_data)))
return NULL;
return __des_crypt_r(key, setting, (struct crypt_data *)*data);
}
char *__crypt_r(__const char *key, __const char *setting,
struct crypt_data *data)
{
return _crypt_retval_magic(
__crypt_rn(key, setting, data, sizeof(*data)),
setting, (char *)data, sizeof(*data));
}
char *__crypt(__const char *key, __const char *setting)
{
return _crypt_retval_magic(
__crypt_rn(key, setting, &_ufc_foobar, sizeof(_ufc_foobar)),
setting, (char *)&_ufc_foobar, sizeof(_ufc_foobar));
}
#else
char *crypt_rn(const char *key, const char *setting, void *data, int size)
{
return _crypt_blowfish_rn(key, setting, (char *)data, size);
}
char *crypt_ra(const char *key, const char *setting,
void **data, int *size)
{
if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
return NULL;
return _crypt_blowfish_rn(key, setting, (char *)*data, *size);
}
char *crypt_r(const char *key, const char *setting, void *data)
{
return _crypt_retval_magic(
crypt_rn(key, setting, data, CRYPT_OUTPUT_SIZE),
setting, (char *)data, CRYPT_OUTPUT_SIZE);
}
char *crypt(const char *key, const char *setting)
{
static char output[CRYPT_OUTPUT_SIZE];
return _crypt_retval_magic(
crypt_rn(key, setting, output, sizeof(output)),
setting, output, sizeof(output));
}
#define __crypt_gensalt_rn crypt_gensalt_rn
#define __crypt_gensalt_ra crypt_gensalt_ra
#define __crypt_gensalt crypt_gensalt
#endif
char *__crypt_gensalt_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size)
{
char *(*use)(const char *_prefix, unsigned long _count,
const char *_input, int _size,
char *_output, int _output_size);
/* This may be supported on some platforms in the future */
if (!input) {
__set_errno(EINVAL);
return NULL;
}
if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2b$", 4) ||
!strncmp(prefix, "$2y$", 4))
use = _crypt_gensalt_blowfish_rn;
else
if (!strncmp(prefix, "$1$", 3))
use = _crypt_gensalt_md5_rn;
else
if (prefix[0] == '_')
use = _crypt_gensalt_extended_rn;
else
if (!prefix[0] ||
(prefix[0] && prefix[1] &&
memchr(_crypt_itoa64, prefix[0], 64) &&
memchr(_crypt_itoa64, prefix[1], 64)))
use = _crypt_gensalt_traditional_rn;
else {
__set_errno(EINVAL);
return NULL;
}
return use(prefix, count, input, size, output, output_size);
}
char *__crypt_gensalt_ra(const char *prefix, unsigned long count,
const char *input, int size)
{
char output[CRYPT_GENSALT_OUTPUT_SIZE];
char *retval;
retval = __crypt_gensalt_rn(prefix, count,
input, size, output, sizeof(output));
if (retval) {
#ifdef _WIN32 | _WIN64
retval = _strdup(retval);
#else
retval = strdup(retval);
#endif
#ifndef __GLIBC__
/* strdup(3) on glibc sets errno, so we don't need to bother */
if (!retval)
__set_errno(ENOMEM);
#endif
}
return retval;
}
char *__crypt_gensalt(const char *prefix, unsigned long count,
const char *input, int size)
{
static char output[CRYPT_GENSALT_OUTPUT_SIZE];
return __crypt_gensalt_rn(prefix, count,
input, size, output, sizeof(output));
}
#if defined(__GLIBC__) && defined(_LIBC)
weak_alias(__crypt_rn, crypt_rn)
weak_alias(__crypt_ra, crypt_ra)
weak_alias(__crypt_r, crypt_r)
weak_alias(__crypt, crypt)
weak_alias(__crypt_gensalt_rn, crypt_gensalt_rn)
weak_alias(__crypt_gensalt_ra, crypt_gensalt_ra)
weak_alias(__crypt_gensalt, crypt_gensalt)
weak_alias(crypt, fcrypt)
#endif
#ifdef TEST
static const char *tests[][3] = {
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW",
"U*U"},
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK",
"U*U*"},
{"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a",
"U*U*U"},
{"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui",
"0123456789abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"chars after 72 are ignored"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
"\xa3"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
"\xff\xff\xa3"},
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
"\xff\xff\xa3"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nqd1wy.pTMdcvrRWxyiGL2eMz.2a85.",
"\xff\xff\xa3"},
{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e",
"\xff\xff\xa3"},
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
"\xa3"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
"\xa3"},
{"$2b$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq",
"\xa3"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
"1\xa3" "345"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
"\xff\xa3" "345"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi",
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.ZC1JEJ8Z4gPfpe1JOr/oyPXTWl9EFd.",
"\xff\xa3" "34" "\xff\xff\xff\xa3" "345"},
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
"\xff\xa3" "345"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e",
"\xff\xa3" "345"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
"\xa3" "ab"},
{"$2x$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
"\xa3" "ab"},
{"$2y$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS",
"\xa3" "ab"},
{"$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS",
"\xd1\x91"},
{"$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS",
"\xd0\xc1\xd2\xcf\xcc\xd8"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6",
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
"chars after 72 are ignored as usual"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy",
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"
"\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"},
{"$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe",
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"
"\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"},
{"$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy",
""},
{"*0", "", "$2a$03$CCCCCCCCCCCCCCCCCCCCC."},
{"*0", "", "$2a$32$CCCCCCCCCCCCCCCCCCCCC."},
{"*0", "", "$2c$05$CCCCCCCCCCCCCCCCCCCCC."},
{"*0", "", "$2z$05$CCCCCCCCCCCCCCCCCCCCC."},
{"*0", "", "$2`$05$CCCCCCCCCCCCCCCCCCCCC."},
{"*0", "", "$2{$05$CCCCCCCCCCCCCCCCCCCCC."},
{"*1", "", "*0"},
{NULL}
};
#define which tests[0]
static volatile sig_atomic_t running;
static void handle_timer(int signum)
{
(void) signum;
running = 0;
}
static void *run(void *arg)
{
unsigned long count = 0;
int i = 0;
void *data = NULL;
int size = 0x12345678;
do {
const char *hash = tests[i][0];
const char *key = tests[i][1];
const char *setting = tests[i][2];
if (!tests[++i][0])
i = 0;
if (setting && strlen(hash) < 30) /* not for benchmark */
continue;
if (strcmp(crypt_ra(key, hash, &data, &size), hash)) {
printf("%d: FAILED (crypt_ra/%d/%lu)\n",
(int)((char *)arg - (char *)0), i, count);
free(data);
return NULL;
}
count++;
} while (running);
free(data);
return count + (char *)0;
}
int main(void)
{
struct itimerval it;
struct tms buf;
clock_t clk_tck, start_real, start_virtual, end_real, end_virtual;
unsigned long count;
void *data;
int size;
char *setting1, *setting2;
int i;
#ifdef TEST_THREADS
pthread_t t[TEST_THREADS];
void *t_retval;
#endif
data = NULL;
size = 0x12345678;
for (i = 0; tests[i][0]; i++) {
const char *hash = tests[i][0];
const char *key = tests[i][1];
const char *setting = tests[i][2];
const char *p;
int ok = !setting || strlen(hash) >= 30;
int o_size;
char s_buf[30], o_buf[61];
if (!setting) {
memcpy(s_buf, hash, sizeof(s_buf) - 1);
s_buf[sizeof(s_buf) - 1] = 0;
setting = s_buf;
}
__set_errno(0);
p = crypt(key, setting);
if ((!ok && !errno) || strcmp(p, hash)) {
printf("FAILED (crypt/%d)\n", i);
return 1;
}
if (ok && strcmp(crypt(key, hash), hash)) {
printf("FAILED (crypt/%d)\n", i);
return 1;
}
for (o_size = -1; o_size <= (int)sizeof(o_buf); o_size++) {
int ok_n = ok && o_size == (int)sizeof(o_buf);
const char *x = "abc";
strcpy(o_buf, x);
if (o_size >= 3) {
x = "*0";
if (setting[0] == '*' && setting[1] == '0')
x = "*1";
}
__set_errno(0);
p = crypt_rn(key, setting, o_buf, o_size);
if ((ok_n && (!p || strcmp(p, hash))) ||
(!ok_n && (!errno || p || strcmp(o_buf, x)))) {
printf("FAILED (crypt_rn/%d)\n", i);
return 1;
}
}
__set_errno(0);
p = crypt_ra(key, setting, &data, &size);
if ((ok && (!p || strcmp(p, hash))) ||
(!ok && (!errno || p || strcmp((char *)data, hash)))) {
printf("FAILED (crypt_ra/%d)\n", i);
return 1;
}
}
setting1 = crypt_gensalt(which[0], 12, data, size);
if (!setting1 || strncmp(setting1, "$2a$12$", 7)) {
puts("FAILED (crypt_gensalt)\n");
return 1;
}
setting2 = crypt_gensalt_ra(setting1, 12, data, size);
if (strcmp(setting1, setting2)) {
puts("FAILED (crypt_gensalt_ra/1)\n");
return 1;
}
(*(char *)data)++;
setting1 = crypt_gensalt_ra(setting2, 12, data, size);
if (!strcmp(setting1, setting2)) {
puts("FAILED (crypt_gensalt_ra/2)\n");
return 1;
}
free(setting1);
free(setting2);
free(data);
#if defined(_SC_CLK_TCK) || !defined(CLK_TCK)
clk_tck = sysconf(_SC_CLK_TCK);
#else
clk_tck = CLK_TCK;
#endif
running = 1;
signal(SIGALRM, handle_timer);
memset(&it, 0, sizeof(it));
it.it_value.tv_sec = 5;
setitimer(ITIMER_REAL, &it, NULL);
start_real = times(&buf);
start_virtual = buf.tms_utime + buf.tms_stime;
count = (char *)run((char *)0) - (char *)0;
end_real = times(&buf);
end_virtual = buf.tms_utime + buf.tms_stime;
if (end_virtual == start_virtual) end_virtual++;
printf("%.1f c/s real, %.1f c/s virtual\n",
(float)count * clk_tck / (end_real - start_real),
(float)count * clk_tck / (end_virtual - start_virtual));
#ifdef TEST_THREADS
running = 1;
it.it_value.tv_sec = 60;
setitimer(ITIMER_REAL, &it, NULL);
start_real = times(&buf);
for (i = 0; i < TEST_THREADS; i++)
if (pthread_create(&t[i], NULL, run, i + (char *)0)) {
perror("pthread_create");
return 1;
}
for (i = 0; i < TEST_THREADS; i++) {
if (pthread_join(t[i], &t_retval)) {
perror("pthread_join");
continue;
}
if (!t_retval) continue;
count = (char *)t_retval - (char *)0;
end_real = times(&buf);
printf("%d: %.1f c/s real\n", i,
(float)count * clk_tck / (end_real - start_real));
}
#endif
return 0;
}
#endif

202
src/contrib/bcrypt/x86.S Normal file
View File

@@ -0,0 +1,202 @@
/*
* Written by Solar Designer <solar at openwall.com> in 1998-2010.
* No copyright is claimed, and the software is hereby placed in the public
* domain. In case this attempt to disclaim copyright and place the software
* in the public domain is deemed null and void, then the software is
* Copyright (c) 1998-2010 Solar Designer and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See crypt_blowfish.c for more information.
*/
#ifdef __i386__
#if defined(__OpenBSD__) && !defined(__ELF__)
#define UNDERSCORES
#define ALIGN_LOG
#endif
#if defined(__CYGWIN32__) || defined(__MINGW32__)
#define UNDERSCORES
#endif
#ifdef __DJGPP__
#define UNDERSCORES
#define ALIGN_LOG
#endif
#ifdef UNDERSCORES
#define _BF_body_r __BF_body_r
#endif
#ifdef ALIGN_LOG
#define DO_ALIGN(log) .align (log)
#elif defined(DUMBAS)
#define DO_ALIGN(log) .align 1 << log
#else
#define DO_ALIGN(log) .align (1 << (log))
#endif
#define BF_FRAME 0x200
#define ctx %esp
#define BF_ptr (ctx)
#define S(N, r) N+BF_FRAME(ctx,r,4)
#ifdef DUMBAS
#define P(N) 0x1000+N+N+N+N+BF_FRAME(ctx)
#else
#define P(N) 0x1000+4*N+BF_FRAME(ctx)
#endif
/*
* This version of the assembly code is optimized primarily for the original
* Intel Pentium but is also careful to avoid partial register stalls on the
* Pentium Pro family of processors (tested up to Pentium III Coppermine).
*
* It is possible to do 15% faster on the Pentium Pro family and probably on
* many non-Intel x86 processors, but, unfortunately, that would make things
* twice slower for the original Pentium.
*
* An additional 2% speedup may be achieved with non-reentrant code.
*/
#define L %esi
#define R %edi
#define tmp1 %eax
#define tmp1_lo %al
#define tmp2 %ecx
#define tmp2_hi %ch
#define tmp3 %edx
#define tmp3_lo %dl
#define tmp4 %ebx
#define tmp4_hi %bh
#define tmp5 %ebp
.text
#define BF_ROUND(L, R, N) \
xorl L,tmp2; \
xorl tmp1,tmp1; \
movl tmp2,L; \
shrl $16,tmp2; \
movl L,tmp4; \
movb tmp2_hi,tmp1_lo; \
andl $0xFF,tmp2; \
movb tmp4_hi,tmp3_lo; \
andl $0xFF,tmp4; \
movl S(0,tmp1),tmp1; \
movl S(0x400,tmp2),tmp5; \
addl tmp5,tmp1; \
movl S(0x800,tmp3),tmp5; \
xorl tmp5,tmp1; \
movl S(0xC00,tmp4),tmp5; \
addl tmp1,tmp5; \
movl 4+P(N),tmp2; \
xorl tmp5,R
#define BF_ENCRYPT_START \
BF_ROUND(L, R, 0); \
BF_ROUND(R, L, 1); \
BF_ROUND(L, R, 2); \
BF_ROUND(R, L, 3); \
BF_ROUND(L, R, 4); \
BF_ROUND(R, L, 5); \
BF_ROUND(L, R, 6); \
BF_ROUND(R, L, 7); \
BF_ROUND(L, R, 8); \
BF_ROUND(R, L, 9); \
BF_ROUND(L, R, 10); \
BF_ROUND(R, L, 11); \
BF_ROUND(L, R, 12); \
BF_ROUND(R, L, 13); \
BF_ROUND(L, R, 14); \
BF_ROUND(R, L, 15); \
movl BF_ptr,tmp5; \
xorl L,tmp2; \
movl P(17),L
#define BF_ENCRYPT_END \
xorl R,L; \
movl tmp2,R
DO_ALIGN(5)
.globl _BF_body_r
_BF_body_r:
movl 4(%esp),%eax
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
subl $BF_FRAME-8,%eax
xorl L,L
cmpl %esp,%eax
ja BF_die
xchgl %eax,%esp
xorl R,R
pushl %eax
leal 0x1000+BF_FRAME-4(ctx),%eax
movl 0x1000+BF_FRAME-4(ctx),tmp2
pushl %eax
xorl tmp3,tmp3
BF_loop_P:
BF_ENCRYPT_START
addl $8,tmp5
BF_ENCRYPT_END
leal 0x1000+18*4+BF_FRAME(ctx),tmp1
movl tmp5,BF_ptr
cmpl tmp5,tmp1
movl L,-8(tmp5)
movl R,-4(tmp5)
movl P(0),tmp2
ja BF_loop_P
leal BF_FRAME(ctx),tmp5
xorl tmp3,tmp3
movl tmp5,BF_ptr
BF_loop_S:
BF_ENCRYPT_START
BF_ENCRYPT_END
movl P(0),tmp2
movl L,(tmp5)
movl R,4(tmp5)
BF_ENCRYPT_START
BF_ENCRYPT_END
movl P(0),tmp2
movl L,8(tmp5)
movl R,12(tmp5)
BF_ENCRYPT_START
BF_ENCRYPT_END
movl P(0),tmp2
movl L,16(tmp5)
movl R,20(tmp5)
BF_ENCRYPT_START
addl $32,tmp5
BF_ENCRYPT_END
leal 0x1000+BF_FRAME(ctx),tmp1
movl tmp5,BF_ptr
cmpl tmp5,tmp1
movl P(0),tmp2
movl L,-8(tmp5)
movl R,-4(tmp5)
ja BF_loop_S
movl 4(%esp),%esp
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
BF_die:
/* Oops, need to re-compile with a larger BF_FRAME. */
hlt
jmp BF_die
#if defined(__ELF__) && defined(__linux__)
.section .note.GNU-stack,"",@progbits
#endif
#endif

230517
src/contrib/sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

12174
src/contrib/sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,46 +2,98 @@
#include "CNShardServer.hpp"
#include "PlayerManager.hpp"
#include "ChatManager.hpp"
#include "CombatManager.hpp"
#include "ItemManager.hpp"
#include "MissionManager.hpp"
#include "NanoManager.hpp"
#include "NPCManager.hpp"
#include "TransportManager.hpp"
#include "Database.hpp"
#include "settings.hpp"
#ifdef __MINGW32__
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
#include "mingw/mingw.thread.h"
#else
#include <thread>
#include <thread>
#endif
#include <string>
#include <signal.h>
void startLogin(uint16_t port) {
CNLoginServer server(port);
server.start();
CNShardServer *shardServer;
std::thread *shardThread;
void startShard(CNShardServer* server) {
server->start();
}
void startShard(uint16_t port) {
CNShardServer server(port);
server.start();
#ifndef _WIN32
// terminate gracefully on SIGINT (for gprof)
void terminate(int arg) {
std::cout << "OpenFusion: terminating." << std::endl;
shardServer->kill();
shardThread->join();
exit(0);
}
void initsignals() {
struct sigaction act;
memset((void*)&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
// tell the OS to not kill us if you use a broken pipe, just let us know thru recv() or send()
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, NULL) < 0) {
perror("sigaction");
exit(1);
}
act.sa_handler = terminate;
if (sigaction(SIGINT, &act, NULL) < 0) {
perror("sigaction");
exit(1);
}
}
#endif
int main() {
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
std::cerr << "OpenFusion: WSAStartup failed" << std::endl;
exit(EXIT_FAILURE);
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
std::cerr << "OpenFusion: WSAStartup failed" << std::endl;
exit(EXIT_FAILURE);
}
#else
initsignals();
#endif
settings::init();
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
PlayerManager::init();
ChatManager::init();
CombatManager::init();
ItemManager::init();
MissionManager::init();
NanoManager::init();
NPCManager::init();
TransportManager::init();
Database::open();
std::cout << "[INFO] Starting Server Threads..." << std::endl;
std::thread loginThread(startLogin, settings::LOGINPORT);
std::thread shardThread(startShard, settings::SHARDPORT);
getchar(); // blocks until input
CNLoginServer loginServer(settings::LOGINPORT);
shardServer = new CNShardServer(settings::SHARDPORT);
shardThread = new std::thread(startShard, (CNShardServer*)shardServer);
loginServer.start();
shardServer->kill();
shardThread->join();
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
}

View File

@@ -1,20 +1,29 @@
#include <iostream>
#include "settings.hpp"
#include "INIReader.hpp"
#include "contrib/INIReader.hpp"
// defaults :)
int settings::VERBOSITY = 1;
int settings::LOGINPORT = 8001;
bool settings::LOGINRANDCHARACTERS = false;
bool settings::APPROVEALLNAMES = true;
int settings::SHARDPORT = 8002;
std::string settings::SHARDSERVERIP = "127.0.0.1";
int settings::VIEWDISTANCE = 25000;
int settings::PLAYERDISTANCE = 20000;
int settings::NPCDISTANCE = 16000;
// default spawn point is city hall
int settings::SPAWN_X = 179213;
int settings::SPAWN_Y = 268451;
int settings::SPAWN_Z = -4210;
std::string settings::GMPASS = "pass";
std::string settings::NPCJSON = "data/NPCs.json";
std::string settings::WARPJSON = "data/warps.json";
std::string settings::MOBJSON = "data/mobs.json";
std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
bool settings::GM = false;
void settings::init() {
INIReader reader("config.ini");
@@ -28,13 +37,21 @@ void settings::init() {
return;
}
APPROVEALLNAMES = reader.GetBoolean("", "acceptallcustomnames", APPROVEALLNAMES);
VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY);
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
LOGINRANDCHARACTERS = reader.GetBoolean("login", "randomcharacters", LOGINRANDCHARACTERS);
SHARDPORT = reader.GetInteger("shard", "port", SHARDPORT);
SHARDSERVERIP = reader.Get("shard", "ip", "127.0.0.1");
VIEWDISTANCE = reader.GetInteger("shard", "view", VIEWDISTANCE);
PLAYERDISTANCE = reader.GetInteger("shard", "playerdistance", PLAYERDISTANCE);
NPCDISTANCE = reader.GetInteger("shard", "npcdistance", NPCDISTANCE);
SPAWN_X = reader.GetInteger("shard", "spawnx", SPAWN_X);
SPAWN_Y = reader.GetInteger("shard", "spawny", SPAWN_Y);
SPAWN_Z = reader.GetInteger("shard", "spawnz", SPAWN_Z);
}
GMPASS = reader.Get("login", "pass", GMPASS);
NPCJSON = reader.Get("shard", "npcdata", NPCJSON);
WARPJSON = reader.Get("shard", "warpdata", WARPJSON);
MOBJSON = reader.Get("shard", "mobdata", MOBJSON);
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
GM = reader.GetBoolean("shard", "gm", GM);
}

View File

@@ -1,17 +1,23 @@
#ifndef _SETT_HPP
#define _SETT_HPP
#pragma once
namespace settings {
extern int VERBOSITY;
extern int LOGINPORT;
extern bool LOGINRANDCHARACTERS;
extern bool APPROVEALLNAMES;
extern int SHARDPORT;
extern std::string SHARDSERVERIP;
extern int VIEWDISTANCE;
extern int PLAYERDISTANCE;
extern int NPCDISTANCE;
extern int SPAWN_X;
extern int SPAWN_Y;
extern int SPAWN_Z;
extern std::string MOTDSTRING;
extern std::string NPCJSON;
extern std::string WARPJSON;
extern std::string MOBJSON;
extern std::string GMPASS;
extern bool GM;
void init();
}
#endif

4843
src/structs/0104.hpp Normal file

File diff suppressed because it is too large Load Diff

4885
src/structs/0728.hpp Normal file

File diff suppressed because it is too large Load Diff