Compare commits

...

314 Commits

Author SHA1 Message Date
David Cernat 9029533ec1
Merge pull request #480 from testman42/patch-1
Update link to TES3MP section on OpenMW forums
6 years ago
Testman 7fadc533c7
Update link to TES3MP section on OpenMW forums
URL changed as TES3MP is no longer it's own section but instead falls under new umbrella section.

I think this is the smallest change I have ever made a pull request for.
6 years ago
David Cernat cf33695447 [General] Change rewrite's version to TBD-rewrite 7 years ago
David Cernat dbd6ccaf97
Merge pull request #455 from testman42/patch-1
Rename "loglevel" to "logLevel"
7 years ago
Testman 702d5b44c9
Rename "loglevel" to "logLevel"
Which should make server browser and not crash the client.
7 years ago
David Cernat 8b482e19ec Merge pull request #453 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Objects.cpp
	apps/openmw-mp/Script/Functions/Objects.hpp
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw-mp/Script/Functions/Worldstate.hpp
7 years ago
David Cernat d5b3d08ec9 Merge pull request #452 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw/mwmp/Worldstate.cpp
	components/openmw-mp/Base/BaseWorldstate.hpp
7 years ago
David Cernat 524363702c Merge pull request #451 from TES3MP/0.6.3 while resolving conflicts
# Conflicts:
#	apps/openmw-mp/Networking.cpp
#	apps/openmw-mp/main.cpp
#	apps/openmw-mp/processors/object/ProcessorObjectDelete.hpp
7 years ago
David Cernat b3c398605f
Merge pull request #448 from TES3MP/0.6.3
Add 0.6.3 commits up to 8 Jun 2018
7 years ago
David Cernat 176aa62b15 Merge pull request #446 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Cells.cpp
	apps/openmw-mp/Script/Functions/Cells.hpp
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw-mp/Script/Functions/Worldstate.hpp
	apps/openmw-mp/Script/ScriptFunctions.hpp
	apps/openmw-mp/processors/player/ProcessorPlayerMap.hpp
	apps/openmw/mwmp/processors/player/ProcessorPlayerMap.hpp
	components/openmw-mp/Base/BasePlayer.hpp
	components/openmw-mp/Packets/Player/PacketPlayerMap.hpp
	components/openmw-mp/Packets/Worldstate/PacketWorldMap.cpp
7 years ago
David Cernat 6bb2a7e24a Merge pull request #444 from TES3MP/0.6.3 while resolving conflicts 7 years ago
David Cernat 058d67dbd0 [Server] Use consistent code style and make all files end in newlines 7 years ago
David Cernat 49bf605e8e [Server] Create Worldstate class and implement associated table 7 years ago
David Cernat 07b781cd32 [Server] Fix variable name for actor table 7 years ago
Stanislav Zhukov 08915c44a0
Merge pull request #431 from Aesylwinn/Travis-master
Fix travis build on master
7 years ago
Kyle Cooley e12330b038 Undo increase to gcc/clang template depth 7 years ago
David Cernat 5c41350532 [Server] Fix function name for setting timeScale 7 years ago
David Cernat fc4d3fe3fa Merge pull request #441 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw-mp/Script/Functions/Worldstate.hpp
7 years ago
David Cernat 569243c987 [Browser] Use better phrasing for disclaimer 7 years ago
Koncord 7180b009bd [Browser] Add disclaimer to the main window
Add forgotten default Browser section in the client config
7 years ago
David Cernat 3f7e163c92 Merge pull request #439 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/CMakeLists.txt
	apps/openmw-mp/Script/Functions/GUI.cpp
	apps/openmw-mp/Script/Functions/GUI.hpp
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw-mp/Script/Functions/Worldstate.hpp
	apps/openmw-mp/processors/worldstate/ProcessorRecordDynamic.hpp
	apps/openmw/mwmp/processors/worldstate/ProcessorRecordDynamic.hpp
	components/CMakeLists.txt
	components/openmw-mp/Packets/Player/PacketGUIBoxes.cpp
7 years ago
David Cernat 7083cb0359 Merge pull request #437 from TES3MP/0.6.3 while resolving conflicts 7 years ago
David Cernat ad4214d3e2 Merge pull request #434 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Networking.cpp
	apps/openmw/mwmp/processors/worldstate/ProcessorGameTime.hpp
	components/CMakeLists.txt
	components/openmw-mp/Base/BasePlayer.hpp
	components/openmw-mp/Packets/Worldstate/PacketGameTime.hpp
7 years ago
Kyle Cooley 83fe29d10f Get rid of tab 7 years ago
Kyle Cooley 3a68f4bd8f Fix travis build on master
- add luajit
- remove terra/callff
- fix template issue
- add missing include
- search for sol in extern
7 years ago
David Cernat 8a393d2984 Merge pull request #430 from TES3MP/0.6.3 while resolving conflicts
# Conflicts:
#	apps/openmw-mp/Networking.cpp
#	apps/openmw-mp/Networking.hpp
#	components/CMakeLists.txt
7 years ago
David Cernat b5922d18fd Merge pull request #428 from TES3MP/0.6.3 while resolving conflicts
# Conflicts:
#   apps/openmw-mp/Networking.cpp
#   apps/openmw-mp/Script/Functions/Objects.cpp
#   apps/openmw-mp/Script/Functions/Objects.hpp
#   apps/openmw-mp/processors/ObjectProcessor.cpp
#   apps/openmw-mp/processors/object/ProcessorContainer.hpp
#   apps/openmw-mp/processors/object/ProcessorDoorState.hpp
#   apps/openmw-mp/processors/object/ProcessorMusicPlay.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectAnimPlay.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectDelete.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectLock.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectMove.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectPlace.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectRotate.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectScale.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectSpawn.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectState.hpp
#   apps/openmw-mp/processors/object/ProcessorObjectTrap.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptGlobalFloat.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptGlobalShort.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptLocalFloat.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptLocalShort.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptMemberFloat.hpp
#   apps/openmw-mp/processors/object/ProcessorScriptMemberShort.hpp
#   apps/openmw-mp/processors/object/ProcessorVideoPlay.hpp
#   apps/openmw/mwmp/Networking.cpp
#   apps/openmw/mwmp/processors/object/BaseObjectProcessor.hpp
#   apps/openmw/mwmp/processors/object/ProcessorMusicPlay.hpp
#   apps/openmw/mwmp/processors/object/ProcessorScriptGlobalFloat.hpp
#   apps/openmw/mwmp/processors/object/ProcessorScriptGlobalShort.hpp
#   apps/openmw/mwmp/processors/object/ProcessorScriptMemberFloat.hpp
#   apps/openmw/mwmp/processors/object/ProcessorScriptMemberShort.hpp
#   apps/openmw/mwmp/processors/object/ProcessorVideoPlay.hpp
7 years ago
David Cernat 98eece808b Merge pull request #427 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/CMakeLists.txt
	apps/openmw-mp/Script/Functions/Objects.cpp
	apps/openmw-mp/Script/Functions/Objects.hpp
	apps/openmw-mp/Script/ScriptFunctions.hpp
7 years ago
David Cernat d4dbfdfdb6 Merge pull request #423 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Networking.cpp
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw-mp/Script/Functions/World.hpp
	apps/openmw-mp/processors/WorldProcessor.cpp
	apps/openmw-mp/processors/WorldProcessor.hpp
	apps/openmw-mp/processors/world/ProcessorContainer.hpp
	apps/openmw-mp/processors/world/ProcessorDoorState.hpp
	apps/openmw-mp/processors/world/ProcessorObjectDelete.hpp
	apps/openmw-mp/processors/world/ProcessorObjectLock.hpp
	apps/openmw-mp/processors/world/ProcessorObjectPlace.hpp
	apps/openmw-mp/processors/world/ProcessorObjectScale.hpp
	apps/openmw-mp/processors/world/ProcessorObjectSpawn.hpp
	apps/openmw-mp/processors/world/ProcessorObjectState.hpp
	apps/openmw-mp/processors/world/ProcessorObjectTrap.hpp
	apps/openmw/mwgui/container.cpp
	apps/openmw/mwmp/Networking.cpp
	apps/openmw/mwmp/ObjectList.cpp
	apps/openmw/mwmp/ObjectList.hpp
	apps/openmw/mwmp/processors/world/ProcessorContainer.hpp
	components/CMakeLists.txt
	components/openmw-mp/Base/BaseObject.hpp
	components/openmw-mp/Packets/Object/ObjectPacket.cpp
	components/openmw-mp/Packets/Object/PacketConsoleCommand.cpp
	components/openmw-mp/Packets/Object/PacketContainer.cpp
	components/openmw-mp/Packets/Object/PacketDoorState.hpp
	components/openmw-mp/Packets/Object/PacketMusicPlay.hpp
	components/openmw-mp/Packets/Object/PacketObjectAnimPlay.hpp
	components/openmw-mp/Packets/Object/PacketObjectLock.hpp
	components/openmw-mp/Packets/Object/PacketObjectMove.hpp
	components/openmw-mp/Packets/Object/PacketObjectPlace.hpp
	components/openmw-mp/Packets/Object/PacketObjectRotate.hpp
	components/openmw-mp/Packets/Object/PacketObjectScale.hpp
	components/openmw-mp/Packets/Object/PacketObjectSpawn.hpp
	components/openmw-mp/Packets/Object/PacketObjectState.hpp
	components/openmw-mp/Packets/Object/PacketObjectTrap.hpp
	components/openmw-mp/Packets/Object/PacketScriptGlobalShort.hpp
	components/openmw-mp/Packets/Object/PacketScriptLocalFloat.hpp
	components/openmw-mp/Packets/Object/PacketScriptLocalShort.hpp
	components/openmw-mp/Packets/Object/PacketScriptMemberShort.hpp
	components/openmw-mp/Packets/Object/PacketVideoPlay.hpp
7 years ago
David Cernat 672bb707a7 Merge pull request #422 from TES3MP/0.6.3 while resolving conflicts
# Conflicts:
#   apps/openmw-mp/Networking.cpp
#   apps/openmw-mp/Networking.hpp
#   apps/openmw-mp/Script/Functions/World.cpp
#   apps/openmw-mp/processors/WorldProcessor.cpp
#   apps/openmw-mp/processors/WorldProcessor.hpp
#   apps/openmw-mp/processors/world/ProcessorContainer.hpp
#   apps/openmw-mp/processors/world/ProcessorDoorState.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectDelete.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectLock.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectPlace.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectScale.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectSpawn.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectState.hpp
#   apps/openmw-mp/processors/world/ProcessorObjectTrap.hpp
#   apps/openmw/mwmp/Networking.cpp
#   components/CMakeLists.txt
#   components/openmw-mp/Controllers/ObjectPacketController.hpp
#   components/openmw-mp/Controllers/WorldPacketController.cpp
#   components/openmw-mp/Packets/Object/ObjectPacket.cpp
#   components/openmw-mp/Packets/Object/ObjectPacket.hpp
#   components/openmw-mp/Packets/Object/PacketConsoleCommand.hpp
#   components/openmw-mp/Packets/Object/PacketContainer.hpp
#   components/openmw-mp/Packets/Object/PacketDoorState.hpp
#   components/openmw-mp/Packets/Object/PacketMusicPlay.hpp
#   components/openmw-mp/Packets/Object/PacketObjectAnimPlay.hpp
#   components/openmw-mp/Packets/Object/PacketObjectDelete.hpp
#   components/openmw-mp/Packets/Object/PacketObjectLock.hpp
#   components/openmw-mp/Packets/Object/PacketObjectMove.hpp
#   components/openmw-mp/Packets/Object/PacketObjectPlace.hpp
#   components/openmw-mp/Packets/Object/PacketObjectRotate.hpp
#   components/openmw-mp/Packets/Object/PacketObjectScale.hpp
#   components/openmw-mp/Packets/Object/PacketObjectSpawn.hpp
#   components/openmw-mp/Packets/Object/PacketObjectState.hpp
#   components/openmw-mp/Packets/Object/PacketObjectTrap.hpp
#   components/openmw-mp/Packets/Object/PacketScriptGlobalShort.hpp
#   components/openmw-mp/Packets/Object/PacketScriptLocalFloat.hpp
#   components/openmw-mp/Packets/Object/PacketScriptLocalShort.hpp
#   components/openmw-mp/Packets/Object/PacketScriptMemberShort.hpp
#   components/openmw-mp/Packets/Object/PacketVideoPlay.hpp
7 years ago
David Cernat 7c8dd7380f Merge pull request #421 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Actors.cpp
	apps/openmw-mp/Script/Functions/Actors.hpp
	apps/openmw-mp/Script/Functions/Cells.cpp
7 years ago
David Cernat c5f33e451f Merge pull request #418 from TES3MP/0.6.3 while resolving conflicts 7 years ago
David Cernat 4846497078 Merge pull request #415 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Positions.cpp
	apps/openmw-mp/Script/Functions/Positions.hpp
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw-mp/Script/Functions/World.hpp
	apps/openmw-mp/Script/Script.hpp
	apps/openmw-mp/Script/ScriptFunctions.cpp
	components/openmw-mp/Base/BasePlayer.hpp
7 years ago
David Cernat 502751cae0 Merge pull request #413 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/processors/player/ProcessorPlayerCellChange.hpp
	components/openmw-mp/Base/BasePlayer.hpp
	components/openmw-mp/Packets/Player/PacketPlayerEquipment.hpp
	components/openmw-mp/Packets/Player/PacketPlayerSkill.cpp
7 years ago
David Cernat d58efde3f1 Merge pull request #410 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Actors.cpp
	apps/openmw-mp/Script/Functions/Chat.cpp
	apps/openmw-mp/Script/Functions/GUI.hpp
	apps/openmw-mp/Script/Functions/Items.cpp
	apps/openmw-mp/Script/Functions/Stats.cpp
	apps/openmw-mp/Script/Functions/Stats.hpp
	apps/openmw-mp/Script/ScriptFunctions.cpp
	apps/openmw-mp/Script/ScriptFunctions.hpp
	apps/openmw-mp/processors/player/ProcessorPlayerCharGen.hpp
	apps/openmw/mwmp/Cell.cpp
	apps/openmw/mwmp/DedicatedActor.cpp
	apps/openmw/mwmp/DedicatedPlayer.cpp
	apps/openmw/mwmp/LocalActor.cpp
	apps/openmw/mwmp/LocalPlayer.cpp
	apps/openmw/mwmp/LocalPlayer.hpp
	apps/openmw/mwmp/Main.cpp
	components/openmw-mp/Base/BaseActor.hpp
	components/openmw-mp/Base/BasePlayer.hpp
	components/openmw-mp/Packets/Player/PacketPlayerAttribute.cpp
	components/openmw-mp/Packets/Player/PacketPlayerSkill.cpp
7 years ago
David Cernat e355dca4dd Merge pull request #409 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/CMakeLists.txt
	apps/openmw-mp/Script/Functions/GUI.cpp
	apps/openmw-mp/Script/Functions/GUI.hpp
	apps/openmw-mp/Script/Functions/Items.hpp
	apps/openmw-mp/Script/Functions/Mechanics.cpp
	apps/openmw-mp/Script/Functions/Mechanics.hpp
	apps/openmw-mp/Script/Functions/Stats.cpp
	apps/openmw-mp/Script/Functions/Stats.hpp
	apps/openmw-mp/Script/ScriptFunctions.cpp
	apps/openmw-mp/Script/ScriptFunctions.hpp
7 years ago
David Cernat ed15d9ebf5 Merge pull request #406 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Mechanics.cpp
	apps/openmw-mp/Script/Functions/Mechanics.hpp
	components/openmw-mp/Base/BasePlayer.hpp
7 years ago
David Cernat 2390744b45 Merge pull request #404 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Player.hpp
	apps/openmw-mp/Script/Functions/Mechanics.cpp
	apps/openmw-mp/Script/Functions/Mechanics.hpp
	apps/openmw-mp/Script/Functions/Settings.hpp
	apps/openmw-mp/Script/Functions/Stats.cpp
	apps/openmw-mp/Script/Functions/Stats.hpp
	components/openmw-mp/Base/BasePlayer.hpp
7 years ago
David Cernat c3b44e11fb
Merge pull request #402 from TES3MP/0.6.3
Add 0.6.3 commits up to 5 Apr 2018
7 years ago
David Cernat f50637bdd4 Merge pull request #401 from TES3MP/0.6.3 while resolving conflicts
# Conflicts:
#	apps/openmw-mp/processors/world/ProcessorContainer.hpp
#	apps/openmw/mwmp/WorldEvent.cpp
7 years ago
Koncord 6131ada0ba [Browser] Add compatibility compilation mode 7 years ago
David Cernat 5bb09d3bed Merge pull request #399 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw/mwmp/WorldEvent.cpp
	apps/openmw/mwmp/WorldEvent.hpp
	apps/openmw/mwmp/processors/world/ProcessorContainer.hpp
7 years ago
Koncord 2aaf9105af [Browser] Add Client Settings and Server Settings Tabs.
Save Browser state to the client config
7 years ago
David Cernat 3d5860d6f4 Merge pull request #396 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Player.cpp
	apps/openmw-mp/Script/Functions/Settings.cpp
	apps/openmw-mp/Script/Functions/Settings.hpp
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw-mp/Script/Functions/World.hpp
	apps/openmw/mwgui/container.cpp
	apps/openmw/mwmp/LocalActor.cpp
	apps/openmw/mwmp/LocalPlayer.cpp
	apps/openmw/mwmp/WorldEvent.cpp
	apps/openmw/mwmp/processors/world/ProcessorContainer.hpp
	components/openmw-mp/Base/BaseEvent.hpp
	components/openmw-mp/Log.cpp
	components/openmw-mp/Log.hpp
7 years ago
David Cernat 7ec08e125b Merge pull request #394 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Settings.cpp
	apps/openmw-mp/Script/Functions/Settings.hpp
7 years ago
David Cernat b2a3dd9d60 Revert "[Server] Add previous state to ON_RECORD_DYNAMIC"
This reverts commit 2ac01dc02a.

Commit 2ac01dc02a had confused the ON_RECORD_DYNAMIC event meant for custom items with a hypothetical ON_PLAYER_STATS_DYNAMIC event meant for dynamic player stats and had added functionality appropriate for the latter.

ON_PLAYER_STATS_DYNAMIC was never implemented because it would have caused too much event spam, with it constantly being triggered by any running player losing fatigue. If it ends up being added, the reverted lines from 2ac01dc02a should be added to it instead.
7 years ago
Koncord 2ac01dc02a [Server] Add previous state to ON_RECORD_DYNAMIC 7 years ago
Koncord 4aff1f1833 [Server] Add previous state to ON_PLAYER_SKILL
That is array of struct with next content: {base, current, mod, damage, progress}
7 years ago
Koncord 017956366f [Server] Add previous state argument to ON_PLAYER_ATTRIBUTE event
That is array of struct with next content: {base, current, mod, damage}
7 years ago
Koncord afbafdf806 [Server] Add PreReading virtual method
add "const" qualifier to exteriorCellPattern
7 years ago
Koncord d0eef7c98e [Server] Add possibility to use previous state of data in events 7 years ago
Koncord 7deff7a42a [Server] Use "else if" instead "if" + "return" 7 years ago
Koncord 23da0b16ea [Server] Fix build 7 years ago
Koncord 6f7771d97e [General] Move similar functions to BasePacketController
Simplify ContainsPacket and fix GetPacket
7 years ago
Koncord 24ba4ae404 [Server] Delete ON_GUI_ACTION 7 years ago
Koncord 73d030b779 [Server] Remove return value from ON_PLAYER_CONNECT event 7 years ago
Koncord 4e869a2974 [General] Use correct versions of C++ per target
C++11 for client, components and launcher
C++14 for server, browser and masterserver
7 years ago
Koncord e85d0db771 [Server] Code style 7 years ago
Koncord 44c549211e [Server] use std::unique_ptr for packet controllers and MasterClient 7 years ago
Koncord 2bfd4627ed [Server] Use queue for MessageBox packets 7 years ago
Koncord 9dae748a76 [General] Change type of GUIMessageBox::id to uint64_t 7 years ago
Koncord bb7c5ee34c [Server] Allow nil as callback 7 years ago
Koncord 54945b537d [Server] Get rid handwritten ids in GUI API 7 years ago
Koncord 4bde7d80f5 [Server] Use regular Ptrs where possible to avoid seizure Ptrs by lua 7 years ago
Koncord f2a88e6a37 [Server] Use multiple parameters instead functions for Mark functions 7 years ago
Koncord 410eb353e8 [Server] Call GC after deleting player 7 years ago
Koncord a9614ad28e [Server] Fix invalid read 7 years ago
Koncord 69436714f9 [Server] Fix uninitialized variable 7 years ago
David Cernat 3b865244d0 [Server] Fix typo related to player deletion 7 years ago
David Cernat 4e9cac96c7 [Server] Add scale and selected spell functions to Player in Sol 7 years ago
David Cernat ac374a8ef9 Merge pull request #384 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
 	apps/openmw-mp/Player.cpp
	apps/openmw-mp/Script/Functions/Mechanics.cpp
	apps/openmw-mp/Script/Functions/Mechanics.hpp
	apps/openmw-mp/Script/Functions/Quests.cpp
	apps/openmw-mp/Script/Functions/Quests.hpp
	apps/openmw-mp/Script/ScriptFunctions.hpp
	apps/openmw-mp/processors/player/ProcessorRecordDynamic.hpp
	apps/openmw/mwmp/LocalPlayer.hpp
	apps/openmw/mwmp/WorldEvent.cpp
	apps/openmw/mwworld/worldimp.cpp
	components/openmw-mp/Base/BasePlayer.hpp
7 years ago
David Cernat bdafa8e9ab
Merge pull request #380 from TES3MP/0.6.3
Add 0.6.3 commits up to 30 Jan 2018
7 years ago
David Cernat f1ba9253b0 [Client] Fix build 7 years ago
David Cernat 5858e05362 Merge pull request #378 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	apps/openmw-mp/CMakeLists.txt
	apps/openmw-mp/Script/ScriptFunctions.hpp
	components/CMakeLists.txt
7 years ago
David Cernat 1a8a518897 Merge pull request #376 from TES3MP/0.6.3 while resolving conflicts
Conflicts:
	README.md
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw/mwmp/LocalPlayer.cpp
	apps/openmw/mwmp/LocalPlayer.hpp
	apps/openmw/mwworld/scene.cpp
	components/openmw-mp/Version.hpp
7 years ago
David Cernat 569911121d [Client] Fix build 7 years ago
David Cernat 5cf71a4e67 Merge branch 'master' of https://github.com/TES3MP/openmw-tes3mp 7 years ago
David Cernat d70b93e095 Merge pull request #373 from TES3MP/0.6.2 while resolving conflicts
# Conflicts:
#	apps/openmw/mwmp/DedicatedActor.cpp
#	apps/openmw/mwmp/processors/player/ProcessorPlayerResurrect.hpp
7 years ago
Koncord 6197636fac [General] Fix "using after move" warning 7 years ago
Koncord edd883853d [Server] Add sendConsoleCommand function to lua api 7 years ago
Koncord 0e97b769f9 [General] Change type of Object::enchantmentCharge 7 years ago
Koncord 4cfb04aa7f [Server] Fix setBedRestAllowed 7 years ago
Koncord 7e5b929ea2 [General] Rework PacketPlayerFaction 7 years ago
Koncord 29ba07fe8c [General] Rework PacketPlayerInventory
Save the Action for each item. Now you can add or remove multiple items
7 years ago
Koncord de0bb3cdab [General] Change type of Item::enchantmentCharge to float 7 years ago
Koncord cb86557aca [Server] Init Player::inUpdateQueue 7 years ago
Koncord 0780329a59 [General] Fix typo 7 years ago
Koncord 1f1cbf53f9 [Client] Mark trivial destructor as default, remove "virtual" keyword
Mark constructor as explicit
7 years ago
Koncord b63bf258ff [Client] Remove "virtual" keyword for getLocalActor & getDedicatedActor 7 years ago
Koncord c2578918f2 [Client] Do not copy strings in getLocalActor and getDedicatedActor 7 years ago
Koncord e2e197d84a [Client] Fix getting element of shared_ptr by reference 7 years ago
Koncord f1e2bc01f6 [General] Use floats instead of doubles for enchantmentCharge 7 years ago
Koncord 1e25a122c9 [General] Use RELIABLE_ORDERED only when jumping 7 years ago
David Cernat a037193e79 Merge pull request #372 from TES3MP/0.6.2 while resolving conflicts
Conflicts:
	apps/openmw-mp/Script/Functions/Actors.cpp
	apps/openmw-mp/Script/Functions/Actors.hpp
	apps/openmw-mp/Script/Functions/Items.cpp
	apps/openmw-mp/Script/Functions/Items.hpp
	apps/openmw-mp/Script/Functions/World.cpp
	apps/openmw-mp/Script/Functions/World.hpp
	apps/openmw/mwmp/WorldEvent.cpp
	components/openmw-mp/Packets/Player/PacketPlayerEquipment.cpp
	components/openmw-mp/Version.hpp
7 years ago
Koncord 05fac2f67d [Browser] Disable connect button by default 7 years ago
Koncord 043eb224e2 [Browser] Cast QStrings to UTF-8 instead Latin1 7 years ago
Koncord 35b771b19e [Browser] Rework Connect window 7 years ago
David Cernat 92060bd6b6
Merge pull request #370 from TES3MP/0.6.2
Add 0.6.2 commits up to 10 Jan 2018
7 years ago
Koncord 1de9f30449 [Server] Fix iterating killed timers when kill() called in the callback
Instance new timers before new tick
7 years ago
David Cernat 45d3b24c17
Merge pull request #367 from Aesylwinn/LibIssues
Refine CMake scripts and fix g++ 5 build
7 years ago
Kyle Cooley 7248a5d037 Remove some duplication, standardize sol2 header inclusion 7 years ago
Kyle Cooley ba4d2bd5fe Merge remote-tracking branch 'origin/master' into LibIssues 7 years ago
David Cernat b6a7377692 Merge pull request #366 from TES3MP/0.6.2 while resolving conflicts, 2nd try
Conflicts:
	apps/openmw-mp/Networking.cpp
	apps/openmw-mp/Script/Functions/Miscellaneous.cpp
	apps/openmw-mp/Script/Functions/Miscellaneous.hpp
	apps/openmw/mwmp/GUI/GUIChat.cpp
7 years ago
Kyle Cooley fcd4d8b842 Fix build for gcc-5.4.0 7 years ago
Kyle Cooley e2103d0bea Clean up find file for LuaJit, add one for Sol2 7 years ago
Koncord 35922e4898 [General] Change "enum ACTOR_ACTION" to "enum class Action" 7 years ago
Koncord ef0384b296 [General] Simplify ProxyMasterPacket 7 years ago
Koncord 5b8f4f3e92 [Browser] Stop PingHelper on refresh 7 years ago
Koncord 647661daf9 [General] Minor fixes in Master packets 7 years ago
David Cernat 2f6e3b4cda [Documentation] Clarify status of version 0.7 7 years ago
Koncord c4949ac5d9 [General] Change regular enums to enum class 7 years ago
Koncord 8f5d31cb03 [Server] Iterate only through updated players on each frame 7 years ago
Koncord 4ab338bbb1 [Server] Register ON_PLAYER_QUICKKEYS event 7 years ago
Koncord 5777759aae [General] Change enum QuickKey::QUICKKEY_TYPE to enum class 7 years ago
Koncord 2019128d92 [Server] Change signature of TimerController::kill, validate callback 7 years ago
David Cernat 51a92bcf8f [Client] Fix build 7 years ago
David Cernat 09958681cd Merge pull request #363 from TES3MP/0.6.2 while resolving conflicts, 2nd try 7 years ago
David Cernat 8bb764c6d6 [Build] Use C++14 instead of C++11, as the server requires the former 7 years ago
David Cernat dddd2f1cc7 Merge pull request #361 from OpenMW/master while resolving conflicts
# Conflicts:
#	.travis.yml
7 years ago
David Cernat a84c4c7ecc [Server] Print module order on startup 7 years ago
David Cernat f2eca2566f [Server] Add storedData table for Player to increase clarity
This way, customData is meant to hold module-specific and temporary data while storedData is meant to hold persistent core data.
7 years ago
Koncord 077a3d06b3 [General] Change type of BasePlayer::resurrectType to ResurrectType 7 years ago
Koncord f9c4b847aa [Server] Make argument const reference 7 years ago
Koncord c5388e49f2 [Server] Remove Pawn switcher in cmake 7 years ago
Koncord ba07d7820f [Server] Remove Terra 7 years ago
Koncord 77d14211c9 [Server] Add Weather.cpp to build 7 years ago
Koncord ecbe0127b0 [Client] Remove unused variable 7 years ago
Koncord fd721143e2 [Client] Implement weather packet 7 years ago
Koncord 25b7095396 [Client] Share WeatherManager to the mwmp::Main class 7 years ago
Koncord 44dc153ebe [Server] Add Weather API 7 years ago
Koncord 1ef6ad6215 [General] Add weather struct and packet 7 years ago
Koncord aff1859759 [Server] Add new line for help strings 7 years ago
Koncord 122a30c183 [General] Change type of refNumIndex & mpNum to unsigned 7 years ago
Koncord 2726d94d10 [Client] Disallow to load save game via launch options 7 years ago
Koncord d98bb74b80 [Client] Disable autosave on waiting 7 years ago
Koncord bfdf348a6c [Server] Fix Undefined behavior 7 years ago
Koncord f11473da87 [Client] Remove WorldEvent::addObject(), use move semantics 7 years ago
Koncord 4d0072a74c [Server] Use foreach loop 7 years ago
Koncord cd620e17ec [Server] Use std::move(tokens) 7 years ago
Koncord dd352f0a91 [Server] Improve Player::sendToLoaded() 7 years ago
Koncord f35d35741e [General] Remove redundant code
Remove BaseEvent::worldObjectCount
Remove BaseActor::count
Use foreach loops in packets and processors
Remove redundant "&" in CellController::get().getCell() calls
7 years ago
Koncord 585c24cee8 [Server] Optimize CellController 7 years ago
Koncord ff8b5061b4 [Server] Modernize CellController singleton 7 years ago
Koncord e97dac7793 [Server] Mark derived processor classes as final 7 years ago
Koncord 7748e582a8 [Client] Mark derived processor classes as final 7 years ago
Koncord 2cb0ea20f0 [General] Modernize packets
Use explicit for constructors
Use override instead virtual for inherited methods
Mark final derived classes as "final"
7 years ago
David Cernat de77ee3126
Merge pull request #356 from OpenMW/master
Add OpenMW commits up to 7 Dec 2017
7 years ago
David Cernat ad61d88cb1 [General] Fix typos and use consistent style for recent additions 7 years ago
Koncord 051f65a4d5 [Client] Make channel buttons blinking on new messages 7 years ago
Koncord 7eecbfd08e [Client] Fix saving channel history 7 years ago
Koncord 1c7330635b [Server] Add & register ON_PLAYER_WEATHER event 7 years ago
Koncord 392e645fe5 [Server] Add isMarkedForDeleteion function 7 years ago
Koncord 91398c5dcc [Server] Implicitly join to "Default" channel 7 years ago
Koncord aa183e6844 [General] Introduce chat channels 7 years ago
Koncord c55f0f73b8 [Server] Pass shared_ptr<Player> by reference 7 years ago
David Cernat 85a9181c12
Merge pull request #355 from OpenMW/master
Add OpenMW commits up to 6 Dec 2017
7 years ago
Koncord 94f3eaa980 [Server] Fix warning "implicit fallthrough" 7 years ago
David Cernat 456bcee68a [Server] Use Utils function as workaround for "bad exception" on Windows 7 years ago
David Cernat cfb5835e17 [Server] Clean up method and variable names 7 years ago
Koncord cd03d59056 [Client] Fix invalid read (memcheck warning) 7 years ago
David Cernat 2bc79fcdf4
Merge pull request #309 from TES3MP/new-script-api
Add new-script-api commits
7 years ago
David Cernat c2f330d4f1
Merge pull request #354 from TES3MP/master
Add master commits up to 4 Dec 2017
7 years ago
David Cernat fc5e883160 [General] Rework PlayerStatsDynamic packets so they are of minimal size 7 years ago
David Cernat 993cc3dfd6 [Server] Rename server "plugins"/"mods" into "modules" for clarity
The terms "plugins" and "mods" were used interchangeably to refer to collections of server scripts, which was bound to cause confusion later on, especially with client data files frequently being referred to as "plugins" and "mods" as well.

Moreover, the server configuration file now starts its manual ordering with "Module1" for consistency with the pluginlist.json (soon to be dataFileList.json) of the CoreScripts.
7 years ago
David Cernat d1ad0c91f8 [General] Rework PlayerEquipment packets so they are of minimal size
Moreover, rename BaseNetCreature's equipedItems into equipmentItems.
7 years ago
David Cernat 7e322f1f8b Merge pull request #352 from TES3MP/master while resolving conflicts
# Conflicts:
#	README.md
7 years ago
David Cernat bd9e8bd10f [General] Simplify storing of attribute and skill index changes 7 years ago
David Cernat 711bdf187a [General] Add BaseNetCreature to CMakeLists in components 7 years ago
David Cernat 720ef5f6c5 [General] Use consistent code style 7 years ago
David Cernat eff3504b05
Merge pull request #350 from TES3MP/master
Add master commits up to 29 Nov 2017
7 years ago
Koncord b55bb445dd [General] Change underlying type of the WidgetType to uint8_t 7 years ago
Koncord a546d99000 [General] Fix type 7 years ago
Koncord 1841562553 [General] Minor cleanup of the World Packets 7 years ago
Koncord 744b8cf168 [General] Minor cleanup of the Actor Packets 7 years ago
Koncord e44fcdc0b3 [General] Cleanup Player packets 7 years ago
David Cernat 901fe72471 [Server] Fix variable shadowing in Player 7 years ago
David Cernat a796f81444 [General] Add and use utility function for int value checks in vectors 7 years ago
Koncord 46d55816b8 [Client] Remove debug code 7 years ago
Koncord 4ebfcc4a21 [Server] Limit handshake attempts 7 years ago
Koncord 729f6e745e [Server] Remove unused get() method from EventController 7 years ago
Koncord fef3764eb1 [General] Add forgotten files to build 7 years ago
Koncord 382f56178c [Client] Add custom windows 7 years ago
Koncord e657934cef [Server] Add custom window API 7 years ago
Koncord c276ff4bd9 [General] Add packet GUIWindow 7 years ago
David Cernat 0a35b897be
Merge pull request #348 from TES3MP/master
Add master commits up to 25 Nov 2017
7 years ago
David Cernat b0965f094a [General] Rework PlayerAttribute packets so they are of minimal size
Previously, whenever a single attribute value changed for a player, that player then sent a PlayerAttribute packet with all values for all 8 attributes.

This did not cause anywhere as much packet spam as PlayerSkill used to, but there was no good reason not to fix it as well.
7 years ago
David Cernat ef79a98544 [General] Rework PlayerSkill packets so they are of minimal size
Previously, whenever a single skill value changed for a player, that player then sent a PlayerSkill packet with all values for all 27 skills, plus the player's progress towards the next level and the bonuses to each attribute on the next level up as the result of sklll increases thus far.

This commit makes PlayerSkill contain only the values of specific skills, moves the player's progress towards the next level to PlayerLevel packets, and moves the bonuses to each attribute on the next level up to PlayerAttribute packets.

Players now also send a PlayerSkill packet whenever their progress towards a new point in a skill changes. This was previously avoided so as to not have massive packet spam.
7 years ago
David Cernat 64b57983f0 [General] Add TRACE log messages in player processors 7 years ago
David Cernat 606ddff813
Merge pull request #346 from TES3MP/master
Add master commits up to 24 Nov 2017
7 years ago
David Cernat cac4684986 [Client] Don't force skill update on cell change
Previously, an attempt by the server to simultaneously change a player's cell and skills (as you'd expect when a player file is loaded) led to:

1) The server sending the cell packet first and the skill packet afterwards

2) The player receiving the cell packet and sending their own skill packet as part of the client's forced skill update

3) The player receiving the skill packet from the server

4) The server receiving the skill packet from the player

The result was that, if the player then left the server without sending another skill packet, the server's memory retained the skills the player had sent instead of the skills it had sent to the player.

This is the first step in a solution to that situation and similar ones.
7 years ago
David Cernat 07d75abdf8 [Server] Use consistent order for includes 7 years ago
David Cernat 1ee460bba8
Merge pull request #344 from TES3MP/master
Add master commits up to 23 Nov 2017
7 years ago
David Cernat d33254f287 [Server] Rename CharClass' isDefault() into isCustom()
It was already using the logic of isCustom() by mistake.
7 years ago
David Cernat 010a80ceca [Server] Place getters and setters in consistent order 7 years ago
David Cernat 947b3f76be [Server] Replace Player's isMale() and setIsMale() with gender property
For simplicity and clarity.
7 years ago
David Cernat 6f822f54aa [Server] Make chat commands case insensitive 7 years ago
David Cernat a3e2ab4d4e [Server] Send correct packet for inventory changes 7 years ago
David Cernat 4cc0216e0a [Server] Send cell changes before position changes, and prioritize both
Previously, a script changing a player's cell and position at the same time would end up sending a position packet first and then a cell change packet that overrode the former, with the player ending up at the center of the destination cell instead of at the correct position.
7 years ago
David Cernat 80be664139 [Server] Fix skill-related script functions
Add getSkillIncrease() and setSkillIncrease() script functions to get and set the attribute bonuses received at the next level up as a result of skill increases.

Previously, getSkill() and setSkill() attempted to return and set the attribute bonuses, respectively. However, they mistakenly used a skill ID as a parameter for the attribute bonuses, when in fact npcStats.mSkillIncrease is an integer array of size 8 where the key stands for an attribute's ID. As a result, setSkill() had the unexpected side effect of messing up a player's major and minor skills because of the invalid values it was setting for npcStats.mSkillIncreases.
7 years ago
David Cernat 57a0415ba3 [Server] Send level packets in Player's update() at the appropriate time
Previously, trying to send a level packet after base info and character class packets in a script actually led to the level packet being sent first and then being overridden by the others, with the player ending up at level 1 on their client.
7 years ago
David Cernat 494b10b97e [Server] Send player packets in a more appropriate order
Previously, the fact that a character class packet got sent after a dynamic stats packet caused the dynamic stats to get overridden on the client by the class change.
7 years ago
David Cernat ba161ddddd [Server] Make a few function names more consistent and fix typos 7 years ago
David Cernat 7788821a69
Merge pull request #340 from TES3MP/master
Add master commits up to 17 Nov 2017
7 years ago
David Cernat 068f733d1e
Merge pull request #338 from TES3MP/master
Add master commits up to 16 Nov 2017
7 years ago
David Cernat 1272b03f25 [Server] Fix typo in player script function 7 years ago
David Cernat b4e8560698 [Client] Send cell states correctly after inputting name
Previously, initial cell states were sent in LocalPlayer::processCharGen() and were ignored by the server because the player was not yet regarded as loaded. The result was that existing players logging in could not see each other until they went through at least one cell change.
7 years ago
David Cernat 926106cf8c [General] Rework CharGen slightly for clarity purposes
Previously, charGenStage.end was doing double duty as both the variable indicating the number of CharGen stages and – when set to 0 – the variable indicating that CharGen was over. The latter role is now filled by a new boolean.
7 years ago
David Cernat ac7a588632 [General] Update config files 7 years ago
David Cernat a21f5d18d6
Merge pull request #336 from TES3MP/master
Add master commits up to 12 Nov 2017
7 years ago
David Cernat a8261bb385 [General] Fix printing of packet identifiers after changes to logger 7 years ago
David Cernat 700e4d032e
Merge pull request #334 from TES3MP/master
Add master commits up to 11 Nov 2017
7 years ago
David Cernat 4dbada69bf Merge pull request #332 from TES3MP/master while resolving conflicts 7 years ago
Koncord ca7f3f7450 [Client] Disable focus on <tab> for chat window 7 years ago
Koncord 64b531aa3c [Server] Remove redundant argument 7 years ago
Koncord f377164db9 [Client] Fix build 7 years ago
Koncord 7ab01b66e4 [General] Rewrite Log class 7 years ago
Koncord d15c674584 [General] Move getFilenameTimestamp() to Utils 7 years ago
Koncord 0da44f69ad [Server] Isolate getModFolder() & getDataFolder() 7 years ago
Koncord 062d6a1824 [Server] Add sandboxed import() function 7 years ago
David Cernat 29cb51cdce [Server] Enable SOL_SAFE_USERTYPE for both Debug and RelWithDebInfo 7 years ago
Koncord 1d111fdca8 [General] Update sol submodule to 2.18.5 7 years ago
Koncord bd7082f57e [Server] Use custom Lua error handler not only on Windows
For some reason sol's default error handler does not wroking properly
7 years ago
David Cernat 71c921faa7 [Server] Rename property cell into description, initialize Cells type 7 years ago
David Cernat 5653d07c7b [Server] Fix build on Windows 7 years ago
Koncord 948090676a [Server] Impove Lua Error handler for Windows 7 years ago
David Cernat 61db22f5ae
Merge pull request #329 from TES3MP/master
Add master commits up to 31 Oct 2017
7 years ago
David Cernat b7e5e77166 [Server] Fix getCaseInsensitiveFilename, simplify Players.size() 7 years ago
David Cernat 378d30834b [Server] Add special error handler for Sol back in, but only for Windows 7 years ago
Koncord 14d47213ef [Server] Add Players.size() to Lua API 7 years ago
Koncord 3495fd43f4 [Client] Add network statistics (CTRL+F2) 7 years ago
Koncord e7a5919477 [Server] Fix path to native libs 7 years ago
Koncord 1aa630e4a9 [Server] Add StackWalker for Windows 7 years ago
Koncord e8915f8ec5 [Client] Fix build 7 years ago
Koncord 14fdec2478 [Server] Add forgotten stacktrace.cpp 7 years ago
David Cernat b801cf2c9e Merge pull request #327 from TES3MP/master
Add master commits up to 26 Oct 2017
7 years ago
David Cernat 878294e4fe Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 7 years ago
Koncord d44848ecbb [Server] Fix build 7 years ago
Koncord 05abb8ace3 [Server] Add Log level constants to lua 7 years ago
Koncord 04a844a9c0 [Server] Use sol's default_handler 7 years ago
Koncord dad0b38f25 [Server] Add custom terminate handler with stacktrace 7 years ago
Koncord a3d5fbbdcd [Server] Add stacktrace 7 years ago
Koncord 916ada108f [General] Modernize Log utility
* Reverse Log levels
* Add LOG_TRACE
* Spawn instance of Log in Get() function
7 years ago
David Cernat bece095579 [Server] Add getCaseInsensitiveFilename script function back in 7 years ago
David Cernat d6dc75e94b Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 7 years ago
David Cernat 76a4abd7c0 Merge pull request #325 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/processors/player/ProcessorPlayerTopic.hpp
7 years ago
David Cernat 0e73571111 Merge pull request #322 from TES3MP/master
Add master commits up to 23 Oct 2017
7 years ago
David Cernat 8a93631e08 Merge pull request #320 from TES3MP/master
Add master commits up to 22 Oct 2017
7 years ago
Koncord ba8613a179 [Browser] Add "no password" filter to browser 7 years ago
David Cernat fb67180809 [Server] Fix build in Visual Studio 7 years ago
Koncord 4530370e52 [Server] Use old style of Server Plugins location 7 years ago
Koncord ce6a4e4032 [Server] Fix indents 7 years ago
Koncord fc3f2483ee [Server] Add manual Server Plugins sort 7 years ago
Koncord dffd3bfa7d [Server] Add customData to Player
example:
counter = 0

Event.register(Events.ON_PLAYER_CONNECT, function(player)
    player.customData.counter = counter
    counter = counter + 1
    return true
end)

CommandController.registerCommand("test", function(player, args)
    player:message(player.customData.counter, false)
    return true
end, "")
7 years ago
Koncord 7a0b45d456 [Server] Load mods in dependencies order 7 years ago
Koncord 66283943c5 [Browser] Fix Clang warnings 7 years ago
Koncord d702845026 [General] Fix Clang warnings in Log.hpp 7 years ago
Koncord 0a0c9893b1 [General] Update sol submodule 7 years ago
David Cernat 6e7c033a5d Merge pull request #318 from TES3MP/master
Add master commits up to 18 Oct 2017
7 years ago
David Cernat bbac26294f [Server] Fix typos and make all files end with newlines 7 years ago
David Cernat b20df4b7cd Merge pull request #316 from TES3MP/master
Add master commits up to 17 Oct 2017
7 years ago
David Cernat 17a8e32782 Merge pull request #314 from TES3MP/master
Add master commits up to 16 Oct 2017
7 years ago
Koncord 1fd16ba69c [Browser] Correctly init parent class 7 years ago
Koncord 62588ce088 [Server] Minor fixes 7 years ago
Koncord 6cb9c3c713 [General] Remove explicit specifier 7 years ago
Koncord 3839a2dcfd [Browser] Remove unused variables 7 years ago
Koncord ed75563a94 [Browser] Do not show "Unreachable" servers when ping filter is enabled 7 years ago
Koncord 01a5196a92 [Browser] Mark few strings translatable 7 years ago
Koncord 15723adb9a [Browser] Make sort servers case insensitive 7 years ago
Koncord 57353cdfff [Browser] Mark unreachable servers as "Unreachable" instead 999 7 years ago
David Cernat fe9a3088bd Merge pull request #312 from TES3MP/master
Add master commits up to 10 Oct 2017
7 years ago
Koncord 5c79e7106f [Browser] Minor improvements 7 years ago
David Cernat 4845599bda Merge pull request #308 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/main.cpp
7 years ago
David Cernat 7a38a0b223 Merge pull request #305 from TES3MP/master
Add master commits up to 6 Oct 2017
7 years ago
Koncord cbabc91b06 [Server] Stop MasterClient thread and inform server owner on ban 7 years ago
Koncord 846f83e3e4 [Master] Use RakNet ban system instead homebrew 7 years ago
David Cernat 841a4f90c1 Merge pull request #301 from TES3MP/master
Add master commits up to 28 Sep 2017
7 years ago
David Cernat 3284769fef [Server] Add getModFolder() script function and Config environment 7 years ago
David Cernat b5ce3cebbc Merge pull request #297 from TES3MP/master
Add master commits up to 22 Sep 2017
7 years ago
Koncord 73aa83aa03 [Master] Use RakNet ban system with homebrew bansystem 7 years ago
Koncord 5fcdff843c [Master] Minor fixes in lua scripts 7 years ago
David Cernat 76f1a61538 Merge pull request #295 from TES3MP/master
Add master commits up to 20 Sep 2017
7 years ago
David Cernat d591180e99 [Server] Clean up logAppend and add new message for server shutdown 7 years ago
David Cernat 381a5fabc4 Merge pull request #291 from TES3MP/master
Add master commits up to 17 Sep 2017
7 years ago
David Cernat 9838cc680a [Server] Enable Lua debugging for RelWithDebInfo builds 7 years ago
David Cernat 98195b5e3c Merge pull request #289 from TES3MP/master
Add master commits up to 15 Sep 2017
7 years ago
Koncord 3124e627cf [General] Move hack back to the right place 7 years ago
Koncord c2286482ae [Master] Add 'savebans' command to RESTful API 7 years ago
Koncord f814ae795d [Master] Fix return from isServerValid()
Temporary accept all servers in OnServerAnnnounce
7 years ago
Koncord 50ef9677fe [Master] Fix master.lua
Remove unban() from loadBans()
Disable strict json order from saveBans()
7 years ago
Koncord 838e05521e [Master] Minor fixes 7 years ago
David Cernat cccbe753d7 [General] Update version to 0.7-alpha 7 years ago
Koncord 6decd148e5 [Server] Fix build on Windows 7 years ago
Koncord 045dc566ea Merge branch 'master' into new-script-api 7 years ago
Koncord bb183457a6 [Master] Init BanAddress/UnbanAddress before opening script 7 years ago
Koncord dc18916f46 [Master] Add isServerValid lambda. Add "OnServerAnnounce" callback 7 years ago
Koncord f99dafbf51 [Master] Add scripts for generating certificates for testing purposes 7 years ago
Koncord aee6fb1265 [Master] Add master lua scripts 7 years ago
Koncord d1388cdf84 [Master] Add Admin Rest Server
Add admin rest callback - "OnAdminRequest". Called on every POST request
7 years ago
Koncord b869fe0b76 [Master] Add Lua
Init master server values by config table in script
Add BanAddress/UnbanAddress to lua
Fix a couple of indents
Pass lua script as first launch parameter of master server
7 years ago
Koncord 0e2817da88 [Master] Move response stuff to ResponseUtils.hpp 7 years ago
Koncord fc8232f943 [Master] Add ban/unban functions 7 years ago
Koncord 26324c2578 [Master] Change Ban structure to vector<string> 7 years ago
Koncord 4e93905350 [Master] Backport SimpleWeb 7 years ago
Koncord 510e657c93 Merge branch 'master' into new-script-api 7 years ago
Koncord 060ebe3d4a [Server] Init isWerewolf variable
Comment unused code
7 years ago
Koncord 1c0adc47ee [Server] Fix [get/set]MajorSkills and [get/set]MinorSkills 7 years ago
Koncord 66fdba957b [Server] Add getDataFolder() to Script API 7 years ago
Koncord 1d16958910 [Server] Fix multiple calls of ON_POST_INIT 7 years ago
Koncord b18c6dec9d [Server] Fix sendList()
Remove return from requestContainers()
Rename "Cells" usertype to "Cell"
7 years ago
Koncord 991a1fe8d8 [General] Update submodules 7 years ago
Koncord 60fc0bedb8 [Server] Use lower case for methods 7 years ago
Koncord 7717f9bece [Server] Replace getKillRefId/getKillNumber with getKill 7 years ago
Koncord fe2dd1bad4 [Server] Use correct cells in "send" functions
Remove unused return type from ON_PLAYER_SENDMESSAGE
7 years ago
Koncord 2d0840cb3a [General] Modernize Script API
This commit changes the style of tes3mp serverside scripting mods. Short list of changes:
* Break compatibility with old server mods
* OOP style lua API
* Basic dependency checker, allowing the installation of multiple server mods without changing configs
* Remove support for C++ plugins
* Change outdated LuaBridge to [sol2](https://github.com/ThePhD/sol2);
* Support GCC, Clang and MSVC compilers
* New environment variables: "TES3MP_SERVER_DIR" and "TES3MP_SERVER_USERDIR";
* New entity "Command controller" for registering new chat commands;
* New Event system
* Simplified Timer API
* All Lua mods now run in their own environments
* Add global namespace - Data that can be used for communicating between mods
* Player and Actor inherit base class NetActor
7 years ago

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "extern/breakpad"]
path = extern/breakpad
url = https://chromium.googlesource.com/breakpad/breakpad
[submodule "extern/sol"]
path = extern/sol
url = https://github.com/ThePhD/sol2

@ -35,7 +35,7 @@ addons:
# The other ones from OpenMW ppa
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev,
# tes3mp stuff
libboost1.61-dev, libqt5opengl5-dev
libboost1.61-dev, libqt5opengl5-dev, libluajit-5.1-dev
]
coverity_scan:
@ -60,8 +60,6 @@ matrix:
env:
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
allow_failures:
- env:
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- env:
- ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"

@ -19,15 +19,3 @@ git clone https://github.com/TES3MP/RakNet
cd RakNet
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
make -j3
cd ~/
git clone https://github.com/Koncord/CallFF
cd CallFF
mkdir build
cd build
cmake ../
make -j3
cd ~/
wget https://github.com/zdevito/terra/releases/download/release-2016-03-25/terra-Linux-x86_64-332a506.zip
unzip terra-Linux-x86_64-332a506.zip

@ -10,7 +10,6 @@ if [ ! -z "${MATRIX_CC}" ]; then
fi
export RAKNET_ROOT=~/RakNet
export Terra_ROOT=~/terra-Linux-x86_64-332a506
export CODE_COVERAGE=0
if [ ! -z "${ANALYZE}" ]; then
@ -38,5 +37,3 @@ ${ANALYZE}cmake .. \
-DUSE_SYSTEM_TINYXML=TRUE \
-DRakNet_LIBRARY_RELEASE=~/RakNet/lib/libRakNetLibStatic.a \
-DRakNet_LIBRARY_DEBUG=~/RakNet/lib/libRakNetLibStatic.a \
-DCallFF_INCLUDES=~/CallFF/include \
-DCallFF_LIBRARY=~/CallFF/build/src/libcallff.a

@ -140,6 +140,16 @@ endif()
find_package(RakNet REQUIRED)
include_directories(${RakNet_INCLUDES})
if (BUILD_OPENMW_MP OR BUILD_MASTER)
find_package(LuaJit REQUIRED)
find_package(Sol2 REQUIRED)
include_directories(${LuaJit_INCLUDE_DIRS} ${Sol2_INCLUDE_DIRS})
endif()
if (BUILD_MASTER)
find_package(OpenSSL REQUIRED)
include_directories(${OpenSSL_INCLUDE_DIRS})
endif()
# Dependencies
find_package(OpenGL REQUIRED)
@ -209,7 +219,6 @@ endif()
IF(BUILD_OPENMW OR BUILD_OPENCS)
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS
osgdb_bmp
@ -253,6 +262,8 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DON'T TOUCH IT!
set(BOOST_COMPONENTS system filesystem program_options)
if(WIN32)

@ -5,7 +5,7 @@ TES3MP
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source engine recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
* TES3MP version: 0.6.3
* TES3MP version: TBD-rewrite
* OpenMW version: 0.44.0
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
@ -17,10 +17,12 @@ Project Status
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
TES3MP is now playable in most respects. Player and NPC movement, animations, combat and spell casting are properly synchronized with small exceptions, as is picking up and dropping items in the world, using doors and levers, and adding and removing items from containers. Journal entries, faction stats and dialogue topics are also synchronized, allowing the majority of quests to work fine.
As of version 0.6, TES3MP is playable in most respects. Player and NPC movement, animations, combat and spell casting are properly synchronized with small exceptions, as is picking up and dropping items in the world, using doors and levers, and adding and removing items from containers. Journal entries, faction stats and dialogue topics are also synchronized, allowing the majority of quests to work fine.
[Serverside Lua scripts](https://github.com/TES3MP/CoreScripts) are used to save and load the state of most of the aforementioned.
Please note that the master branch is an unfinished rewrite of a large part of the project and is not yet playable. You should use the [latest 0.6 release](https://github.com/TES3MP/openmw-tes3mp/releases/latest) for now.
Contributing
--------------
@ -35,7 +37,7 @@ Getting Started
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide)
* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=44)
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
* [Subreddit](https://www.reddit.com/r/tes3mp)
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)

@ -1,5 +1,4 @@
set (CMAKE_CXX_STANDARD 14)
option(OPTION_TES3MP_PRE07 "Temporary. Pre 0.7.0 compatible mode." OFF)
set(BROWSER_UI
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
@ -17,6 +16,7 @@ set(BROWSER
PingUpdater.cpp
PingHelper.cpp
QueryHelper.cpp
Settings.cpp
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
)
@ -36,6 +36,7 @@ set(BROWSER_HEADER
netutils/Utils.hpp
netutils/QueryClient.hpp
Types.hpp
Settings.hpp
)
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
@ -75,6 +76,13 @@ add_executable(tes3mp-browser
${UI_HDRS}
)
set_property(TARGET tes3mp-browser PROPERTY CXX_STANDARD 14)
if (OPTION_TES3MP_PRE07)
target_compile_definitions(tes3mp-browser PRIVATE TES3MP_PRE07)
endif (OPTION_TES3MP_PRE07)
if (WIN32)
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
endif (WIN32)

@ -13,6 +13,8 @@
#include <QJsonArray>
#include <QFile>
#include <QJsonDocument>
#include <apps/browser/netutils/Utils.hpp>
#include <QtWidgets/QFileDialog>
using namespace Process;
@ -31,9 +33,6 @@ MainWindow::MainWindow(QWidget *parent)
tblServerBrowser->setModel(proxyModel);
tblFavorites->setModel(proxyModel);
// Remove Favorites tab while it remains broken
tabWidget->removeTab(1);
tblServerBrowser->hideColumn(ServerData::ADDR);
tblFavorites->hideColumn(ServerData::ADDR);
@ -54,22 +53,45 @@ MainWindow::MainWindow(QWidget *parent)
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
connect(pbModulePath, &QPushButton::clicked, [this](bool) {
QString str = QFileDialog::getExistingDirectory(this, tr("Module path"),
leModulePath->text(), QFileDialog::ShowDirsOnly);
if(!str.isEmpty())
leModulePath->setText(str);
});
loadFavorites();
queryHelper->refresh();
settingsMgr.loadBrowserSettings(*this);
#ifdef TES3MP_PRE07
gbModules->setTitle(tr("Plugins Path"));
chbAutosort->setVisible(false);
listModules->setVisible(false);
leAddModule->setVisible(false);
pbAddModule->setVisible(false);
pbUpModule->setVisible(false);
pbDownModule->setVisible(false);
pbRemModule->setVisible(false);
#endif
}
MainWindow::~MainWindow()
{
settingsMgr.saveBrowserSettings(*this);
delete mGameInvoker;
}
void MainWindow::addServerAndUpdate(QString addr)
void MainWindow::addServerAndUpdate(const QString &addr)
{
favorites->insertRow(0);
QModelIndex mi = favorites->index(0, ServerData::ADDR);
favorites->setData(mi, addr, Qt::EditRole);
/*auto address = addr.split(":");
auto data = getExtendedData(address[0].toLatin1(), address[1].toUShort());*/
//NetController::get()->updateInfo(favorites, mi);
//QueryClient::Update(RakNet::SystemAddress())
/*auto data = QueryClient::Get().Query();
@ -121,19 +143,16 @@ void MainWindow::play()
if (id < 0)
return;
ServerInfoDialog infoDialog(this);
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
infoDialog.Server(sm->myData[sourceId].addr);
ServerInfoDialog infoDialog(sm->myData[sourceId].addr, this);
if (!infoDialog.refresh())
{
queryHelper->refresh();
if (!infoDialog.exec())
return;
}
if (!infoDialog.exec())
if (!infoDialog.isUpdated())
return;
QStringList arguments;
@ -142,7 +161,8 @@ void MainWindow::play()
if (sm->myData[sourceId].GetPassword() == 1)
{
bool ok;
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
QString passw = QInputDialog::getText(this, tr("Connecting to: ") + sm->myData[sourceId].addr, tr("Password: "),
QLineEdit::Password, "", &ok);
if (!ok)
return;
arguments.append(QLatin1String("--password=") + passw.toLatin1());
@ -228,6 +248,11 @@ void MainWindow::havePlayersSwitch(bool state)
proxyModel->filterEmptyServers(state);
}
void MainWindow::noPasswordSwitch(bool state)
{
proxyModel->filterPassworded(state);
}
void MainWindow::maxLatencyChanged(int index)
{
int maxLatency = index * 50;

@ -9,6 +9,7 @@
#include "ui_Main.h"
#include "ServerModel.hpp"
#include "MySortFilterProxyModel.hpp"
#include "Settings.hpp"
#include <components/process/processinvoker.hpp>
class QueryHelper;
@ -17,11 +18,11 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
protected:
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
void addServerAndUpdate(QString addr);
void addServerAndUpdate(const QString &addr);
protected slots:
void tabSwitched(int index);
void addServer();
@ -31,6 +32,7 @@ protected slots:
void serverSelected();
void notFullSwitch(bool state);
void havePlayersSwitch(bool state);
void noPasswordSwitch(bool state);
void maxLatencyChanged(int index);
void gamemodeChanged(const QString &text);
private:
@ -38,6 +40,7 @@ private:
Process::ProcessInvoker *mGameInvoker;
ServerModel *browser, *favorites;
MySortFilterProxyModel *proxyModel;
SettingsMgr settingsMgr;
void loadFavorites();
};

@ -6,6 +6,7 @@
#include "ServerModel.hpp"
#include <qdebug.h>
#include <apps/browser/netutils/Utils.hpp>
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
@ -13,26 +14,49 @@ bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);
int ping = sourceModel()->data(pingIndex).toInt();
bool pingOk;
int ping = sourceModel()->data(pingIndex).toInt(&pingOk);
int players = sourceModel()->data(plIndex).toInt();
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
if (maxPing > 0 && (ping == -1 || ping > maxPing))
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
return false;
if (filterEmpty && players == 0)
return false;
if (filterFull && players >= maxPlayers)
return false;
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
return false;
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
bool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if(sortColumn() == ServerData::PING)
{
bool valid;
int pingright = sourceModel()->data(source_right).toInt(&valid);
pingright = valid ? pingright : PING_UNREACHABLE;
int pingleft = sourceModel()->data(source_left).toInt(&valid);
pingleft = valid ? pingleft : PING_UNREACHABLE;
return pingleft < pingright;
}
else
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
filterEmpty = false;
filterFull = false;
filterPasswEnabled = false;
maxPing = 0;
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
}
void MySortFilterProxyModel::filterEmptyServers(bool state)
@ -52,3 +76,9 @@ void MySortFilterProxyModel::pingLessThan(int maxPing)
this->maxPing = maxPing;
invalidateFilter();
}
void MySortFilterProxyModel::filterPassworded(bool state)
{
filterPasswEnabled = state;
invalidateFilter();
}

@ -13,13 +13,15 @@ class MySortFilterProxyModel : public QSortFilterProxyModel
Q_OBJECT
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;
public:
MySortFilterProxyModel(QObject *parent);
explicit MySortFilterProxyModel(QObject *parent);
void filterFullServer(bool state);
void filterEmptyServers(bool state);
void filterPassworded(bool state);
void pingLessThan(int maxPing);
private:
bool filterEmpty, filterFull;
bool filterEmpty, filterFull, filterPasswEnabled;
int maxPing;
};

@ -7,13 +7,19 @@
#include <QDebug>
#include "PingUpdater.hpp"
void PingHelper::Add(int row, AddrPair addrPair)
void PingHelper::Add(int row, const AddrPair &addrPair)
{
pingUpdater->addServer(row, addrPair);
if (!pingThread->isRunning())
pingThread->start();
}
void PingHelper::Reset()
{
//if (pingThread->isRunning())
Stop();
}
void PingHelper::Stop()
{
emit pingUpdater->stop();
@ -35,9 +41,8 @@ PingHelper &PingHelper::Get()
return helper;
}
PingHelper::PingHelper()
PingHelper::PingHelper() : QObject()
{
QObject();
pingThread = new QThread;
pingUpdater = new PingUpdater;
pingUpdater->moveToThread(pingThread);
@ -48,11 +53,4 @@ PingHelper::PingHelper()
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
}
PingHelper::~PingHelper()
{
}

@ -17,17 +17,17 @@ class PingHelper : public QObject
Q_OBJECT
public:
void Add(int row, AddrPair addrPair);
void Reset();
void Add(int row, const AddrPair &addrPair);
void Stop();
void SetModel(QAbstractTableModel *model);
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
static PingHelper &Get();
private:
PingHelper();
~PingHelper();
PingHelper(const PingHelper&) = delete;
PingHelper& operator=(const PingHelper&) = delete;
private:
PingHelper();
signals:
void stop();
public slots:

@ -14,7 +14,7 @@ void PingUpdater::stop()
run = false;
}
void PingUpdater::addServer(int row, AddrPair addr)
void PingUpdater::addServer(int row, const AddrPair &addr)
{
servers.push_back({row, addr});
run = true;
@ -42,7 +42,7 @@ void PingUpdater::process()
unsigned ping = PingRakNetServer(server.second.first.toLatin1(), server.second.second);
qDebug() << "Pong from" << server.second.first + "|" + QString::number(server.second.second)
<< ":" << ping << "ms";
<< ":" << ping << "ms" << "Sizeof servers: " << servers.size();
emit updateModel(server.first, ping);
}

@ -14,7 +14,7 @@ class PingUpdater : public QObject
{
Q_OBJECT
public:
void addServer(int row, AddrPair addrPair);
void addServer(int row, const AddrPair &addrPair);
public slots:
void stop();
void process();

@ -19,8 +19,8 @@ QueryHelper::QueryHelper(QAbstractItemModel *model)
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
connect(queryUpdate, SIGNAL(updateModel(QString, unsigned short, QueryData)),
this, SLOT(update(QString, unsigned short, QueryData)));
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
queryUpdate->moveToThread(queryThread);
}
@ -29,6 +29,7 @@ void QueryHelper::refresh()
if (!queryThread->isRunning())
{
_model->removeRows(0, _model->rowCount());
PingHelper::Get().Stop();
queryThread->start();
emit started();
}
@ -39,7 +40,7 @@ void QueryHelper::terminate()
queryThread->terminate();
}
void QueryHelper::update(QString addr, unsigned short port, QueryData data)
void QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)
{
ServerModel *model = ((ServerModel*)_model);
model->insertRow(model->rowCount());
@ -80,7 +81,7 @@ void QueryUpdate::process()
return;
}
for (auto server : data)
for (const auto &server : data)
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
emit finished();
}

@ -23,7 +23,7 @@ public slots:
void refresh();
void terminate();
private slots:
void update(QString addr, unsigned short port, QueryData data);
void update(const QString &addr, unsigned short port, const QueryData& data);
signals:
void finished();
void started();
@ -38,7 +38,7 @@ class QueryUpdate : public QObject
Q_OBJECT
signals:
void finished();
void updateModel(QString addr, unsigned short port, QueryData data);
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
public slots:
void process();
};

@ -6,32 +6,65 @@
#include "qdebug.h"
#include "ServerInfoDialog.hpp"
#include <apps/browser/netutils/Utils.hpp>
#include <algorithm>
#include <utility>
#include <QThread>
using namespace std;
using namespace RakNet;
ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
ThrWorker::ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port): addr(std::move(addr)), port(port), stopped(false)
{
this->dialog = dialog;
}
void ThrWorker::process()
{
stopped = false;
auto newSD = QueryClient::Get().Update(SystemAddress(addr.toUtf8(), port));
if (dialog != nullptr)
dialog->setData(newSD);
stopped = true;
emit finished();
}
ServerInfoDialog::ServerInfoDialog(const QString &addr, QWidget *parent): QDialog(parent)
{
setupUi(this);
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
refreshThread = new QThread;
QStringList list = addr.split(':');
worker = new ThrWorker(this, list[0].toLatin1(), list[1].toUShort());
worker->moveToThread(refreshThread);
connect(refreshThread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), refreshThread, SLOT(quit()));
connect(refreshThread, SIGNAL(finished()), this, SLOT(refresh()));
connect(btnRefresh, &QPushButton::clicked, [this]{
if (!refreshThread->isRunning())
refreshThread->start();
});
}
ServerInfoDialog::~ServerInfoDialog()
{
worker->dialog = nullptr;
if (!refreshThread->isRunning())
refreshThread->terminate();
}
bool ServerInfoDialog::isUpdated()
{
return sd.first != UNASSIGNED_SYSTEM_ADDRESS;
}
void ServerInfoDialog::Server(QString addr)
void ServerInfoDialog::setData(std::pair<RakNet::SystemAddress, QueryData> &newSD)
{
this->addr = addr;
sd = newSD;
}
bool ServerInfoDialog::refresh()
void ServerInfoDialog::refresh()
{
QStringList list = addr.split(':');
auto sd = QueryClient::Get().Update(SystemAddress(list[0].toLatin1(), list[1].toUShort()));
if (sd.first != UNASSIGNED_SYSTEM_ADDRESS)
{
leAddr->setText(sd.first.ToString(true, ':'));
@ -41,17 +74,16 @@ bool ServerInfoDialog::refresh()
btnConnect->setDisabled(ping == PING_UNREACHABLE);
listPlayers->clear();
for (auto player : sd.second.players)
for (const auto &player : sd.second.players)
listPlayers->addItem(QString::fromStdString(player));
listPlugins->clear();
for (auto plugin : sd.second.plugins)
for (const auto &plugin : sd.second.plugins)
listPlugins->addItem(QString::fromStdString(plugin.name));
listRules->clear();
const static vector<std::string> defaultRules {"gamemode", "maxPlayers", "name", "passw", "players", "version"};
for (auto rule : sd.second.rules)
for (auto &rule : sd.second.rules)
{
if (::find(defaultRules.begin(), defaultRules.end(), rule.first) != defaultRules.end())
continue;
@ -64,7 +96,12 @@ bool ServerInfoDialog::refresh()
}
lblPlayers->setText(QString::number(sd.second.players.size()) + " / " + QString::number(sd.second.GetMaxPlayers()));
return true;
}
return false;
}
int ServerInfoDialog::exec()
{
if (!refreshThread->isRunning())
refreshThread->start();
return QDialog::exec();
}

@ -6,18 +6,45 @@
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
#include "ui_ServerInfo.h"
#include <apps/browser/netutils/Utils.hpp>
#include <RakNetTypes.h>
#include <components/openmw-mp/Master/MasterData.hpp>
class ThrWorker;
class ServerInfoDialog : public QDialog, public Ui::Dialog
{
Q_OBJECT
public:
explicit ServerInfoDialog(QWidget *parent = 0);
virtual ~ServerInfoDialog();
void Server(QString addr);
explicit ServerInfoDialog(const QString &addr, QWidget *parent = nullptr);
~ServerInfoDialog() override;
bool isUpdated();
void setData(std::pair<RakNet::SystemAddress, QueryData> &newSD);
public slots:
void refresh();
int exec() Q_DECL_OVERRIDE;
private:
QThread *refreshThread;
ThrWorker* worker;
std::pair<RakNet::SystemAddress, QueryData> sd;
};
class ThrWorker: public QObject
{
friend class ServerInfoDialog;
Q_OBJECT
public:
ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port);
public slots:
bool refresh();
void process();
signals:
void finished();
private:
QString addr;
unsigned short port;
bool stopped;
ServerInfoDialog *dialog;
};

@ -1,16 +1,11 @@
#include <qmessagebox.h>
#include "ServerModel.hpp"
#include <qdebug.h>
#include <apps/browser/netutils/Utils.hpp>
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
{
}
ServerModel::~ServerModel()
{
}
/*QHash<int, QByteArray> ServerModel::roleNames() const
{
return roles;
@ -49,7 +44,7 @@ QVariant ServerModel::data(const QModelIndex &index, int role) const
var = QString(sd.rules.at("name").str.c_str());
break;
case ServerData::PING:
var = sd.ping;
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
break;
case ServerData::MODNAME:
if (sd.rules.at("gamemode").str == "")
@ -136,7 +131,7 @@ bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int r
sd.SetPassword(value.toBool());
break;
case ServerData::VERSION:
sd.SetVersion(value.toString().toLatin1());
sd.SetVersion(value.toString().toUtf8());
ok = !sd.addr.isEmpty();
break;
case ServerData::PLAYERS:
@ -146,14 +141,14 @@ bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int r
sd.SetMaxPlayers(value.toInt(&ok));
break;
case ServerData::HOSTNAME:
sd.SetName(value.toString().toLatin1());
sd.SetName(value.toString().toUtf8());
ok = !sd.addr.isEmpty();
break;
case ServerData::PING:
sd.ping = value.toInt(&ok);
break;
case ServerData::MODNAME:
sd.SetGameMode(value.toString().toLatin1());
sd.SetGameMode(value.toString().toUtf8());
break;
default:
return false;
@ -170,9 +165,7 @@ bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
Q_UNUSED(index);
beginInsertRows(QModelIndex(), position, position + count - 1);
for (int row = 0; row < count; ++row) {
myData.insert(position, {});
}
myData.insert(position, count, {});
endInsertRows();
return true;

@ -29,8 +29,7 @@ class ServerModel: public QAbstractTableModel
{
Q_OBJECT
public:
explicit ServerModel(QObject *parent = 0);
~ServerModel();
explicit ServerModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;

@ -0,0 +1,198 @@
//
// Created by koncord on 31.03.18.
//
#include "Settings.hpp"
#include <QDebug>
#include <components/files/configurationmanager.hpp>
#include <apps/browser/netutils/QueryClient.hpp>
#include "MainWindow.hpp"
std::string loadSettings (Settings::Manager & settings, const std::string &cfgName)
{
Files::ConfigurationManager mCfgMgr;
// Create the settings manager and load default settings file
const std::string localdefault = (mCfgMgr.getLocalPath() / (cfgName + "-default.cfg")).string();
const std::string globaldefault = (mCfgMgr.getGlobalPath() / (cfgName + "-default.cfg")).string();
// prefer local
if (boost::filesystem::exists(localdefault))
settings.loadDefault(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadDefault(globaldefault);
else
throw std::runtime_error ("No default settings file found! Make sure the file \"" + cfgName + "-default.cfg\" was properly installed.");
// load user settings if they exist
const std::string settingspath = (mCfgMgr.getUserConfigPath() / (cfgName + ".cfg")).string();
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);
return settingspath;
}
SettingsMgr::SettingsMgr()
{
clientCfg = loadSettings(clientMgr, "tes3mp-client");
switchMgr();
serverCfg = loadSettings(serverMgr, "tes3mp-server");
switchMgr();
std::string addr = clientMgr.getString("address", "Master");
int port = clientMgr.getInt("port", "Master");
QueryClient::Get().SetServer(addr, port);
}
void SettingsMgr::loadBrowserSettings(Ui::MainWindow &mw)
{
mw.comboLatency->setCurrentIndex(clientMgr.getInt("maxLatency", "Browser"));
mw.leGamemode->setText(QString::fromStdString(clientMgr.getString("gameMode", "Browser")));
mw.cBoxNotFull->setCheckState(clientMgr.getBool("notFull", "Browser") ? Qt::Checked : Qt::Unchecked);
mw.cBoxWithPlayers->setCheckState(clientMgr.getBool("withPlayers", "Browser") ? Qt::Checked : Qt::Unchecked);
mw.cBBoxWOPass->setCheckState(clientMgr.getBool("noPassword", "Browser") ? Qt::Checked : Qt::Unchecked);
mw.tblServerBrowser->sortByColumn(clientMgr.getInt("sortByCol", "Browser"),
clientMgr.getBool("sortByColAscending", "Browser") ? Qt::AscendingOrder : Qt::DescendingOrder);
loadClientSettings(mw);
switchMgr();
loadServerSettings(mw);
switchMgr();
}
void SettingsMgr::saveBrowserSettings(Ui::MainWindow &mw)
{
clientMgr.setInt("maxLatency", "Browser", mw.comboLatency->currentIndex());
clientMgr.setString("gameMode", "Browser", mw.leGamemode->text().toStdString());
clientMgr.setBool("notFull", "Browser", mw.cBoxNotFull->checkState() == Qt::Checked);
clientMgr.setBool("withPlayers", "Browser", mw.cBoxWithPlayers->checkState() == Qt::Checked);
clientMgr.setBool("noPassword", "Browser", mw.cBBoxWOPass->checkState() == Qt::Checked);
clientMgr.setInt("sortByCol", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorSection());
clientMgr.setBool("sortByColAscending", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder);
saveClientSettings(mw);
clientMgr.saveUser(clientCfg);
switchMgr();
saveServerSettings(mw);
switchMgr();
}
void SettingsMgr::loadClientSettings(Ui::MainWindow &mw)
{
mw.leClientAddress->setText(QString::fromStdString(clientMgr.getString("destinationAddress", "General")));
mw.leClientPort->setText(QString::fromStdString(clientMgr.getString("port", "General")));
mw.leClientPassword->setText(QString::fromStdString(clientMgr.getString("password", "General")));
mw.combLoglevel->setCurrentIndex(clientMgr.getInt("logLevel", "General"));
mw.leClientMAddress->setText(QString::fromStdString(clientMgr.getString("address", "Master")));
mw.leClientMPort->setText(QString::fromStdString(clientMgr.getString("port", "Master")));
mw.pbChatKey->setText(QString::fromStdString(clientMgr.getString("keySay", "Chat")));
mw.pbModeKey->setText(QString::fromStdString(clientMgr.getString("keyChatMode", "Chat")));
mw.sbPosX->setValue(clientMgr.getInt("x", "Chat"));
mw.sbPosY->setValue(clientMgr.getInt("y", "Chat"));
mw.sbPosW->setValue(clientMgr.getInt("w", "Chat"));
mw.sbPosH->setValue(clientMgr.getInt("h", "Chat"));
mw.sbDelay->setValue(clientMgr.getFloat("delay", "Chat"));
}
void SettingsMgr::saveClientSettings(Ui::MainWindow &mw)
{
clientMgr.setString("destinationAddress", "General", mw.leClientAddress->text().toStdString());
clientMgr.setString("port", "General", mw.leClientPort->text().toStdString());
clientMgr.setString("password", "General", mw.leClientPassword->text().toStdString());
clientMgr.setInt("logLevel", "General", mw.combLoglevel->currentIndex());
clientMgr.setString("address", "Master", mw.leClientMAddress->text().toStdString());
clientMgr.setString("port", "Master", mw.leClientMPort->text().toStdString());
clientMgr.setString("keySay", "Chat", mw.pbChatKey->text().toStdString());
clientMgr.setString("keyChatMode", "Chat", mw.pbModeKey->text().toStdString());
clientMgr.setInt("x", "Chat", mw.sbPosX->value());
clientMgr.setInt("y", "Chat", mw.sbPosY->value());
clientMgr.setInt("w", "Chat", mw.sbPosW->value());
clientMgr.setInt("h", "Chat", mw.sbPosH->value());
clientMgr.setFloat("delay", "Chat", mw.sbDelay->value());
}
void SettingsMgr::loadServerSettings(Ui::MainWindow &mw)
{
mw.leServerAddress->setText(QString::fromStdString(serverMgr.getString("localAddress", "General")));
mw.leServerPort->setText(QString::fromStdString(serverMgr.getString("port", "General")));
mw.sbMaxPlayers->setValue(serverMgr.getInt("maximumPlayers", "General"));
mw.leHostname->setText(QString::fromStdString(serverMgr.getString("hostname", "General")));
mw.combServerLoglevel->setCurrentIndex(serverMgr.getInt("logLevel", "General"));
mw.leServerPassword->setText(QString::fromStdString(serverMgr.getString("password", "General")));
mw.chbMSenabled->setCheckState(serverMgr.getBool("enabled", "MasterServer") ? Qt::Checked : Qt::Unchecked);
mw.leServerMaddress->setText(QString::fromStdString(serverMgr.getString("address", "MasterServer")));
mw.leServerMPort->setText(QString::fromStdString(serverMgr.getString("port", "MasterServer")));
mw.sbRate->setValue(serverMgr.getInt("rate", "MasterServer"));
#ifndef TES3MP_PRE07
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Modules")));
mw.chbAutosort->setCheckState(serverMgr.getBool("autoSort", "Modules") ? Qt::Checked : Qt::Unchecked);
#else
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Plugins")));
#endif
}
void SettingsMgr::saveServerSettings(Ui::MainWindow &mw)
{
serverMgr.setString("localAddress", "General", mw.leServerAddress->text().toStdString());
serverMgr.setString("port", "General", mw.leServerPort->text().toStdString());
serverMgr.setInt("maximumPlayers", "General", mw.sbMaxPlayers->value());
serverMgr.setString("hostname", "General", mw.leHostname->text().toStdString());
serverMgr.setInt("logLevel", "General", mw.combServerLoglevel->currentIndex());
serverMgr.setString("password", "General", mw.leServerPassword->text().toStdString());
serverMgr.setBool("enabled", "MasterServer", mw.chbMSenabled->checkState() == Qt::Checked);
serverMgr.setString("address", "MasterServer", mw.leServerMaddress->text().toStdString());
serverMgr.setString("port", "MasterServer", mw.leServerMPort->text().toStdString());
serverMgr.setInt("rate", "MasterServer", mw.sbRate->value());
#ifndef TES3MP_PRE07
serverMgr.setString("home", "Modules", mw.leModulePath->text().toStdString());
serverMgr.setBool("autoSort", "Modules", mw.chbAutosort->checkState() == Qt::Checked);
#else
serverMgr.setString("home", "Plugins", mw.leModulePath->text().toStdString());
#endif
serverMgr.saveUser(serverCfg);
}
void SettingsMgr::switchMgr()
{
static Settings::CategorySettingValueMap saveUserSettings;
static Settings::CategorySettingValueMap saveDefaultSettings;
static Settings::CategorySettingVector saveChangedSettings;
static bool currentMgrIsClient = true;
if(!currentMgrIsClient)
{
saveUserSettings.swap(clientMgr.mUserSettings);
saveDefaultSettings.swap(clientMgr.mDefaultSettings);
saveChangedSettings.swap(clientMgr.mChangedSettings);
qDebug() << "Manager switched to Client config";
}
else
{
saveUserSettings.swap(serverMgr.mUserSettings);
saveDefaultSettings.swap(serverMgr.mDefaultSettings);
saveChangedSettings.swap(serverMgr.mChangedSettings);
qDebug() << "Manager switched to Server config";
}
currentMgrIsClient = !currentMgrIsClient;
}

@ -0,0 +1,32 @@
//
// Created by koncord on 31.03.18.
//
#pragma once
#include <components/settings/settings.hpp>
namespace Ui
{
class MainWindow;
}
class SettingsMgr
{
public:
SettingsMgr();
void loadBrowserSettings(Ui::MainWindow &mw);
void saveBrowserSettings(Ui::MainWindow &mw);
void loadClientSettings(Ui::MainWindow &mw);
void saveClientSettings(Ui::MainWindow &mw);
void loadServerSettings(Ui::MainWindow &mw);
void saveServerSettings(Ui::MainWindow &mw);
private:
Settings::Manager serverMgr, clientMgr;
std::string serverCfg, clientCfg;
void switchMgr();
};

@ -4,42 +4,8 @@
#include <apps/browser/netutils/QueryClient.hpp>
#include "MainWindow.hpp"
std::string loadSettings (Settings::Manager & settings)
{
Files::ConfigurationManager mCfgMgr;
// Create the settings manager and load default settings file
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
// prefer local
if (boost::filesystem::exists(localdefault))
settings.loadDefault(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadDefault(globaldefault);
else
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
// load user settings if they exist
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);
return settingspath;
}
int main(int argc, char *argv[])
{
Settings::Manager mgr;
loadSettings(mgr);
std::string addr = mgr.getString("address", "Master");
int port = mgr.getInt("port", "Master");
// initialize resources, if needed
// Q_INIT_RESOURCE(resfile);
QueryClient::Get().SetServer(addr, port);
QApplication app(argc, argv);
MainWindow d;

@ -30,7 +30,7 @@ QueryClient::~QueryClient()
RakPeerInterface::DestroyInstance(peer);
}
void QueryClient::SetServer(std::string addr, unsigned short port)
void QueryClient::SetServer(const string &addr, unsigned short port)
{
masterAddr = SystemAddress(addr.c_str(), port);
}
@ -83,7 +83,7 @@ map<SystemAddress, QueryData> QueryClient::Query()
return query;
}
pair<SystemAddress, QueryData> QueryClient::Update(RakNet::SystemAddress addr)
pair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)
{
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
pair<SystemAddress, QueryData> server;
@ -179,7 +179,7 @@ ConnectionState QueryClient::Connect()
{
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
strlen(TES3MP_MASTERSERVER_PASSW), 0, 0, 5, 500);
strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);
while (true)
{

@ -14,16 +14,16 @@
class QueryClient
{
private:
public:
QueryClient(QueryClient const &) = delete;
QueryClient(QueryClient &&) = delete;
QueryClient &operator=(QueryClient const &) = delete;
QueryClient &operator=(QueryClient &&) = delete;
public:
static QueryClient &Get();
void SetServer(std::string addr, unsigned short port);
void SetServer(const std::string &addr, unsigned short port);
std::map<RakNet::SystemAddress, QueryData> Query();
std::pair<RakNet::SystemAddress, QueryData> Update(RakNet::SystemAddress addr);
std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);
int Status();
private:
RakNet::ConnectionState Connect();

@ -44,11 +44,15 @@ unsigned int PingRakNetServer(const char *addr, unsigned short port)
break;
case ID_CONNECTED_PING:
case ID_UNCONNECTED_PONG:
{
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
bsIn.Read(time);
time = now - time;
done = true;
break;
}
default:
break;
}
peer->DeallocatePacket(packet);
}
@ -69,9 +73,9 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
sstr << TES3MP_VERSION;
sstr << TES3MP_PROTO_VERSION;
std::string msg = "";
std::string msg;
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
msg = "Connection attempt failed.\n";
@ -142,14 +146,14 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
{
RakNet::RakString str;
bs.Read(str);
data.players.push_back(str.C_String());
data.players.emplace_back(str.C_String());
}
bs.Read(length);
for (size_t i = 0; i < length; i++)
{
RakNet::RakString str;
bs.Read(str);
data.plugins.push_back(str.C_String());
data.plugins.emplace_back(str.C_String());
}
done = true;
}

@ -0,0 +1,69 @@
//
// Created by koncord on 04.09.17.
//
#include "AdminRest.hpp"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "RestUtils.hpp"
using namespace std;
using namespace chrono;
using namespace boost::property_tree;
AdminRest::AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile,
unsigned short port, std::shared_ptr<MasterServer> master) : httpServer(cert, key, verifyFile), master(master)
{
httpServer.config.port = port;
}
void AdminRest::start()
{
static const string AdminArea = "^/api/admin?";
httpServer.resource[AdminArea]["POST"] = [this](auto response, auto request) {
cout << request->method << endl;
cout << request->path << endl;
cout << request->http_version << endl;
for (auto &header : request->header)
cout << header.first << ": " << header.second << endl;
string resp;
master->luaStuff([&request, &response, &resp](sol::state &state) {
sol::protected_function func = state["OnAdminRequest"];
sol::protected_function_result result = func.call(request->remote_endpoint_address, request->content.string());
if (result.valid())
*response << result.get<string>();
else
{
cerr << "Error: " << result.get<string>() << endl;
*response << response500;
}
});
};
/*httpServer.on_error = [](auto request, const boost::system::error_code& err)
{
std::cerr << "Error: " << err.message() << " " << err.category().name() << std::endl;
};*/
httpServer.default_resource["GET"] = [](auto response, auto /*request*/) {
cout << "Default request" << endl;
*response << response400;
};
thr = thread([this](){httpServer.start();});
}
void AdminRest::stop()
{
httpServer.stop();
if(thr.joinable())
thr.join();
}

@ -0,0 +1,25 @@
//
// Created by koncord on 04.09.17.
//
#pragma once
#include "SimpleWeb/https_server.hpp"
#include "MasterServer.hpp"
typedef SimpleWeb::Server<SimpleWeb::HTTPS> HttpsServer;
class AdminRest
{
public:
AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile, unsigned short port, std::shared_ptr<MasterServer> master);
void start();
void stop();
private:
HttpsServer httpServer;
std::shared_ptr<MasterServer> master;
std::thread thr;
};

@ -1,14 +1,13 @@
project(masterserver)
#set(CMAKE_CXX_STANDARD 14)
add_definitions(-std=gnu++14)
include_directories("./")
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp)
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp AdminRest.cpp)
add_executable(masterserver ${SOURCE_FILES})
target_link_libraries(masterserver ${RakNet_LIBRARY} components)
target_link_libraries(masterserver ${RakNet_LIBRARY} ${LuaJit_LIBRARIES} ${OPENSSL_LIBRARIES} components)
set_property(TARGET masterserver PROPERTY CXX_STANDARD 14)
option(BUILD_MASTER_TEST "build master server test program" OFF)

@ -12,19 +12,54 @@
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Utils.hpp>
#include <boost/filesystem.hpp>
using namespace RakNet;
using namespace std;
using namespace mwmp;
using namespace chrono;
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
MasterServer::MasterServer(const std::string &luaScript)
{
state.open_libraries();
boost::filesystem::path absPath = boost::filesystem::absolute(luaScript);
std::string package_path = state["package"]["path"];
state["package"]["path"] = Utils::convertPath(absPath.parent_path().string() + "/?.lua") + ";" + package_path;
state.set_function("BanAddress", &MasterServer::ban, this);
state.set_function("UnbanAddress", &MasterServer::unban, this);
state.script_file(luaScript);
sol::table config = state["config"];
if (config.get_type() != sol::type::table)
throw runtime_error("config is not correct");
sol::object maxConnections = config["maxConnections"];
if (maxConnections.get_type() != sol::type::number)
throw runtime_error("config.maxConnections is not correct");
sol::object port = config["port"];
if (port.get_type() != sol::type::number)
throw runtime_error("config.port is not correct");
state.new_usertype<MasterServer::SServer>("Server",
"name", sol::property(&MasterServer::SServer::GetName),
"gamemode", sol::property(&MasterServer::SServer::GetGameMode),
"version", sol::property(&MasterServer::SServer::GetVersion)
);
peer = RakPeerInterface::GetInstance();
sockdescr = SocketDescriptor(port, 0);
peer->Startup(maxConnections, &sockdescr, 1, 1000);
sockdescr = SocketDescriptor(port.as<unsigned short>(), nullptr);
peer->Startup(maxConnections.as<unsigned short>(), &sockdescr, 1, 1000);
peer->SetLimitIPConnectionFrequency(true);
peer->SetMaximumIncomingConnections(maxConnections);
peer->SetMaximumIncomingConnections(maxConnections.as<unsigned short>());
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
run = false;
}
@ -52,6 +87,13 @@ void MasterServer::Thread()
PacketMasterAnnounce pma(peer);
pma.SetSendStream(&send);
luaStuff([](sol::state &state) {
sol::protected_function func = state["OnInit"];
sol::protected_function_result result = func.call();
if (!result.valid())
cerr << "Error: " << result.get<string>() << endl;
});
while (run)
{
Packet *packet = peer->Receive();
@ -67,9 +109,9 @@ void MasterServer::Thread()
servers.erase(it++);
else ++it;
}
for(auto id = pendingACKs.begin(); id != pendingACKs.end();)
for (auto id = pendingACKs.begin(); id != pendingACKs.end();)
{
if(now - id->second >= 30s)
if (now - id->second >= 30s)
{
cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
peer->CloseConnection(id->first, true);
@ -113,7 +155,7 @@ void MasterServer::Thread()
SystemAddress addr;
data.Read(addr); // update 1 server
ServerIter it = servers.find(addr);
auto it = servers.find(addr);
if (it != servers.end())
{
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
@ -127,7 +169,7 @@ void MasterServer::Thread()
}
case ID_MASTER_ANNOUNCE:
{
ServerIter iter = servers.find(packet->systemAddress);
auto iter = servers.find(packet->systemAddress);
pma.SetReadStream(&data);
SServer server;
@ -141,6 +183,26 @@ void MasterServer::Thread()
pendingACKs[packet->guid] = steady_clock::now();
};
auto isServerValid = [&](const SServer &sserver) {
bool ret = false;
auto addr = packet->systemAddress.ToString(false);
lock_guard<mutex> lock(banMutex);
if (peer->IsBanned(addr)) // check if address is banned
return false;
luaStuff([&ret, &packet, &sserver, &addr](sol::state &state) {
sol::protected_function func = state["OnServerAnnounce"];
sol::protected_function_result result = func.call(addr, sserver);
if (result.valid())
ret = result.get<bool>();
else
cerr << "Error: " << result.get<string>() << endl;
});
return ret;
};
if (iter != servers.end())
{
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
@ -152,9 +214,19 @@ void MasterServer::Thread()
}
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{
cout << "Updated";
iter->second = server;
keepAliveFunc();
if (isServerValid(server))
{
cout << "Updated";
iter->second = server;
keepAliveFunc();
}
else
{
cout << "Update rejected";
servers.erase(iter);
pendingACKs.erase(packet->guid);
}
}
else
{
@ -164,9 +236,14 @@ void MasterServer::Thread()
}
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{
cout << "Added";
iter = servers.insert({packet->systemAddress, server}).first;
keepAliveFunc();
if (isServerValid(server))
{
cout << "Added";
iter = servers.insert({packet->systemAddress, server}).first;
keepAliveFunc();
}
else
cout << "Adding rejected";
}
else
{
@ -186,7 +263,8 @@ void MasterServer::Thread()
peer->CloseConnection(packet->systemAddress, true);
break;
default:
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length " << packet->length << " from " << packet->systemAddress.ToString() << endl;
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length "
<< packet->length << " from " << packet->systemAddress.ToString() << endl;
peer->CloseConnection(packet->systemAddress, true);
}
}
@ -234,3 +312,22 @@ MasterServer::ServerMap *MasterServer::GetServers()
{
return &servers;
}
void MasterServer::luaStuff(std::function<void(sol::state &)> f)
{
lock_guard<mutex> lock(luaMutex);
f(state);
}
void MasterServer::ban(const std::string &addr)
{
lock_guard<mutex> lock(banMutex);
peer->AddToBanList(addr.c_str());
}
void MasterServer::unban(const std::string &addr)
{
lock_guard<mutex> lock(banMutex);
peer->RemoveFromBanList(addr.c_str());
}

@ -9,18 +9,12 @@
#include <chrono>
#include <RakPeerInterface.h>
#include <components/openmw-mp/Master/MasterData.hpp>
#include <sol.hpp>
#include <mutex>
class MasterServer
{
public:
struct Ban
{
RakNet::SystemAddress sa;
bool permanent;
struct Date
{
} date;
};
struct SServer : QueryData
{
std::chrono::steady_clock::time_point lastUpdate;
@ -29,7 +23,7 @@ public:
//typedef ServerMap::const_iterator ServerCIter;
typedef ServerMap::iterator ServerIter;
MasterServer(unsigned short maxConnections, unsigned short port);
explicit MasterServer(const std::string &luaScript);
~MasterServer();
void Start();
@ -38,6 +32,10 @@ public:
void Wait();
ServerMap* GetServers();
void luaStuff(std::function<void(sol::state &)> f);
void ban(const std::string &addr);
void unban(const std::string &addr);
private:
void Thread();
@ -49,6 +47,9 @@ private:
ServerMap servers;
bool run;
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
sol::state state;
std::mutex luaMutex;
std::mutex banMutex;
};

@ -7,22 +7,12 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "RestUtils.hpp"
using namespace std;
using namespace chrono;
using namespace boost::property_tree;
static string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
static string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
static string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
inline void ResponseStr(HttpServer::Response &response, string content, string type = "", string code = "200 OK")
{
response << "HTTP/1.1 " << code << "\r\n";
if (!type.empty())
response << "Content-Type: " << type <<"\r\n";
response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
}
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
{
server.SetName(pt.get<string>("hostname").c_str());
@ -70,7 +60,7 @@ void RestServer::start()
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
ss << "}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
}
catch(out_of_range e)
{
@ -93,7 +83,7 @@ void RestServer::start()
ss << ", ";
}
ss << "}}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
updatedCache = false;
}
*response << str;
@ -110,7 +100,7 @@ void RestServer::start()
MasterServer::SServer server;
ptreeToServer(pt, server);
unsigned short port = pt.get<unsigned short>("port");
auto port = pt.get<unsigned short>("port");
server.lastUpdate = steady_clock::now();
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
updatedCache = true;
@ -137,7 +127,6 @@ void RestServer::start()
*response << response400;
return;
}
if (request->content.size() != 0)
{
try
@ -171,14 +160,14 @@ void RestServer::start()
ss << ", \"players\": " << players;
ss << "}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
};
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
*response << response400;
};
httpServer.start();
thr = thread([this](){httpServer.start();});
}
void RestServer::cacheUpdated()
@ -189,4 +178,6 @@ void RestServer::cacheUpdated()
void RestServer::stop()
{
httpServer.stop();
if(thr.joinable())
thr.join();
}

@ -24,6 +24,7 @@ private:
HttpServer httpServer;
MasterServer::ServerMap *serverMap;
bool updatedCache = true;
std::thread thr;
};

@ -0,0 +1,25 @@
//
// Created by koncord on 04.09.17.
//
#pragma once
#include <string>
#include "SimpleWeb/base_server.hpp"
static std::string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
static std::string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
static std::string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
static std::string response403 = "HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\n\r\nForbidden";
static std::string response500 = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\nInternal Server Error";
template <class Protocol>
inline void ResponseStr(std::shared_ptr<typename SimpleWeb::ServerBase<Protocol>::Response> response,
std::string content, std::string type = "", std::string code = "200 OK")
{
*response << "HTTP/1.1 " << code << "\r\n";
if (!type.empty())
*response << "Content-Type: " << type <<"\r\n";
*response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
}

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Ole Christian Eidheim
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 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.

@ -1,72 +1,137 @@
#ifndef BASE_SERVER_HPP
#define BASE_SERVER_HPP
#pragma once
#include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <map>
#include <unordered_map>
#include <thread>
#include "utility.hpp"
#include <condition_variable>
#include <functional>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <unordered_set>
#include <regex>
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
#define CASE_INSENSITIVE_EQUALS_AND_HASH
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
struct case_insensitive_equals
{
bool operator()(const std::string &key1, const std::string &key2) const
{
return boost::algorithm::iequals(key1, key2);
}
};
struct case_insensitive_hash
{
size_t operator()(const std::string &key) const
{
std::size_t seed = 0;
for (auto &c: key)
boost::hash_combine(seed, std::tolower(c));
return seed;
}
};
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
using error_code = std::error_code;
using errc = std::errc;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
namespace SimpleWeb
{
template<class socket_type>
namespace SimpleWeb {
template <class socket_type>
class Server;
template<class socket_type>
class ServerBase
{
public:
virtual ~ServerBase()
{}
template <class socket_type>
class ServerBase {
protected:
class Session;
class Response : public std::ostream
{
public:
class Response : public std::enable_shared_from_this<Response>, public std::ostream {
friend class ServerBase<socket_type>;
friend class Server<socket_type>;
asio::streambuf streambuf;
boost::asio::streambuf streambuf;
std::shared_ptr<Session> session;
long timeout_content;
std::shared_ptr<socket_type> socket;
Response(std::shared_ptr<Session> session, long timeout_content) noexcept : std::ostream(&streambuf), session(std::move(session)), timeout_content(timeout_content) {}
Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket)
{}
template <typename size_type>
void write_header(const CaseInsensitiveMultimap &header, size_type size) {
bool content_length_written = false;
bool chunked_transfer_encoding = false;
for(auto &field : header) {
if(!content_length_written && case_insensitive_equal(field.first, "content-length"))
content_length_written = true;
else if(!chunked_transfer_encoding && case_insensitive_equal(field.first, "transfer-encoding") && case_insensitive_equal(field.second, "chunked"))
chunked_transfer_encoding = true;
*this << field.first << ": " << field.second << "\r\n";
}
if(!content_length_written && !chunked_transfer_encoding && !close_connection_after_response)
*this << "Content-Length: " << size << "\r\n\r\n";
else
*this << "\r\n";
}
public:
size_t size()
{
size_t size() noexcept {
return streambuf.size();
}
/// Use this function if you need to recursively send parts of a longer message
void send(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
session->connection->set_timeout(timeout_content);
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) {
self->session->connection->cancel_timeout();
auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(callback)
callback(ec);
});
}
/// Write directly to stream buffer using std::ostream::write
void write(const char_type *ptr, std::streamsize n) {
std::ostream::write(ptr, n);
}
/// Convenience function for writing status line, potential header fields, and empty content
void write(StatusCode status_code = StatusCode::success_ok, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, 0);
}
/// Convenience function for writing status line, header fields, and content
void write(StatusCode status_code, const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, content.size());
if(!content.empty())
*this << content;
}
/// Convenience function for writing status line, header fields, and content
void write(StatusCode status_code, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
content.seekg(0, std::ios::end);
auto size = content.tellg();
content.seekg(0, std::ios::beg);
write_header(header, size);
if(size)
*this << content.rdbuf();
}
/// Convenience function for writing success status line, header fields, and content
void write(const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header);
}
/// Convenience function for writing success status line, header fields, and content
void write(std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header);
}
/// Convenience function for writing success status line, and header fields
void write(const CaseInsensitiveMultimap &header) {
write(StatusCode::success_ok, std::string(), header);
}
/// If true, force server to close the connection after the response have been sent.
///
/// This is useful when implementing a HTTP/1.0-server sending content
@ -74,438 +139,399 @@ namespace SimpleWeb
bool close_connection_after_response = false;
};
class Content : public std::istream
{
class Content : public std::istream {
friend class ServerBase<socket_type>;
public:
size_t size()
{
size_t size() noexcept {
return streambuf.size();
}
std::string string()
{
std::stringstream ss;
ss << rdbuf();
return ss.str();
/// Convenience function to return std::string. The stream buffer is consumed.
std::string string() noexcept {
try {
std::stringstream ss;
ss << rdbuf();
return ss.str();
}
catch(...) {
return std::string();
}
}
private:
boost::asio::streambuf &streambuf;
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
{}
asio::streambuf &streambuf;
Content(asio::streambuf &streambuf) noexcept : std::istream(&streambuf), streambuf(streambuf) {}
};
class Request
{
class Request {
friend class ServerBase<socket_type>;
friend class Server<socket_type>;
friend class Session;
public:
std::string method, path, http_version;
std::string method, path, query_string, http_version;
Content content;
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
CaseInsensitiveMultimap header;
std::smatch path_match;
std::string remote_endpoint_address;
unsigned short remote_endpoint_port;
/// Returns query keys with percent-decoded values.
CaseInsensitiveMultimap parse_query_string() noexcept {
return SimpleWeb::QueryString::parse(query_string);
}
private:
Request(const socket_type &socket) : content(streambuf)
{
try
{
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
asio::streambuf streambuf;
Request(const std::string &remote_endpoint_address = std::string(), unsigned short remote_endpoint_port = 0) noexcept
: content(streambuf), remote_endpoint_address(remote_endpoint_address), remote_endpoint_port(remote_endpoint_port) {}
};
protected:
class Connection : public std::enable_shared_from_this<Connection> {
public:
template <typename... Args>
Connection(std::shared_ptr<ScopeRunner> handler_runner, Args &&... args) noexcept : handler_runner(std::move(handler_runner)), socket(new socket_type(std::forward<Args>(args)...)) {}
std::shared_ptr<ScopeRunner> handler_runner;
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
std::mutex socket_close_mutex;
std::unique_ptr<asio::steady_timer> timer;
void close() noexcept {
error_code ec;
std::unique_lock<std::mutex> lock(socket_close_mutex); // The following operations seems to be needed to run sequentially
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
socket->lowest_layer().close(ec);
}
void set_timeout(long seconds) noexcept {
if(seconds == 0) {
timer = nullptr;
return;
}
timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket->get_io_service()));
timer->expires_from_now(std::chrono::seconds(seconds));
auto self = this->shared_from_this();
timer->async_wait([self](const error_code &ec) {
if(!ec)
self->close();
});
}
void cancel_timeout() noexcept {
if(timer) {
error_code ec;
timer->cancel(ec);
}
}
};
class Session {
public:
Session(std::shared_ptr<Connection> connection) noexcept : connection(std::move(connection)) {
try {
auto remote_endpoint = this->connection->socket->lowest_layer().remote_endpoint();
request = std::shared_ptr<Request>(new Request(remote_endpoint.address().to_string(), remote_endpoint.port()));
}
catch(...) {
request = std::shared_ptr<Request>(new Request());
}
catch (...)
{}
}
boost::asio::streambuf streambuf;
std::shared_ptr<Connection> connection;
std::shared_ptr<Request> request;
};
class Config
{
public:
class Config {
friend class ServerBase<socket_type>;
Config(unsigned short port) : port(port)
{}
Config(unsigned short port) noexcept : port(port) {}
public:
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
unsigned short port;
/// Number of threads that the server will use when start() is called. Defaults to 1 thread.
/// If io_service is not set, number of threads that the server will use when start() is called.
/// Defaults to 1 thread.
size_t thread_pool_size = 1;
/// Timeout on request handling. Defaults to 5 seconds.
size_t timeout_request = 5;
long timeout_request = 5;
/// Timeout on content handling. Defaults to 300 seconds.
size_t timeout_content = 300;
long timeout_content = 300;
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
/// If empty, the address will be any address.
std::string address;
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
bool reuse_address = true;
};
///Set before calling start().
/// Set before calling start().
Config config;
private:
class regex_orderable : public std::regex
{
class regex_orderable : public std::regex {
std::string str;
public:
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)
{}
regex_orderable(const std::string &regex_str) : std::regex(regex_str), str(regex_str)
{}
bool operator<(const regex_orderable &rhs) const
{
public:
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr) {}
regex_orderable(std::string regex_str) : std::regex(regex_str), str(std::move(regex_str)) {}
bool operator<(const regex_orderable &rhs) const noexcept {
return str < rhs.str;
}
};
public:
/// Warning: do not add or remove resources after start() is called
std::map<regex_orderable, std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
resource;
std::map<regex_orderable, std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>>> resource;
std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
std::function<
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
const boost::system::error_code &)>
on_error;
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
std::function<void(std::shared_ptr<socket_type> socket,
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
/// If you have your own asio::io_service, store its pointer here before running start().
std::shared_ptr<asio::io_service> io_service;
virtual void start()
{
if (!io_service)
io_service = std::make_shared<boost::asio::io_service>();
virtual void start() {
if(!io_service) {
io_service = std::make_shared<asio::io_service>();
internal_io_service = true;
}
if (io_service->stopped())
if(io_service->stopped())
io_service->reset();
boost::asio::ip::tcp::endpoint endpoint;
if (config.address.size() > 0)
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),
config.port);
asio::ip::tcp::endpoint endpoint;
if(config.address.size() > 0)
endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
else
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port);
if (!acceptor)
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(
new boost::asio::ip::tcp::acceptor(*io_service));
if(!acceptor)
acceptor = std::unique_ptr<asio::ip::tcp::acceptor>(new asio::ip::tcp::acceptor(*io_service));
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address));
acceptor->bind(endpoint);
acceptor->listen();
accept();
//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear();
for (size_t c = 1; c < config.thread_pool_size; c++)
{
threads.emplace_back([this]()
{
io_service->run();
});
}
if(internal_io_service) {
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear();
for(size_t c = 1; c < config.thread_pool_size; c++) {
threads.emplace_back([this]() {
this->io_service->run();
});
}
//Main thread
if (config.thread_pool_size > 0)
io_service->run();
// Main thread
if(config.thread_pool_size > 0)
io_service->run();
//Wait for the rest of the threads, if any, to finish as well
for (auto &t: threads)
{
t.join();
// Wait for the rest of the threads, if any, to finish as well
for(auto &t : threads)
t.join();
}
}
void stop()
{
acceptor->close();
if (config.thread_pool_size > 0)
io_service->stop();
/// Stop accepting new requests, and close current connections.
void stop() noexcept {
if(acceptor) {
error_code ec;
acceptor->close(ec);
{
std::unique_lock<std::mutex> lock(*connections_mutex);
for(auto &connection : *connections)
connection->close();
connections->clear();
}
if(internal_io_service)
io_service->stop();
}
}
///Use this function if you need to recursively send parts of a longer message
void send(const std::shared_ptr<Response> &response,
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const
{
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]
(const boost::system::error_code &ec, size_t /*bytes_transferred*/)
{
if (callback)
callback(ec);
});
virtual ~ServerBase() noexcept {
handler_runner->stop();
stop();
}
/// If you have your own boost::asio::io_service, store its pointer here before running start().
/// You might also want to set config.thread_pool_size to 0.
std::shared_ptr<boost::asio::io_service> io_service;
protected:
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
bool internal_io_service = false;
std::unique_ptr<asio::ip::tcp::acceptor> acceptor;
std::vector<std::thread> threads;
ServerBase(unsigned short port) : config(port)
{}
virtual void accept()=0;
std::shared_ptr<boost::asio::deadline_timer>
get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)
{
if (seconds == 0)
return nullptr;
auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);
timer->expires_from_now(boost::posix_time::seconds(seconds));
timer->async_wait([socket](const boost::system::error_code &ec)
{
if (!ec)
{
boost::system::error_code ec;
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
socket->lowest_layer().close();
}
});
return timer;
}
std::shared_ptr<std::unordered_set<Connection *>> connections;
std::shared_ptr<std::mutex> connections_mutex;
void read_request_and_content(const std::shared_ptr<socket_type> &socket)
{
//Create new streambuf (Request::streambuf) for async_read_until()
//shared_ptr is used to pass temporary objects to the asynchronous functions
std::shared_ptr<Request> request(new Request(*socket));
std::shared_ptr<ScopeRunner> handler_runner;
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket, config.timeout_request);
ServerBase(unsigned short port) noexcept : config(port), connections(new std::unordered_set<Connection *>()), connections_mutex(new std::mutex()), handler_runner(new ScopeRunner()) {}
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", [this, socket, request, timer]
(const boost::system::error_code &ec,
size_t bytes_transferred)
{
if (timer)
timer->cancel();
if (!ec)
virtual void accept() = 0;
template <typename... Args>
std::shared_ptr<Connection> create_connection(Args &&... args) noexcept {
auto connections = this->connections;
auto connections_mutex = this->connections_mutex;
auto connection = std::shared_ptr<Connection>(new Connection(handler_runner, std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
{
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
//"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
//The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
//streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
size_t num_additional_bytes =
request->streambuf.size() - bytes_transferred;
if (!this->parse_request(request))
std::unique_lock<std::mutex> lock(*connections_mutex);
auto it = connections->find(connection);
if(it != connections->end())
connections->erase(it);
}
delete connection;
});
{
std::unique_lock<std::mutex> lock(*connections_mutex);
connections->emplace(connection.get());
}
return connection;
}
void read_request_and_content(const std::shared_ptr<Session> &session) {
session->connection->set_timeout(config.timeout_request);
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
// request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
// "After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
// The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
// streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred;
if(!RequestMessage::parse(session->request->content, session->request->method, session->request->path,
session->request->query_string, session->request->http_version, session->request->header)) {
if(this->on_error)
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
return;
}
//If content, read that as well
auto it = request->header.find("Content-Length");
if (it != request->header.end())
{
unsigned long long content_length;
try
{
// If content, read that as well
auto it = session->request->header.find("Content-Length");
if(it != session->request->header.end()) {
unsigned long long content_length = 0;
try {
content_length = stoull(it->second);
}
catch (const std::exception &e)
{
if (on_error)
on_error(request, boost::system::error_code(
boost::system::errc::protocol_error,
boost::system::generic_category()));
catch(const std::exception &e) {
if(this->on_error)
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
return;
}
if (content_length > num_additional_bytes)
{
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket,
config.timeout_content);
boost::asio::async_read(*socket, request->streambuf,
boost::asio::transfer_exactly(
content_length -
num_additional_bytes),
[this, socket, request, timer]
(const boost::system::error_code &ec,
size_t /*bytes_transferred*/)
{
if (timer)
timer->cancel();
if (!ec)
this->find_resource(socket,
request);
else if (on_error)
on_error(request, ec);
});
if(content_length > num_additional_bytes) {
session->connection->set_timeout(config.timeout_content);
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec)
this->find_resource(session);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
else
this->find_resource(socket, request);
this->find_resource(session);
}
else
this->find_resource(socket, request);
this->find_resource(session);
}
else if (on_error)
on_error(request, ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
bool parse_request(const std::shared_ptr<Request> &request) const
{
std::string line;
getline(request->content, line);
size_t method_end;
if ((method_end = line.find(' ')) != std::string::npos)
{
size_t path_end;
if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)
{
request->method = line.substr(0, method_end);
request->path = line.substr(method_end + 1, path_end - method_end - 1);
size_t protocol_end;
if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)
void find_resource(const std::shared_ptr<Session> &session) {
// Upgrade connection
if(on_upgrade) {
auto it = session->request->header.find("Upgrade");
if(it != session->request->header.end()) {
// remove connection from connections
{
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0)
return false;
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
std::unique_lock<std::mutex> lock(*connections_mutex);
auto it = connections->find(session->connection.get());
if(it != connections->end())
connections->erase(it);
}
else
return false;
getline(request->content, line);
size_t param_end;
while ((param_end = line.find(':')) != std::string::npos)
{
size_t value_start = param_end + 1;
if ((value_start) < line.size())
{
if (line[value_start] == ' ')
value_start++;
if (value_start < line.size())
request->header.emplace(line.substr(0, param_end),
line.substr(value_start, line.size() - value_start - 1));
}
getline(request->content, line);
}
}
else
return false;
}
else
return false;
return true;
}
void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)
{
//Upgrade connection
if (on_upgrade)
{
auto it = request->header.find("Upgrade");
if (it != request->header.end())
{
on_upgrade(socket, request);
on_upgrade(session->connection->socket, session->request);
return;
}
}
//Find path- and method-match, and call write_response
for (auto &regex_method: resource)
{
auto it = regex_method.second.find(request->method);
if (it != regex_method.second.end())
{
// Find path- and method-match, and call write_response
for(auto &regex_method : resource) {
auto it = regex_method.second.find(session->request->method);
if(it != regex_method.second.end()) {
std::smatch sm_res;
if (std::regex_match(request->path, sm_res, regex_method.first))
{
request->path_match = std::move(sm_res);
write_response(socket, request, it->second);
if(std::regex_match(session->request->path, sm_res, regex_method.first)) {
session->request->path_match = std::move(sm_res);
write_response(session, it->second);
return;
}
}
}
auto it = default_resource.find(request->method);
if (it != default_resource.end())
{
write_response(socket, request, it->second);
}
auto it = default_resource.find(session->request->method);
if(it != default_resource.end())
write_response(session, it->second);
}
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<
typename ServerBase<socket_type>::Request>)> &resource_function)
{
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket, config.timeout_content);
auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]
(Response *response_ptr)
{
void write_response(const std::shared_ptr<Session> &session,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
session->connection->set_timeout(config.timeout_content);
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
auto response = std::shared_ptr<Response>(response_ptr);
this->send(response, [this, response, request, timer](
const boost::system::error_code &ec)
{
if (timer)
timer->cancel();
if (!ec)
{
if (response->close_connection_after_response)
response->send([this, response](const error_code &ec) {
if(!ec) {
if(response->close_connection_after_response)
return;
auto range = request->header.equal_range(
"Connection");
for (auto it = range.first; it != range.second; it++)
{
if (boost::iequals(it->second, "close"))
{
auto range = response->session->request->header.equal_range("Connection");
for(auto it = range.first; it != range.second; it++) {
if(case_insensitive_equal(it->second, "close"))
return;
}
else if (boost::iequals(it->second, "keep-alive"))
{
this->read_request_and_content(
response->socket);
else if(case_insensitive_equal(it->second, "keep-alive")) {
auto new_session = std::make_shared<Session>(response->session->connection);
this->read_request_and_content(new_session);
return;
}
}
if (request->http_version >= "1.1")
this->read_request_and_content(response->socket);
if(response->session->request->http_version >= "1.1") {
auto new_session = std::make_shared<Session>(response->session->connection);
this->read_request_and_content(new_session);
return;
}
}
else if (on_error)
on_error(request, ec);
else if(this->on_error)
this->on_error(response->session->request, ec);
});
});
try
{
resource_function(response, request);
try {
resource_function(response, session->request);
}
catch (const std::exception &e)
{
if (on_error)
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
boost::system::generic_category()));
catch(const std::exception &e) {
if(on_error)
on_error(session->request, make_error_code::make_error_code(errc::operation_canceled));
return;
}
}
};
}
#endif //BASE_SERVER_HPP

@ -1,55 +1,42 @@
/*
* https://github.com/eidheim/Simple-Web-Server/
*
* The MIT License (MIT)
* Copyright (c) 2014-2016 Ole Christian Eidheim
*/
#ifndef SERVER_HTTP_HPP
#define SERVER_HTTP_HPP
#pragma once
#include "base_server.hpp"
namespace SimpleWeb
{
namespace SimpleWeb {
template<class socket_type>
template <class socket_type>
class Server : public ServerBase<socket_type> {};
typedef boost::asio::ip::tcp::socket HTTP;
using HTTP = asio::ip::tcp::socket;
template<>
class Server<HTTP> : public ServerBase<HTTP>
{
template <>
class Server<HTTP> : public ServerBase<HTTP> {
public:
Server() : ServerBase<HTTP>::ServerBase(80)
{}
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
protected:
virtual void accept()
{
//Create new socket for this connection
//Shared_ptr is used to pass temporary objects to the asynchronous functions
auto socket = std::make_shared<HTTP>(*io_service);
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
{
//Immediately start accepting a new connection (if io_service hasn't been stopped)
if (ec != boost::asio::error::operation_aborted)
accept();
if (!ec)
{
boost::asio::ip::tcp::no_delay option(true);
socket->set_option(option);
this->read_request_and_content(socket);
void accept() override {
auto session = std::make_shared<Session>(create_connection(*io_service));
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
// Immediately start accepting a new connection (unless io_service has been stopped)
if(ec != asio::error::operation_aborted)
this->accept();
if(!ec) {
asio::ip::tcp::no_delay option(true);
error_code ec;
session->connection->socket->set_option(option, ec);
this->read_request_and_content(session);
}
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
};
}
#endif //SERVER_HTTP_HPP
} // namespace SimpleWeb

@ -1,91 +1,82 @@
#ifndef HTTPS_SERVER_HPP
#define HTTPS_SERVER_HPP
#pragma once
#include "base_server.hpp"
#ifdef USE_STANDALONE_ASIO
#include <asio/ssl.hpp>
#else
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#endif
#include <algorithm>
#include <openssl/ssl.h>
namespace SimpleWeb
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
namespace SimpleWeb {
using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>;
template<>
class Server<HTTPS> : public ServerBase<HTTPS>
{
template <>
class Server<HTTPS> : public ServerBase<HTTPS> {
std::string session_id_context;
bool set_session_id_context = false;
public:
Server(const std::string &cert_file, const std::string &private_key_file,
const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),
context(boost::asio::ssl::context::tlsv12)
{
Server(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string())
: ServerBase<HTTPS>::ServerBase(443), context(asio::ssl::context::tlsv12) {
context.use_certificate_chain_file(cert_file);
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
if (verify_file.size() > 0)
{
if(verify_file.size() > 0) {
context.load_verify_file(verify_file);
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
boost::asio::ssl::verify_client_once);
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | asio::ssl::verify_client_once);
set_session_id_context = true;
}
}
void start()
{
if (set_session_id_context)
{
void start() override {
if(set_session_id_context) {
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
session_id_context = std::to_string(config.port) + ':';
session_id_context.append(config.address.rbegin(), config.address.rend());
SSL_CTX_set_session_id_context(context.native_handle(),
reinterpret_cast<const unsigned char *>(session_id_context.data()),
std::min<size_t>(session_id_context.size(),
SSL_MAX_SSL_SESSION_ID_LENGTH));
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
std::min<size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
}
ServerBase::start();
}
protected:
boost::asio::ssl::context context;
asio::ssl::context context;
virtual void accept()
{
//Create new socket for this connection
//Shared_ptr is used to pass temporary objects to the asynchronous functions
auto socket = std::make_shared<HTTPS>(*io_service, context);
void accept() override {
auto session = std::make_shared<Session>(create_connection(*io_service, context));
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
{
//Immediately start accepting a new connection (if io_service hasn't been stopped)
if (ec != boost::asio::error::operation_aborted)
accept();
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(ec != asio::error::operation_aborted)
this->accept();
if (!ec)
{
boost::asio::ip::tcp::no_delay option(true);
socket->lowest_layer().set_option(option);
if(!ec) {
asio::ip::tcp::no_delay option(true);
error_code ec;
session->connection->socket->lowest_layer().set_option(option, ec);
//Set timeout on the following boost::asio::ssl::stream::async_handshake
auto timer = get_timeout_timer(socket, config.timeout_request);
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
(const boost::system::error_code &ec)
{
if (timer)
timer->cancel();
if (!ec)
read_request_and_content(socket);
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
session->connection->set_timeout(config.timeout_request);
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec)
this->read_request_and_content(session);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
};
}
#endif //HTTPS_SERVER_HPP
} // namespace SimpleWeb

@ -0,0 +1,154 @@
#pragma once
#include <string>
#include <vector>
namespace SimpleWeb {
enum class StatusCode {
unknown = 0,
information_continue = 100,
information_switching_protocols,
information_processing,
success_ok = 200,
success_created,
success_accepted,
success_non_authoritative_information,
success_no_content,
success_reset_content,
success_partial_content,
success_multi_status,
success_already_reported,
success_im_used = 226,
redirection_multiple_choices = 300,
redirection_moved_permanently,
redirection_found,
redirection_see_other,
redirection_not_modified,
redirection_use_proxy,
redirection_switch_proxy,
redirection_temporary_redirect,
redirection_permanent_redirect,
client_error_bad_request = 400,
client_error_unauthorized,
client_error_payment_required,
client_error_forbidden,
client_error_not_found,
client_error_method_not_allowed,
client_error_not_acceptable,
client_error_proxy_authentication_required,
client_error_request_timeout,
client_error_conflict,
client_error_gone,
client_error_length_required,
client_error_precondition_failed,
client_error_payload_too_large,
client_error_uri_too_long,
client_error_unsupported_media_type,
client_error_range_not_satisfiable,
client_error_expectation_failed,
client_error_im_a_teapot,
client_error_misdirection_required = 421,
client_error_unprocessable_entity,
client_error_locked,
client_error_failed_dependency,
client_error_upgrade_required = 426,
client_error_precondition_required = 428,
client_error_too_many_requests,
client_error_request_header_fields_too_large = 431,
client_error_unavailable_for_legal_reasons = 451,
server_error_internal_server_error = 500,
server_error_not_implemented,
server_error_bad_gateway,
server_error_service_unavailable,
server_error_gateway_timeout,
server_error_http_version_not_supported,
server_error_variant_also_negotiates,
server_error_insufficient_storage,
server_error_loop_detected,
server_error_not_extended = 510,
server_error_network_authentication_required
};
const static std::vector<std::pair<StatusCode, std::string>> &status_codes() noexcept {
const static std::vector<std::pair<StatusCode, std::string>> status_codes = {
{StatusCode::unknown, ""},
{StatusCode::information_continue, "100 Continue"},
{StatusCode::information_switching_protocols, "101 Switching Protocols"},
{StatusCode::information_processing, "102 Processing"},
{StatusCode::success_ok, "200 OK"},
{StatusCode::success_created, "201 Created"},
{StatusCode::success_accepted, "202 Accepted"},
{StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"},
{StatusCode::success_no_content, "204 No Content"},
{StatusCode::success_reset_content, "205 Reset Content"},
{StatusCode::success_partial_content, "206 Partial Content"},
{StatusCode::success_multi_status, "207 Multi-Status"},
{StatusCode::success_already_reported, "208 Already Reported"},
{StatusCode::success_im_used, "226 IM Used"},
{StatusCode::redirection_multiple_choices, "300 Multiple Choices"},
{StatusCode::redirection_moved_permanently, "301 Moved Permanently"},
{StatusCode::redirection_found, "302 Found"},
{StatusCode::redirection_see_other, "303 See Other"},
{StatusCode::redirection_not_modified, "304 Not Modified"},
{StatusCode::redirection_use_proxy, "305 Use Proxy"},
{StatusCode::redirection_switch_proxy, "306 Switch Proxy"},
{StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"},
{StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"},
{StatusCode::client_error_bad_request, "400 Bad Request"},
{StatusCode::client_error_unauthorized, "401 Unauthorized"},
{StatusCode::client_error_payment_required, "402 Payment Required"},
{StatusCode::client_error_forbidden, "403 Forbidden"},
{StatusCode::client_error_not_found, "404 Not Found"},
{StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"},
{StatusCode::client_error_not_acceptable, "406 Not Acceptable"},
{StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"},
{StatusCode::client_error_request_timeout, "408 Request Timeout"},
{StatusCode::client_error_conflict, "409 Conflict"},
{StatusCode::client_error_gone, "410 Gone"},
{StatusCode::client_error_length_required, "411 Length Required"},
{StatusCode::client_error_precondition_failed, "412 Precondition Failed"},
{StatusCode::client_error_payload_too_large, "413 Payload Too Large"},
{StatusCode::client_error_uri_too_long, "414 URI Too Long"},
{StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"},
{StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"},
{StatusCode::client_error_expectation_failed, "417 Expectation Failed"},
{StatusCode::client_error_im_a_teapot, "418 I'm a teapot"},
{StatusCode::client_error_misdirection_required, "421 Misdirected Request"},
{StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"},
{StatusCode::client_error_locked, "423 Locked"},
{StatusCode::client_error_failed_dependency, "424 Failed Dependency"},
{StatusCode::client_error_upgrade_required, "426 Upgrade Required"},
{StatusCode::client_error_precondition_required, "428 Precondition Required"},
{StatusCode::client_error_too_many_requests, "429 Too Many Requests"},
{StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"},
{StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"},
{StatusCode::server_error_internal_server_error, "500 Internal Server Error"},
{StatusCode::server_error_not_implemented, "501 Not Implemented"},
{StatusCode::server_error_bad_gateway, "502 Bad Gateway"},
{StatusCode::server_error_service_unavailable, "503 Service Unavailable"},
{StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"},
{StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"},
{StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"},
{StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"},
{StatusCode::server_error_loop_detected, "508 Loop Detected"},
{StatusCode::server_error_not_extended, "510 Not Extended"},
{StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}};
return status_codes;
}
inline StatusCode status_code(const std::string &status_code_str) noexcept {
for(auto &status_code : status_codes()) {
if(status_code.second == status_code_str)
return status_code.first;
}
return StatusCode::unknown;
}
inline const std::string &status_code(StatusCode status_code_enum) noexcept {
for(auto &status_code : status_codes()) {
if(status_code.first == status_code_enum)
return status_code.second;
}
return status_codes()[0].second;
}
} // namespace SimpleWeb

@ -0,0 +1,340 @@
#pragma once
#include "status_code.hpp"
#include <atomic>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
namespace SimpleWeb {
inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept {
return str1.size() == str2.size() &&
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
return tolower(a) == tolower(b);
});
}
class CaseInsensitiveEqual {
public:
bool operator()(const std::string &str1, const std::string &str2) const noexcept {
return case_insensitive_equal(str1, str2);
}
};
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
class CaseInsensitiveHash {
public:
size_t operator()(const std::string &str) const noexcept {
size_t h = 0;
std::hash<int> hash;
for(auto c : str)
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
return h;
}
};
using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
/// Percent encoding and decoding
class Percent {
public:
/// Returns percent-encoded string
static std::string encode(const std::string &value) noexcept {
static auto hex_chars = "0123456789ABCDEF";
std::string result;
result.reserve(value.size()); // Minimum size of result
for(auto &chr : value) {
if(chr == ' ')
result += '+';
else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']')
result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
else
result += chr;
}
return result;
}
/// Returns percent-decoded string
static std::string decode(const std::string &value) noexcept {
std::string result;
result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result
for(size_t i = 0; i < value.size(); ++i) {
auto &chr = value[i];
if(chr == '%' && i + 2 < value.size()) {
auto hex = value.substr(i + 1, 2);
auto decoded_chr = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
result += decoded_chr;
i += 2;
}
else if(chr == '+')
result += ' ';
else
result += chr;
}
return result;
}
};
/// Query string creation and parsing
class QueryString {
public:
/// Returns query string created from given field names and values
static std::string create(const CaseInsensitiveMultimap &fields) noexcept {
std::string result;
bool first = true;
for(auto &field : fields) {
result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second);
first = false;
}
return result;
}
/// Returns query keys with percent-decoded values.
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept {
CaseInsensitiveMultimap result;
if(query_string.empty())
return result;
size_t name_pos = 0;
auto name_end_pos = std::string::npos;
auto value_pos = std::string::npos;
for(size_t c = 0; c < query_string.size(); ++c) {
if(query_string[c] == '&') {
auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
if(!name.empty()) {
auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
result.emplace(std::move(name), Percent::decode(value));
}
name_pos = c + 1;
name_end_pos = std::string::npos;
value_pos = std::string::npos;
}
else if(query_string[c] == '=') {
name_end_pos = c;
value_pos = c + 1;
}
}
if(name_pos < query_string.size()) {
auto name = query_string.substr(name_pos, name_end_pos - name_pos);
if(!name.empty()) {
auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
result.emplace(std::move(name), Percent::decode(value));
}
}
return result;
}
};
class HttpHeader {
public:
/// Parse header fields
static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
CaseInsensitiveMultimap result;
std::string line;
getline(stream, line);
size_t param_end;
while((param_end = line.find(':')) != std::string::npos) {
size_t value_start = param_end + 1;
if(value_start < line.size()) {
if(line[value_start] == ' ')
value_start++;
if(value_start < line.size())
result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
}
getline(stream, line);
}
return result;
}
};
class RequestMessage {
public:
/// Parse request line and header fields
static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
header.clear();
std::string line;
getline(stream, line);
size_t method_end;
if((method_end = line.find(' ')) != std::string::npos) {
method = line.substr(0, method_end);
size_t query_start = std::string::npos;
size_t path_and_query_string_end = std::string::npos;
for(size_t i = method_end + 1; i < line.size(); ++i) {
if(line[i] == '?' && (i + 1) < line.size())
query_start = i + 1;
else if(line[i] == ' ') {
path_and_query_string_end = i;
break;
}
}
if(path_and_query_string_end != std::string::npos) {
if(query_start != std::string::npos) {
path = line.substr(method_end + 1, query_start - method_end - 2);
query_string = line.substr(query_start, path_and_query_string_end - query_start);
}
else
path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
size_t protocol_end;
if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) {
if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0)
return false;
version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
}
else
return false;
header = HttpHeader::parse(stream);
}
else
return false;
}
else
return false;
return true;
}
};
class ResponseMessage {
public:
/// Parse status line and header fields
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
header.clear();
std::string line;
getline(stream, line);
size_t version_end = line.find(' ');
if(version_end != std::string::npos) {
if(5 < line.size())
version = line.substr(5, version_end - 5);
else
return false;
if((version_end + 1) < line.size())
status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
else
return false;
header = HttpHeader::parse(stream);
}
else
return false;
return true;
}
};
class ContentDisposition {
public:
/// Can be used to parse the Content-Disposition header field value when
/// clients are posting requests with enctype="multipart/form-data"
static CaseInsensitiveMultimap parse(const std::string &line) {
CaseInsensitiveMultimap result;
size_t para_start_pos = 0;
size_t para_end_pos = std::string::npos;
size_t value_start_pos = std::string::npos;
for(size_t c = 0; c < line.size(); ++c) {
if(para_start_pos != std::string::npos) {
if(para_end_pos == std::string::npos) {
if(line[c] == ';') {
result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
para_start_pos = std::string::npos;
}
else if(line[c] == '=')
para_end_pos = c;
}
else {
if(value_start_pos == std::string::npos) {
if(line[c] == '"' && c + 1 < line.size())
value_start_pos = c + 1;
}
else if(line[c] == '"') {
result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
para_start_pos = std::string::npos;
para_end_pos = std::string::npos;
value_start_pos = std::string::npos;
}
}
}
else if(line[c] != ' ' && line[c] != ';')
para_start_pos = c;
}
if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
result.emplace(line.substr(para_start_pos), std::string());
return result;
}
};
} // namespace SimpleWeb
#ifdef __SSE2__
#include <emmintrin.h>
namespace SimpleWeb {
inline void spin_loop_pause() noexcept { _mm_pause(); }
} // namespace SimpleWeb
// TODO: need verification that the following checks are correct:
#elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
#include <intrin.h>
namespace SimpleWeb {
inline void spin_loop_pause() noexcept { _mm_pause(); }
} // namespace SimpleWeb
#else
namespace SimpleWeb {
inline void spin_loop_pause() noexcept {}
} // namespace SimpleWeb
#endif
namespace SimpleWeb {
/// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service
class ScopeRunner {
/// Scope count that is set to -1 if scopes are to be canceled
std::atomic<long> count;
public:
class SharedLock {
friend class ScopeRunner;
std::atomic<long> &count;
SharedLock(std::atomic<long> &count) noexcept : count(count) {}
SharedLock &operator=(const SharedLock &) = delete;
SharedLock(const SharedLock &) = delete;
public:
~SharedLock() noexcept {
count.fetch_sub(1);
}
};
ScopeRunner() noexcept : count(0) {}
/// Returns nullptr if scope should be exited, or a shared lock otherwise
std::unique_ptr<SharedLock> continue_lock() noexcept {
long expected = count;
while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
spin_loop_pause();
if(expected < 0)
return nullptr;
else
return std::unique_ptr<SharedLock>(new SharedLock(count));
}
/// Blocks until all shared locks are released, then prevents future shared locks
void stop() noexcept {
long expected = 0;
while(!count.compare_exchange_weak(expected, -1)) {
if(expected < 0)
return;
expected = 0;
spin_loop_pause();
}
}
};
} // namespace SimpleWeb

@ -1,36 +1,74 @@
#include <iostream>
#include <Kbhit.h>
#include <RakSleep.h>
#include <sol.hpp>
#include "MasterServer.hpp"
#include "RestServer.hpp"
#include "AdminRest.hpp"
using namespace RakNet;
using namespace std;
unique_ptr<RestServer> restServer;
unique_ptr<MasterServer> masterServer;
bool run = true;
shared_ptr<MasterServer> masterServer;
unique_ptr<AdminRest> restAdminServer;
int main()
int main(int argc, char* argv[])
{
masterServer.reset(new MasterServer(2000, 25560));
restServer.reset(new RestServer(8080, masterServer->GetServers()));
if (argc != 2)
return 1;
string luaScript(argv[1]);
masterServer = make_shared<MasterServer>(luaScript);
masterServer->luaStuff([](sol::state &state)
{
sol::table config = state["config"];
sol::object restPort = config["restPort"];
if (restPort.get_type() != sol::type::number)
throw runtime_error("config.restPort is not correct");
restServer = make_unique<RestServer>(restPort.as<unsigned short>(), masterServer->GetServers());
sol::object restAdminCert = config["restAdminCert"];
if (restAdminCert.get_type() != sol::type::string)
throw runtime_error("config.restAdminCert is not correct");
sol::object restAdminKey = config["restAdminKey"];
if (restAdminKey.get_type() != sol::type::string)
throw runtime_error("config.restAdminKey is not correct");
sol::object restAdminVerifyFile = config["restAdminVerifyFile"];
if (restAdminVerifyFile.get_type() != sol::type::string)
throw runtime_error("config.restAdminVerifyFile is not correct");
sol::object restAdminPort = config["restAdminPort"];
if (restAdminPort.get_type() != sol::type::number)
throw runtime_error("config.restAdminPort is not correct");
restAdminServer = make_unique<AdminRest>(restAdminCert.as<string>(), restAdminKey.as<string>(),
restAdminVerifyFile.as<string>(), restAdminPort.as<unsigned short>(), masterServer);
});
auto onExit = [](int /*sig*/){
restServer->stop();
restAdminServer->stop();
masterServer->luaStuff([](sol::state &state) {
sol::protected_function func = state["OnExit"];
if (func.valid())
func.call();
});
masterServer->Stop(false);
masterServer->Wait();
run = false;
};
signal(SIGINT, onExit);
signal(SIGTERM, onExit);
masterServer->Start();
thread server_thread([]() { restServer->start(); });
server_thread.join();
restServer->start();
restAdminServer->start();
masterServer->Wait();
return 0;

@ -0,0 +1,323 @@
//
// Created by koncord on 25.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Actors.hpp"
#include "Cell.hpp"
#include "CellController.hpp"
#include "Player.hpp"
#include "Players.hpp"
using namespace std;
void Actor::Init(LuaState &lua)
{
lua.getState()->new_usertype<Actor>("Actor",
"getPosition", &NetActor::getPosition,
"setPosition", &NetActor::setPosition,
"getRotation", &NetActor::getRotation,
"setRotation", &NetActor::setRotation,
"getHealth", &NetActor::getHealth,
"setHealth", &NetActor::setHealth,
"getMagicka", &NetActor::getMagicka,
"setMagicka", &NetActor::setMagicka,
"getFatigue", &NetActor::getFatigue,
"setFatigue", &NetActor::setFatigue,
"getCell", &NetActor::getCell,
"getInventory", &NetActor::getInventory,
"followPlayer", &Actor::followPlayer,
"followActor", &Actor::followActor,
"refId", sol::property(&Actor::getRefId, &Actor::setRefId),
"refNumIndex", sol::property(&Actor::getRefNumIndex, &Actor::setRefNumIndex),
"mpNum", sol::property(&Actor::getMpNum, &Actor::setMpNum)
);
}
Actor::Actor() : NetActor()
{
}
void Actor::followPlayer(int pid)
{
actor->aiAction = mwmp::BaseActor::AIAction::Follow;
actor->hasAiTarget = true;
actor->aiTarget.isPlayer = true;
auto player = Players::getPlayerByPID(pid).get();
actor->aiTarget.guid = player->guid;
aiChanged = true;
}
void Actor::followActor(unsigned int refNumIndex, unsigned int mpNum)
{
actor->aiAction = mwmp::BaseActor::AIAction::Follow;
actor->hasAiTarget = true;
actor->aiTarget.isPlayer = false;
actor->aiTarget.refNumIndex = refNumIndex;
actor->aiTarget.mpNum = mpNum;
aiChanged = true;
}
std::string Actor::getRefId() const
{
return actor->refId;
}
void Actor::setRefId(const std::string &refId)
{
actor->refId = refId;
}
unsigned Actor::getRefNumIndex() const
{
return actor->refNumIndex;
}
void Actor::setRefNumIndex(unsigned refNumIndex)
{
actor->refNumIndex = refNumIndex;
}
unsigned Actor::getMpNum() const
{
return actor->mpNum;
}
void Actor::setMpNum(unsigned mpNum)
{
actor->mpNum = mpNum;
}
bool Actor::doesHavePosition() const
{
return actor->hasPositionData;
}
bool Actor::doesHaveStatsDynamic() const
{
return actor->hasStatsDynamicData;
}
void ActorController::Init(LuaState &lua)
{
sol::table actorsTable = lua.getState()->create_named_table("Actors");
actorsTable.set_function("createActor", [&lua](){
return lua.getActorCtrl().createActor();
});
actorsTable.set_function("sendActors", [&lua](shared_ptr<Player> player, vector<shared_ptr<Actor>> actors,
const std::string &cellDescription, bool sendToAll) {
lua.getActorCtrl().sendActors(player, actors, Utils::getCellFromDescription(cellDescription), sendToAll);
});
actorsTable.set_function("sendList", [&lua](shared_ptr<Player> player, vector<shared_ptr<Actor>> actors,
const std::string &cellDescription, bool sendToAll) {
lua.getActorCtrl().sendList(player, actors, Utils::getCellFromDescription(cellDescription), sendToAll);
});
actorsTable.set_function("requestList", [&lua](shared_ptr<Player> player, const std::string &cellDescription){
lua.getActorCtrl().requestList(player, Utils::getCellFromDescription(cellDescription));
});
actorsTable.set_function("getActors", [&lua](shared_ptr<Player> player, const std::string &cellDescription){
lua.getActorCtrl().getActors(player, Utils::getCellFromDescription(cellDescription));
});
}
ActorController::ActorController()
{
}
ActorController::~ActorController()
{
}
std::shared_ptr<Actor> ActorController::createActor()
{
Actor *actor = new Actor();
actor->actor.reset(new mwmp::BaseActor);
return shared_ptr<Actor>(actor);
}
void ActorController::sendActors(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors,
const ESM::Cell &cell, bool sendToAll)
{
actorList.cell = cell;
actorList.guid = player->guid;
bool positionChanged = false;
bool statsChanged = false;
/*bool attributesChanged = false;
bool skillsChanged = false;
bool baseInfoChanged = false;*/
bool equipmentChanged = false;
bool aiChanged = false;
bool changedCell = false;
actorList.baseActors.clear();
for (auto &actor : actors)
{
actorList.baseActors.push_back(actor->actor);
if (actor->positionChanged)
positionChanged = true;
if (actor->statsChanged)
statsChanged = true;
if (actor->aiChanged)
aiChanged = true;
/*if (actor->attributesChanged)
attributesChanged = true;
if (actor->skillsChanged)
skillsChanged = true;
if (actor->baseInfoChanged)
baseInfoChanged = true;*/
if (actor->inventory.isEquipmentChanged())
{
equipmentChanged = true;
actor->inventory.resetEquipmentFlag();
}
if (actor->cellAPI.isChangedCell())
{
changedCell = true;
actor->cellAPI.resetChangedCell();
}
actor->resetUpdateFlags();
}
auto actorCtrl = mwmp::Networking::get().getActorPacketController();
Cell *serverCell = nullptr;
if (sendToAll)
serverCell = CellController::get().getCell(actorList.cell);
if (positionChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
if (statsChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_STATS_DYNAMIC);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
/*if (attributesChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
}
if (skillsChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
}
if (baseInfoChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
}*/
if (equipmentChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_EQUIPMENT);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
if (aiChanged)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_AI);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
if (changedCell)
{
auto packet = actorCtrl->GetPacket(ID_ACTOR_CELL_CHANGE);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
}
void ActorController::sendList(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors,
const ESM::Cell &cell, bool sendToAll)
{
actorList.cell = player->cell;
actorList.guid = player->guid;
actorList.action = mwmp::BaseActorList::Action::Set;
for (auto &actor : actors)
{
actorList.baseActors.push_back(actor->actor);
}
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
CellController::get().getCell(actorList.cell)->sendToLoaded(packet, &actorList);
}
void ActorController::requestList(std::shared_ptr<Player> player, const ESM::Cell &cell)
{
actorList.cell = player->cell;
actorList.guid = player->guid;
actorList.action = mwmp::BaseActorList::Action::Request;
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
}
std::vector<std::shared_ptr<Actor>> ActorController::getActors(std::shared_ptr<Player> player, const ESM::Cell &cell)
{
Cell *serverCell = CellController::get().getCell(player->cell);
std::vector<std::shared_ptr<Actor>> actorList;
for (const auto &actor : serverCell->getActorList()->baseActors)
{
auto a = new Actor;
a->actor = actor;
actorList.emplace_back(a);
}
return actorList;
}

@ -0,0 +1,59 @@
//
// Created by koncord on 25.08.17.
//
#pragma once
#include <string>
#include <components/openmw-mp/Base/BaseActor.hpp>
#include "NetActor.hpp"
class LuaState;
class Player;
class Actor: public NetActor
{
friend class ActorController;
public:
static void Init(LuaState &lua);
public:
Actor();
std::string getRefId() const;
void setRefId(const std::string &refId);
unsigned getRefNumIndex() const;
void setRefNumIndex(unsigned refNumIndex);
unsigned getMpNum() const;
void setMpNum(unsigned mpNum);
void followPlayer(int pid);
void followActor(unsigned int refNumIndex, unsigned int mpNum);
bool doesHavePosition() const; // ????
bool doesHaveStatsDynamic() const; // ????
std::shared_ptr<mwmp::BaseActor> actor;
};
class ActorController
{
public:
static void Init(LuaState &lua);
public:
ActorController();
~ActorController();
std::shared_ptr<Actor> createActor();
void sendActors(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors, const ESM::Cell &cell, bool sendToAll = false);
void sendList(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors, const ESM::Cell &cell, bool sendToAll = false);
void requestList(std::shared_ptr<Player> player, const ESM::Cell &cell);
std::vector<std::shared_ptr<Actor>> getActors(std::shared_ptr<Player> player, const ESM::Cell &cell);
private:
mwmp::BaseActorList actorList;
};

@ -0,0 +1,34 @@
//
// Created by koncord on 02.01.18.
//
#include "BaseMgr.hpp"
#include "Player.hpp"
#include "Worldstate.hpp"
BaseMgr::BaseMgr(Player *player) : player(player), changed(false)
{
}
BaseMgr::BaseMgr(Worldstate *worldstate) : worldstate(worldstate), changed(false)
{
}
void BaseMgr::update()
{
if (!changed)
return;
changed = false;
processUpdate();
}
void BaseMgr::setChanged()
{
changed = true;
if (player != nullptr)
player->addToUpdateQueue();
}

@ -0,0 +1,24 @@
//
// Created by koncord on 02.01.18.
//
#pragma once
class Player;
class Worldstate;
class BaseMgr
{
public:
explicit BaseMgr(Player *player);
explicit BaseMgr(Worldstate *worldstate);
void update();
protected:
bool isChanged() const { return changed; };
void setChanged();
virtual void processUpdate() = 0;
Player *player;
Worldstate *worldstate;
private:
bool changed;
};

@ -0,0 +1,59 @@
//
// Created by koncord on 15.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Books.hpp"
#include "Player.hpp"
void Books::Init(LuaState &lua)
{
lua.getState()->new_usertype<Books>("Books",
"addBook", &Books::addBook,
"getBookId", &Books::getBookId,
"getChanges", &Books::getChanges,
"reset", &Books::reset
);
}
Books::Books(Player *player) : BaseMgr(player)
{
}
void Books::addBook(const std::string &bookId)
{
if (!isChanged())
reset();
player->bookChanges.books.push_back({bookId});
setChanged();
}
std::string Books::getBookId(unsigned i) const
{
if (i >= player->bookChanges.books.size())
return "invalid";
return player->bookChanges.books.at(i).bookId;
}
unsigned Books::getChanges() const
{
return player->bookChanges.books.size();
}
void Books::reset()
{
player->bookChanges.books.clear();
}
void Books::processUpdate()
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK);
packet->setPlayer(player);
packet->Send(/*toOthers*/ false);
}

@ -0,0 +1,26 @@
//
// Created by koncord on 15.08.17.
//
#pragma once
#include <string>
#include "BaseMgr.hpp"
class LuaState;
class Player;
class Books final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit Books(Player *player);
void addBook(const std::string &bookId);
std::string getBookId(unsigned i) const;
unsigned getChanges() const;
void reset();
private:
void processUpdate() final;
};

@ -1,13 +1,5 @@
project(tes3mp-server)
if(UNIX) #temporarily disabled for non-unix
if(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm")) #temporarily disabled for arm
find_package(CallFF REQUIRED)
include_directories(${CallFF_INCLUDES})
endif(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm"))
endif(UNIX)
option(BUILD_WITH_PAWN "Enable Pawn language" OFF)
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
if(ENABLE_BREAKPAD)
@ -22,91 +14,48 @@ if(ENABLE_BREAKPAD)
include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})
endif(ENABLE_BREAKPAD)
if(BUILD_WITH_PAWN)
add_subdirectory(amx)
#set(Pawn_ROOT ${CMAKE_SOURCE_DIR}/external/pawn/)
set(Pawn_INCLUDES ${Pawn_ROOT}/include)
set(Pawn_LIBRARY ${Pawn_ROOT}/lib/libamx.a)
set(PawnScript_Sources
Script/LangPawn/LangPAWN.cpp
Script/LangPawn/PawnFunc.cpp)
set(PawnScript_Headers ${Pawn_INCLUDES}
Script/LangPawn/LangPAWN.hpp
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_PAWN -DPAWN_CELL_SIZE=64")
#include_directories(${Pawn_INCLUDES})
include_directories("./amx/linux")
endif(BUILD_WITH_PAWN)
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
option(FORCE_LUA "Use Lua instead Terra" OFF)
if(BUILD_WITH_LUA)
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
if(WIN32 OR FORCE_LUA)
find_package(Lua51 REQUIRED)
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
else()
find_package(Terra REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")
endif()
set(LuaScript_Sources
Script/LangLua/LangLua.cpp
Script/LangLua/LuaFunc.cpp)
set(LuaScript_Headers ${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge ${CMAKE_SOURCE_DIR}/extern/LuaBridge/detail
Script/LangLua/LangLua.hpp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
endif(BUILD_WITH_LUA)
set(NativeScript_Sources
Script/LangNative/LangNative.cpp
)
set(NativeScript_Headers
Script/LangNative/LangNative.hpp
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
# local files
set(SERVER
main.cpp
Player.cpp
Player.cpp Players.cpp
Networking.cpp
MasterClient.cpp
Cell.cpp
CellController.cpp
Utils.cpp
Script/Script.cpp Script/ScriptFunction.cpp
Script/ScriptFunctions.cpp
Script/Functions/Actors.cpp Script/Functions/Objects.cpp Script/Functions/Miscellaneous.cpp
Script/Functions/Worldstate.cpp
Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp
Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp
Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/Settings.cpp
Script/Functions/Shapeshift.cpp Script/Functions/Spells.cpp Script/Functions/Stats.cpp
Script/Functions/Timer.cpp
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
${PawnScript_Sources}
${LuaScript_Sources}
${NativeScript_Sources}
CharClass.cpp
Inventory.cpp
Settings.cpp
Timer.cpp
Books.cpp
GUI.cpp
Dialogue.cpp
Factions.cpp
Cells.cpp
Quests.cpp
Spells.cpp
Actors.cpp
NetActor.cpp
CellState.cpp
Object.cpp
stacktrace.cpp
Window.cpp
Weather.cpp
Worldstate.cpp
BaseMgr.cpp
Script/CommandController.cpp Script/EventController.cpp Script/LuaState.cpp Script/luaUtils.cpp
)
if(WIN32)
list(APPEND SERVER stackwalker/StackWalker.cpp)
endif()
set(SERVER_HEADER
Script/Types.hpp Script/Script.hpp Script/SystemInterface.hpp
Script/ScriptFunction.hpp Script/Platform.hpp Script/Language.hpp
Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp
${PawnScript_Headers}
${LuaScript_Headers}
${NativeScript_Headers}
${CallFF_INCLUDES}
)
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
@ -123,6 +72,9 @@ source_group(tes3mp-server\\processors\\actor FILES ${PROCESSORS_ACTOR})
set(PROCESSORS_PLAYER
processors/player/ProcessorChatMsg.hpp processors/player/ProcessorGUIMessageBox.hpp
processors/player/ProcessorGUIWindow.hpp
processors/player/ProcessorGameWeather.hpp processors/player/ProcessorPlayerAnimFlags.hpp
processors/player/ProcessorPlayerAnimPlay.hpp processors/player/ProcessorPlayerAttack.hpp
processors/player/ProcessorPlayerAttribute.hpp processors/player/ProcessorPlayerBook.hpp
@ -183,7 +135,16 @@ add_executable(tes3mp-server
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_OBJECT} ${PROCESSORS_WORLDSTATE} ${PROCESSORS}
${APPLE_BUNDLE_RESOURCES}
)
add_definitions(-std=gnu++14 -Wno-ignored-qualifiers)
# For Lua debugging
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_FUNCTIONS> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_FUNCTIONS>)
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_USERTYPE> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_USERTYPE>)
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SERVER_DEBUG> $<$<CONFIG:RelWithDebInfo>:SERVER_DEBUG>)
set_property(TARGET tes3mp-server PROPERTY CXX_STANDARD 14)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(tes3mp-server PRIVATE -Wno-ignored-qualifiers -ftemplate-depth=2048)
endif()
target_link_libraries(tes3mp-server
#${Boost_SYSTEM_LIBRARY}
@ -192,11 +153,8 @@ target_link_libraries(tes3mp-server
#${Boost_PROGRAM_OPTIONS_LIBRARY}
${RakNet_LIBRARY}
components
${Terra_LIBRARY}
${LUA_LIBRARIES}
${Pawn_LIBRARY}
${LuaJit_LIBRARIES}
${Breakpad_Library}
${CallFF_LIBRARY}
)
if (UNIX)

@ -2,19 +2,20 @@
// Created by koncord on 18.02.17.
//
#include "Cell.hpp"
#include <iostream>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <iostream>
#include "Script/EventController.hpp"
#include "Networking.hpp"
#include "Cell.hpp"
#include "Player.hpp"
#include "Script/Script.hpp"
using namespace std;
Cell::Cell(ESM::Cell cell) : cell(cell)
Cell::Cell(const ESM::Cell &cell) : cell(cell)
{
cellActorList.count = 0;
}
Cell::Iterator Cell::begin() const
@ -48,14 +49,15 @@ void Cell::addPlayer(Player *player)
LOG_APPEND(Log::LOG_INFO, "- Adding %s to Cell %s", player->npc.mName.c_str(), getDescription().c_str());
Script::Call<Script::CallbackIdentity("OnCellLoad")>(player->getId(), getDescription().c_str());
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_LOAD>(player, getDescription());
players.push_back(player);
}
void Cell::removePlayer(Player *player)
{
for (Iterator it = begin(); it != end(); it++)
for (auto it = begin(); it != end(); it++)
{
if (*it == player)
{
@ -69,7 +71,8 @@ void Cell::removePlayer(Player *player)
LOG_APPEND(Log::LOG_INFO, "- Removing %s from Cell %s", player->npc.mName.c_str(), getDescription().c_str());
Script::Call<Script::CallbackIdentity("OnCellUnload")>(player->getId(), getDescription().c_str());
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_UNLOAD>(player, getDescription());
players.erase(it);
return;
@ -79,77 +82,67 @@ void Cell::removePlayer(Player *player)
void Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList)
{
for (unsigned int i = 0; i < newActorList->count; i++)
for (auto &newActor : newActorList->baseActors)
{
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
mwmp::BaseActor *cellActor;
if (containsActor(newActor.refNumIndex, newActor.mpNum))
if (containsActor(newActor->refNumIndex, newActor->mpNum))
{
cellActor = getActor(newActor.refNumIndex, newActor.mpNum);
mwmp::BaseActor *cellActor = getActor(newActor->refNumIndex, newActor->mpNum);
switch (packetID)
{
case ID_ACTOR_POSITION:
cellActor->hasPositionData = true;
cellActor->position = newActor.position;
cellActor->position = newActor->position;
break;
case ID_ACTOR_STATS_DYNAMIC:
cellActor->hasStatsDynamicData = true;
cellActor->creatureStats.mDynamic[0] = newActor.creatureStats.mDynamic[0];
cellActor->creatureStats.mDynamic[1] = newActor.creatureStats.mDynamic[1];
cellActor->creatureStats.mDynamic[2] = newActor.creatureStats.mDynamic[2];
cellActor->creatureStats.mDynamic[0] = newActor->creatureStats.mDynamic[0];
cellActor->creatureStats.mDynamic[1] = newActor->creatureStats.mDynamic[1];
cellActor->creatureStats.mDynamic[2] = newActor->creatureStats.mDynamic[2];
break;
}
}
else
cellActorList.baseActors.push_back(newActor);
}
cellActorList.count = cellActorList.baseActors.size();
}
bool Cell::containsActor(int refNumIndex, int mpNum)
bool Cell::containsActor(unsigned refNumIndex, unsigned mpNum)
{
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
for (const auto &actor : cellActorList.baseActors)
{
mwmp::BaseActor actor = cellActorList.baseActors.at(i);
if (actor.refNumIndex == refNumIndex && actor.mpNum == mpNum)
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
return true;
}
return false;
}
mwmp::BaseActor *Cell::getActor(int refNumIndex, int mpNum)
mwmp::BaseActor *Cell::getActor(unsigned refNumIndex, unsigned mpNum)
{
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
for (const auto &actor : cellActorList.baseActors)
{
mwmp::BaseActor *actor = &cellActorList.baseActors.at(i);
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
return actor;
return actor.get();
}
return 0;
return nullptr;
}
void Cell::removeActors(const mwmp::BaseActorList *newActorList)
void Cell::removeActors(const mwmp::BaseActorList &newActorList)
{
for (std::vector<mwmp::BaseActor>::iterator it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
for (auto it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
{
int refNumIndex = (*it).refNumIndex;
int mpNum = (*it).mpNum;
unsigned refNumIndex = (*it)->refNumIndex;
unsigned mpNum = (*it)->mpNum;
bool foundActor = false;
for (unsigned int i = 0; i < newActorList->count; i++)
for (const auto &newActor : newActorList.baseActors)
{
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
if (newActor.refNumIndex == refNumIndex && newActor.mpNum == mpNum)
if (newActor->refNumIndex == refNumIndex && newActor->mpNum == mpNum)
{
it = cellActorList.baseActors.erase(it);
foundActor = true;
@ -160,8 +153,6 @@ void Cell::removeActors(const mwmp::BaseActorList *newActorList)
if (!foundActor)
it++;
}
cellActorList.count = cellActorList.baseActors.size();
}
RakNet::RakNetGUID *Cell::getAuthority()
@ -191,7 +182,7 @@ void Cell::sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *bas
std::list <Player*> plList;
for (auto pl : players)
for (auto &pl : players)
{
if (pl != nullptr && !pl->npc.mName.empty())
plList.push_back(pl);
@ -200,7 +191,7 @@ void Cell::sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *bas
plList.sort();
plList.unique();
for (auto pl : plList)
for (auto &pl : plList)
{
if (pl->guid == baseActorList->guid) continue;
@ -218,7 +209,7 @@ void Cell::sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *
std::list <Player*> plList;
for (auto pl : players)
for (auto &pl : players)
{
if (pl != nullptr && !pl->npc.mName.empty())
plList.push_back(pl);
@ -227,7 +218,7 @@ void Cell::sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *
plList.sort();
plList.unique();
for (auto pl : plList)
for (auto &pl : plList)
{
if (pl->guid == baseObjectList->guid) continue;

@ -20,7 +20,7 @@ class Cell
{
friend class CellController;
public:
Cell(ESM::Cell cell);
Cell(const ESM::Cell &cell);
typedef std::deque<Player*> TPlayers;
typedef TPlayers::const_iterator Iterator;
@ -31,9 +31,9 @@ public:
void removePlayer(Player *player);
void readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList);
bool containsActor(int refNumIndex, int mpNum);
mwmp::BaseActor *getActor(int refNumIndex, int mpNum);
void removeActors(const mwmp::BaseActorList *newActorList);
bool containsActor(unsigned refNumIndex, unsigned mpNum);
mwmp::BaseActor *getActor(unsigned refNumIndex, unsigned mpNum);
void removeActors(const mwmp::BaseActorList &newActorList);
RakNet::RakNetGUID *getAuthority();
void setAuthority(const RakNet::RakNetGUID& guid);

@ -1,50 +1,32 @@
#include "CellController.hpp"
#include <iostream>
#include <apps/openmw-mp/Script/EventController.hpp>
#include "Networking.hpp"
#include "CellController.hpp"
#include "Cell.hpp"
#include "Player.hpp"
#include "Script/Script.hpp"
using namespace std;
CellController::CellController()
{
}
CellController::~CellController()
{
for (auto cell : cells)
delete cell;
}
CellController *CellController::sThis = nullptr;
void CellController::create()
{
assert(!sThis);
sThis = new CellController;
}
void CellController::destroy()
CellController &CellController::get()
{
assert(sThis);
delete sThis;
sThis = nullptr;
static CellController cellCtrl;
return cellCtrl;
}
CellController *CellController::get()
Cell *CellController::getCell(const ESM::Cell &esmCell)
{
assert(sThis);
return sThis;
}
Cell *CellController::getCell(ESM::Cell *esmCell)
{
if (esmCell->isExterior())
return getCellByXY(esmCell->mData.mX, esmCell->mData.mY);
if (esmCell.isExterior())
return getCellByXY(esmCell.mData.mX, esmCell.mData.mY);
else
return getCellByName(esmCell->mName);
return getCellByName(esmCell.mName);
}
@ -64,7 +46,7 @@ Cell *CellController::getCellByXY(int x, int y)
return *it;
}
Cell *CellController::getCellByName(std::string cellName)
Cell *CellController::getCellByName(const std::string &cellName)
{
auto it = find_if(cells.begin(), cells.end(), [cellName](const Cell *c)
{
@ -80,7 +62,7 @@ Cell *CellController::getCellByName(std::string cellName)
return *it;
}
Cell *CellController::addCell(ESM::Cell cellData)
Cell *CellController::addCell(const ESM::Cell &cellData)
{
LOG_APPEND(Log::LOG_INFO, "- Loaded cells: %d", cells.size());
auto it = find_if(cells.begin(), cells.end(), [cellData](const Cell *c) {
@ -123,7 +105,8 @@ void CellController::removeCell(Cell *cell)
{
if (*it != nullptr && *it == cell)
{
Script::Call<Script::CallbackIdentity("OnCellDeletion")>(cell->getDescription().c_str());
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_DELETION>(cell->getDescription());
LOG_APPEND(Log::LOG_INFO, "- Removing %s from CellController", cell->getDescription().c_str());
delete *it;
@ -149,7 +132,7 @@ void CellController::deletePlayer(Player *player)
{
LOG_APPEND(Log::LOG_INFO, "- Iterating through Cells from Player %s", player->npc.mName.c_str());
for (auto it = player->getCells()->begin(); player->getCells()->size() != 0; ++it)
for (auto it = player->getCells()->begin(); !player->getCells()->empty(); ++it)
removePlayer(*it, player);
}
@ -157,7 +140,7 @@ void CellController::update(Player *player)
{
for (auto cell : player->cellStateChanges.cellStates)
{
if (cell.type == mwmp::CellState::LOAD)
if (cell.type == mwmp::CellState::Type::Load)
{
Cell *c = addCell(cell.cell);
c->addPlayer(player);

@ -15,32 +15,32 @@ class Cell;
class CellController
{
private:
CellController();
CellController() = default;
~CellController();
CellController(CellController&); // not used
public:
static void create();
static void destroy();
static CellController *get();
CellController(const CellController&) = delete;
CellController(CellController&&) = delete;
CellController& operator=(const CellController&) = delete;
CellController& operator=(CellController&&) = delete;
static CellController &get();
public:
typedef std::deque<Cell*> TContainer;
typedef TContainer::iterator TIter;
Cell * addCell(ESM::Cell cell);
Cell *addCell(const ESM::Cell &cellData);
void removeCell(Cell *);
void removePlayer(Cell *cell, Player *player);
void deletePlayer(Player *player);
Cell *getCell(ESM::Cell *esmCell);
Cell *getCell(const ESM::Cell &esmCell);
Cell *getCellByXY(int x, int y);
Cell *getCellByName(std::string cellName);
Cell *getCellByName(const std::string &cellName);
void update(Player *player);
private:
static CellController *sThis;
TContainer cells;
};

@ -0,0 +1,32 @@
//
// Created by koncord on 25.08.17.
//
#include "Script/LuaState.hpp"
#include "CellState.hpp"
#include <utility>
void CellState::Init(LuaState &lua)
{
lua.getState()->new_usertype<CellState>("CellState",
"type", sol::property(&CellState::getStateType),
"description", sol::property(&CellState::getDescription)
);
}
CellState::CellState(mwmp::CellState state) : state(std::move(state))
{
}
mwmp::CellState::Type CellState::getStateType() const
{
return state.type;
}
std::string CellState::getDescription() const
{
return state.cell.getDescription();
}

@ -0,0 +1,24 @@
//
// Created by koncord on 25.08.17.
//
#pragma once
#include <string>
#include <components/openmw-mp/Base/BasePlayer.hpp>
class LuaState;
class CellState
{
public:
static void Init(LuaState &lua);
explicit CellState(mwmp::CellState state);
public:
mwmp::CellState::Type getStateType() const;
std::string getDescription() const;
private:
mwmp::CellState state;
};

@ -0,0 +1,108 @@
//
// Created by koncord on 25.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Player.hpp"
#include "Networking.hpp"
#include "Cells.hpp"
#include "NetActor.hpp"
using namespace std;
void Cells::Init(LuaState &lua)
{
lua.getState()->new_usertype<Cells>("Cell",
"description", sol::property(&Cells::getDescription, &Cells::setDescription),
"getExterior", &Cells::getExterior,
"setExterior", &Cells::setExterior,
"getRegion", &Cells::getRegion,
"isExterior", &Cells::isExterior,
"isChangingRegion", &Cells::isChangingRegion
);
}
Cells::Cells(NetActor *netActor) : netActor(netActor), changedCell(false)
{
}
Cells::~Cells()
{
}
void Cells::update()
{
}
std::string Cells::getDescription() const
{
return netActor->getNetCreature()->cell.getDescription();
}
inline void Cells::setChanged()
{
if (!changedCell && netActor->isPlayer())
netActor->toPlayer()->addToUpdateQueue();
changedCell = true;
}
void Cells::setDescription(const std::string &cellDescription)
{
/*LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %s", netActor->getNetCreature()->npc.mName.c_str(),
netActor->getNetCreature()->cell.getDescription().c_str(), cellDescription.c_str());*/
netActor->getNetCreature()->cell = Utils::getCellFromDescription(cellDescription);
setChanged();
}
std::tuple<int, int> Cells::getExterior() const
{
return make_tuple(netActor->getNetCreature()->cell.mData.mX, netActor->getNetCreature()->cell.mData.mY);
}
void Cells::setExterior(int x, int y)
{
/*LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %i,%i", netActor->getNetCreature()->npc.mName.c_str(),
netActor->getNetCreature()->cell.getDescription().c_str(), x, y);*/
// If the player is currently in an interior, turn off the interior flag from the cell
if (!netActor->getNetCreature()->cell.isExterior())
netActor->getNetCreature()->cell.mData.mFlags &= ~ESM::Cell::Interior;
netActor->getNetCreature()->cell.mData.mX = x;
netActor->getNetCreature()->cell.mData.mY = y;
setChanged();
}
bool Cells::isExterior() const
{
return netActor->getNetCreature()->cell.isExterior();
}
bool Cells::isChangingRegion() const
{
return netActor->getNetCreature()->isChangingRegion;
}
std::string Cells::getRegion() const
{
return netActor->getNetCreature()->cell.mRegion;
}
bool Cells::isChangedCell() const
{
return changedCell;
}
void Cells::resetChangedCell()
{
changedCell = false;
}

@ -0,0 +1,45 @@
//
// Created by koncord on 25.08.17.
//
#pragma once
#include <cstddef>
#include <string>
#include <components/openmw-mp/Base/BasePlayer.hpp>
class LuaState;
class NetActor;
class Cells
{
public:
static void Init(LuaState &lua);
public:
explicit Cells(NetActor *netActor);
~Cells();
void update();
std::string getDescription() const;
void setDescription(const std::string &cellDescription);
std::tuple<int, int> getExterior() const;
void setExterior(int x, int y);
bool isExterior() const;
bool isChangingRegion() const;
std::string getRegion() const;
bool isChangedCell() const;
void resetChangedCell();
private:
NetActor *netActor;
bool changedCell;
void setChanged();
};

@ -0,0 +1,147 @@
//
// Created by koncord on 12.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "CharClass.hpp"
#include "Player.hpp"
using namespace std;
void CharClass::Init(LuaState &lua)
{
lua.getState()->new_usertype<CharClass>("Class",
//"__gc", sol::destructor(deleter),
"default", sol::property(&CharClass::getDefault, &CharClass::setDefault),
"isCustom", &CharClass::isCustom,
"name", sol::property(&CharClass::getName, &CharClass::setName),
"description", sol::property(&CharClass::getDescription, &CharClass::setDescription),
"specialization",
sol::property(&CharClass::getSpecialization, &CharClass::setSpecialization),
"getMajorAttributes", &CharClass::getMajorAttributes,
"setMajorAttributes", &CharClass::setMajorAttributes,
"getMinorSkills", &CharClass::getMinorSkills,
"setMinorSkills", &CharClass::setMinorSkills,
"getMajorSkills", &CharClass::getMajorSkills,
"setMajorSkills", &CharClass::setMajorSkills
);
}
CharClass::CharClass(Player *player) : BaseMgr(player)
{
}
string CharClass::getDefault() const
{
return player->charClass.mId;
}
void CharClass::setDefault(const string &className)
{
player->charClass.mId = className;
setChanged();
printf("CharClass::setDefault()\n");
}
bool CharClass::isCustom() const
{
return player->charClass.mId.empty();
}
void CharClass::setName(const string &className)
{
player->charClass.mName = className;
setChanged();
}
string CharClass::getName() const
{
return player->charClass.mName;
}
std::string CharClass::getDescription() const
{
return player->charClass.mDescription;
}
void CharClass::setDescription(const string &desc)
{
player->charClass.mDescription = desc;
setChanged();
}
std::tuple<int, int> CharClass::getMajorAttributes() const
{
const auto &data = player->charClass.mData;
return make_tuple(data.mAttribute[0], data.mAttribute[1]);
}
void CharClass::setMajorAttributes(int first, int second)
{
auto &data = player->charClass.mData;
data.mAttribute[0] = first;
data.mAttribute[1] = second;
setChanged();
}
int CharClass::getSpecialization() const
{
return player->charClass.mData.mSpecialization;
}
void CharClass::setSpecialization(int spec)
{
auto &data = player->charClass.mData;
data.mSpecialization = spec;
setChanged();
}
std::tuple<int, int, int, int, int> CharClass::getMinorSkills() const
{
const auto &data = player->charClass.mData;
return make_tuple( data.mSkills[0][0], data.mSkills[1][0], data.mSkills[2][0], data.mSkills[3][0], data.mSkills[4][0]);
}
void CharClass::setMinorSkills(int first, int second, int third, int fourth, int fifth)
{
auto &data = player->charClass.mData;
data.mSkills[0][0] = first;
data.mSkills[1][0] = second;
data.mSkills[2][0] = third;
data.mSkills[3][0] = fourth;
data.mSkills[4][0] = fifth;
setChanged();
}
std::tuple<int, int, int, int, int> CharClass::getMajorSkills() const
{
const auto &data = player->charClass.mData;
return make_tuple( data.mSkills[0][1], data.mSkills[1][1], data.mSkills[2][1], data.mSkills[3][1], data.mSkills[4][1]);
}
void CharClass::setMajorSkills(int first, int second, int third, int fourth, int fifth)
{
auto &data = player->charClass.mData;
data.mSkills[0][1] = first;
data.mSkills[1][1] = second;
data.mSkills[2][1] = third;
data.mSkills[3][1] = fourth;
data.mSkills[4][1] = fifth;
setChanged();
}
void CharClass::processUpdate()
{
printf("CharClass::update()\n");
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS);
packet->setPlayer(player);
packet->Send(false);
}

@ -0,0 +1,48 @@
//
// Created by koncord on 12.08.17.
//
#pragma once
#include <string>
#include <tuple>
#include "BaseMgr.hpp"
class LuaState;
class Player;
class CharClass final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit CharClass(Player *player);
std::string getDefault() const;
void setDefault(const std::string &className);
bool isCustom() const;
std::string getName() const;
void setName(const std::string &className);
std::string getDescription() const;
void setDescription(const std::string &desc);
std::tuple<int, int> getMajorAttributes() const;
void setMajorAttributes(int first, int second);
int getSpecialization() const;
void setSpecialization(int spec);
std::tuple<int, int, int, int, int> getMinorSkills() const;
void setMinorSkills(int fisrt, int second, int third, int fourth, int fifth);
std::tuple<int, int, int, int, int> getMajorSkills() const;
void setMajorSkills(int fisrt, int second, int third, int fourth, int fifth);
private:
void processUpdate() final;
};

@ -0,0 +1,82 @@
//
// Created by koncord on 15.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Dialogue.hpp"
#include "Player.hpp"
void Dialogue::Init(LuaState &lua)
{
lua.getState()->new_usertype<Dialogue>("Dialogue",
"addTopic", &Dialogue::addTopic,
"getTopicId", &Dialogue::getTopicId,
"size", &Dialogue::size,
"reset", &Dialogue::reset);
}
Dialogue::Dialogue(Player *player) : BaseMgr(player)
{
}
void Dialogue::reset()
{
player->topicChanges.topics.clear();
}
void Dialogue::processUpdate()
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC);
packet->setPlayer(player);
packet->Send(/*toOthers*/ false);
}
void Dialogue::addTopic(const std::string &topicId)
{
if (!isChanged())
reset();
setChanged();
player->topicChanges.topics.push_back({topicId});
}
std::string Dialogue::getTopicId(unsigned int i) const
{
return player->topicChanges.topics.at(i).topicId;
}
size_t Dialogue::size() const
{
return player->topicChanges.topics.size();
}
void Dialogue::playAnimation(const std::string &groupname, int mode, int count, bool persist)
{
player->animation.groupname = groupname;
player->animation.mode = mode;
player->animation.count = count;
player->animation.persist = persist;
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY);
packet->setPlayer(player);
packet->Send(false);
player->sendToLoaded(*packet);
}
void Dialogue::playSpeech(const std::string &sound)
{
player->sound = sound;
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH);
packet->setPlayer(player);
packet->Send(false);
player->sendToLoaded(*packet);
}

@ -0,0 +1,32 @@
//
// Created by koncord on 15.08.17.
//
#pragma once
#include <string>
#include "BaseMgr.hpp"
class LuaState;
class Player;
class Dialogue final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit Dialogue(Player *player);
void addTopic(const std::string &topicId);
std::string getTopicId(unsigned int i) const;
size_t size() const;
void playAnimation(const std::string &groupname, int mode, int count, bool persist);
void playSpeech(const std::string &sound);
void reset();
private:
void processUpdate() final;
};

@ -0,0 +1,127 @@
//
// Created by koncord on 17.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Factions.hpp"
#include "Player.hpp"
void Factions::Init(LuaState &lua)
{
lua.getState()->new_usertype<Factions>("Factions",
"addFaction", &Factions::addFaction,
"getFaction", &Factions::getFaction,
"setFaction", &Factions::setFaction,
"clear", &Factions::clear,
"size", &Factions::size
);
}
Factions::Factions(Player *player): BaseMgr(player)
{
}
Factions::~Factions()
{
}
void Factions::processUpdate()
{
auto packet =mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION);
packet->setPlayer(player);
packet->Send(/*toOthers*/ false);
clear();
}
void Factions::addFaction(const Faction &faction)
{
player->factionChanges.factions.emplace_back(faction.faction);
setChanged();
}
Faction Factions::getFaction(int id) const
{
return Faction(player->factionChanges.factions.at(id));
}
void Factions::setFaction(int id, const Faction &faction)
{
player->factionChanges.factions.at(id) = faction.faction;
setChanged();
}
void Factions::clear()
{
player->factionChanges.factions.clear();
setChanged();
}
size_t Factions::size() const
{
return player->factionChanges.factions.size();
}
void Faction::Init(LuaState &lua)
{
lua.getState()->new_usertype<Faction>("Faction",
"factionId", sol::property(&Faction::getFactionId, &Faction::setFactionId),
"rank", sol::property(&Faction::getFactionRank, &Faction::setFactionRank),
"isExpelled", sol::property(&Faction::getFactionExpulsionState, &Faction::setFactionExpulsionState),
"reputation", sol::property(&Faction::getFactionReputation, &Faction::setFactionReputation)
);
}
Faction::Faction(mwmp::Faction &faction): faction(faction)
{
}
std::string Faction::getFactionId() const
{
return faction.factionId;
}
void Faction::setFactionId(const std::string &factionId)
{
faction.factionId = factionId;
}
int Faction::getFactionRank() const
{
return faction.rank;
}
void Faction::setFactionRank(unsigned int rank)
{
faction.rankChanged();
faction.rank = rank;
}
bool Faction::getFactionExpulsionState() const
{
return faction.isExpelled;
}
void Faction::setFactionExpulsionState(bool expulsionState)
{
faction.expulsionChanged();
faction.isExpelled = expulsionState;
}
int Faction::getFactionReputation() const
{
return faction.reputation;
}
void Faction::setFactionReputation(int reputation)
{
faction.reputationChanged();
faction.reputation = reputation;
}

@ -0,0 +1,53 @@
//
// Created by koncord on 17.08.17.
//
#pragma once
#include <string>
#include <components/openmw-mp/Base/BasePlayer.hpp>
#include "BaseMgr.hpp"
class LuaState;
class Player;
class Faction
{
friend class Factions;
public:
static void Init(LuaState &lua);
public:
explicit Faction(mwmp::Faction &faction);
std::string getFactionId() const;
void setFactionId(const std::string &factionId);
int getFactionRank() const;
void setFactionRank(unsigned int rank);
bool getFactionExpulsionState() const;
void setFactionExpulsionState(bool expulsionState);
int getFactionReputation() const;
void setFactionReputation(int reputation);
mwmp::Faction faction;
};
class Factions final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit Factions(Player *player);
~Factions();
void addFaction(const Faction &faction);
Faction getFaction(int id) const;
void setFaction(int id, const Faction &faction);
size_t size() const;
void clear();
private:
void processUpdate() final;
};

@ -0,0 +1,279 @@
//
// Created by koncord on 15.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "GUI.hpp"
#include "Player.hpp"
#include "Networking.hpp"
#include <RakPeerInterface.h>
void GUI::Init(LuaState &lua)
{
lua.getState()->new_usertype<GUI>("GUI",
"messageBox", &GUI::messageBox,
"customMessageBox", &GUI::customMessageBox,
"inputDialog", &GUI::inputDialog,
"passwordDialog", &GUI::passwordDialog,
"listBox", &GUI::listBox,
"setMapVisibility", &GUI::setMapVisibility,
"setMapVisibilityAll", &GUI::setMapVisibilityAll,
"createWindow", &GUI::createWindow,
"deleteWindow", &GUI::deleteWindow
);
Window::Init(lua);
}
GUI::GUI(Player *player): BaseMgr(player)
{
}
void GUI::processUpdate()
{
player->guiMessageBox = guiQueue.front().first;
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);
packet->setPlayer(player);
packet->Send(false);
}
uint64_t GUI::generateGuiId()
{
return RakNet::RakPeerInterface::Get64BitUniqueRandomNumber();
}
void GUI::messageBox(sol::function fn, const char *label, sol::this_environment te)
{
mwmp::BasePlayer::GUIMessageBox mbox;
mbox.id = generateGuiId();
mbox.label = label;
mbox.type = Player::GUIMessageBox::Type::MessageBox;
guiQueue.emplace(std::move(mbox), std::move(fn));
setChanged();
}
void GUI::customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te)
{
mwmp::BasePlayer::GUIMessageBox mbox;
mbox.id = generateGuiId();
mbox.label = label;
mbox.buttons = buttons;
mbox.type = Player::GUIMessageBox::Type::CustomMessageBox;
guiQueue.emplace(std::move(mbox), std::move(fn));
setChanged();
}
void GUI::inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te)
{
mwmp::BasePlayer::GUIMessageBox mbox;
mbox.id = generateGuiId();
mbox.label = label;
mbox.note = note;
mbox.type = Player::GUIMessageBox::Type::InputDialog;
guiQueue.emplace(std::move(mbox), std::move(fn));
setChanged();
}
void GUI::passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te)
{
mwmp::BasePlayer::GUIMessageBox mbox;
mbox.id = generateGuiId();
mbox.label = label;
mbox.note = note;
mbox.type = Player::GUIMessageBox::Type::PasswordDialog;
guiQueue.emplace(std::move(mbox), std::move(fn));
setChanged();
}
void GUI::listBox(sol::function fn, const char *label, const char *items, sol::this_environment te)
{
mwmp::BasePlayer::GUIMessageBox mbox;
mbox.id = generateGuiId();
mbox.label = label;
mbox.data = items;
mbox.type = Player::GUIMessageBox::Type::ListBox;
guiQueue.emplace(std::move(mbox), std::move(fn));
setChanged();
}
void GUI::onGUIAction()
{
auto mbox = std::move(guiQueue.front().first);
auto callback = std::move(guiQueue.front().second);
guiQueue.pop();
if (!guiQueue.empty())
setChanged();
if (player->guiMessageBox.id != mbox.id)
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Wrong MessageBox id from %s(%d).", player->npc.mName, player->getId());
return;
}
if (callback.valid())
callback.call(player, player->guiMessageBox.data);
}
void GUI::setMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state)
{
LOG_MESSAGE(Log::LOG_WARN, "stub");
}
void GUI::setMapVisibilityAll(unsigned short targetPID, unsigned short state)
{
LOG_MESSAGE(Log::LOG_WARN, "stub");
}
std::shared_ptr<Window> GUI::createWindow(short x, short y, sol::function fn, sol::this_environment te)
{
int id = 0;
for (auto &window : windows)
{
if (window.second == nullptr)
{
id = window.first;
break;
}
}
if (id == 0)
id = lastWindowId++;
auto window = std::make_shared<Window>(player, id);
window->setSize(x, y);
window->setCallback(fn);
windows[id] = window;
return window;
}
void GUI::deleteWindow(std::shared_ptr<Window> window)
{
auto it = windows.find(window->getID());
if (it != windows.end())
{
it->second = nullptr;
}
}
void GUI::onGUIWindowAction()
{
auto it = windows.find(player->guiWindow.id);
if (it != windows.end() && it->second != nullptr)
{
it->second->call(player->guiWindow);
}
}
void QuickKeys::Init(LuaState &lua)
{
lua.getState()->new_usertype<QuickKeys>("QuickKeys",
"addQuickKey", &QuickKeys::addQuickKey,
"getQuickKey", &QuickKeys::getQuickKey,
"setQuickKey", &QuickKeys::setQuickKey,
"clear", &QuickKeys::clear,
"size", &QuickKeys::size
);
}
QuickKeys::QuickKeys(Player *player) : BaseMgr(player)
{
}
void QuickKeys::processUpdate()
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_QUICKKEYS);
packet->setPlayer(player);
packet->Send(false);
clear();
}
void QuickKeys::addQuickKey(const QuickKey &quickKey)
{
player->quickKeyChanges.quickKeys.push_back(quickKey.quickKey);
setChanged();
}
QuickKey QuickKeys::getQuickKey(int id) const
{
return QuickKey(player->quickKeyChanges.quickKeys.at(id));
}
void QuickKeys::setQuickKey(int id, const QuickKey &quickKey)
{
player->quickKeyChanges.quickKeys.at(id) = quickKey.quickKey;
setChanged();
}
void QuickKeys::clear()
{
player->quickKeyChanges.quickKeys.clear();
setChanged();
}
size_t QuickKeys::size() const
{
return player->quickKeyChanges.quickKeys.size();
}
void QuickKey::Init(LuaState &lua)
{
lua.getState()->new_usertype<QuickKey>("QuickKey",
"slot", sol::property(&QuickKey::getSlot, &QuickKey::setSlot),
"type", sol::property(&QuickKey::getType, &QuickKey::setType),
"itemId", sol::property(&QuickKey::getItemId, &QuickKey::setItemId)
);
}
QuickKey::QuickKey(mwmp::QuickKey &quickKey): quickKey(quickKey)
{
}
int QuickKey::getSlot() const
{
return quickKey.slot;
}
void QuickKey::setSlot(unsigned short slot)
{
quickKey.slot = slot;
}
mwmp::QuickKey::Type QuickKey::getType() const
{
return quickKey.type;
}
void QuickKey::setType(mwmp::QuickKey::Type type)
{
quickKey.type = type;
}
std::string QuickKey::getItemId() const
{
return quickKey.itemId;
}
void QuickKey::setItemId(const std::string &itemId)
{
quickKey.itemId = itemId;
}

@ -0,0 +1,82 @@
#pragma once
#include <queue>
#include "Window.hpp"
#include "BaseMgr.hpp"
class LuaState;
class Player;
class GUI final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit GUI(Player *player);
void messageBox(sol::function fn, const char *label, sol::this_environment te);
void customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te);
void inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
void passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
void listBox(sol::function fn, const char *label, const char *items, sol::this_environment te);
void onGUIAction();
//state 0 - disallow, 1 - allow
void setMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state);
void setMapVisibilityAll(unsigned short targetPID, unsigned short state);
std::shared_ptr<Window> createWindow(short x, short y, sol::function fn, sol::this_environment te);
void deleteWindow(std::shared_ptr<Window> window);
void onGUIWindowAction();
void addQuickKey(unsigned short slot, int type, const std::string &itemId);
std::string getTopicId(unsigned int i) const;
unsigned int getChanges() const;
private:
uint64_t generateGuiId();
void processUpdate() final;
std::unordered_map<int, std::shared_ptr<Window>> windows;
std::queue<std::pair<mwmp::BasePlayer::GUIMessageBox, sol::function>> guiQueue;
int lastWindowId;
};
class QuickKey
{
friend class QuickKeys;
public:
static void Init(LuaState &lua);
public:
explicit QuickKey(mwmp::QuickKey &quickKey);
int getSlot() const;
void setSlot(unsigned short slot);
mwmp::QuickKey::Type getType() const;
void setType(mwmp::QuickKey::Type slot);
std::string getItemId() const;
void setItemId(const std::string &itemId);
mwmp::QuickKey quickKey;
};
class QuickKeys final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit QuickKeys(Player *player);
void addQuickKey(const QuickKey &quickKey);
QuickKey getQuickKey(int id) const;
void setQuickKey(int id, const QuickKey &quickKey);
size_t size() const;
void clear();
private:
void processUpdate() final;
};

@ -0,0 +1,188 @@
//
// Created by koncord on 12.08.17.
//
#include <components/misc/stringops.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <apps/openmw/mwworld/inventorystore.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Inventory.hpp"
#include "NetActor.hpp"
#include <Player.hpp>
using namespace std;
void Inventory::Init(LuaState &lua)
{
lua.getState()->new_usertype<Inventory>("Inventory",
"getInventoryChangesSize", &Inventory::getChangesSize,
"addItem", &Inventory::addItem,
"removeItem", &Inventory::removeItem,
"getInventoryItem", &Inventory::getInventoryItem,
"equipItem", &Inventory::equipItem,
"unequipItem", &Inventory::unequipItem,
"hasItemEquipped", &Inventory::hasItemEquipped,
"getEquipmentItem", &Inventory::getEquipmentItem
);
}
Inventory::Inventory(NetActor *actor) : netActor(actor), equipmentChanged(false), inventoryChanged(false)
{
printf("Inventory::Inventory()\n");
}
Inventory::~Inventory()
{
printf("Inventory::~Inventory()\n");
}
void Inventory::update()
{
printf("Inventory::update()");
/*if (isEquipmentChanged())
{
if (netActor->isPlayer())
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
packet->setPlayer(dynamic_cast<Player *>(netActor));
packet->Send(false);
packet->Send(true);
}
else
{
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT);
packet->setActorList(&actorList);
packet->Send(actorList.guid);
if (sendToAll)
serverCell->sendToLoaded(packet, &actorList);
}
resetEquipmentFlag();
}*/
/*if (equipmentChanged)
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
packet->setPlayer(netActor->getNetCreature());
packet->Send(false);
packet->Send(true);
}
if (inventoryChanged != 0)
{
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
packet->setPlayer(netActor->getNetCreature());
packet->Send(false);
}
equipmentChanged = false;
inventoryChanged = 0;*/
}
int Inventory::getChangesSize() const
{
return netActor->getNetCreature()->inventoryChanges.items.size();
}
void Inventory::equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, float enchantmentCharge)
{
netActor->getNetCreature()->equipmentItems[slot].refId = refId;
netActor->getNetCreature()->equipmentItems[slot].count = count;
netActor->getNetCreature()->equipmentItems[slot].charge = charge;
netActor->getNetCreature()->equipmentItems[slot].enchantmentCharge = enchantmentCharge;
if (!Utils::vectorContains(&netActor->getNetCreature()->equipmentIndexChanges, slot))
netActor->getNetCreature()->equipmentIndexChanges.push_back(slot);
if (!equipmentChanged && netActor->isPlayer())
netActor->toPlayer()->addToUpdateQueue();
equipmentChanged = true;
}
void Inventory::unequipItem( unsigned short slot)
{
equipItem(slot, "", 0, -1, -1);
}
void Inventory::addItem(const std::string &refId, unsigned int count, int charge, float enchantmentCharge)
{
if (!inventoryChanged)
resetInventoryFlag();
mwmp::Item item;
item.refId = refId;
item.count = count;
item.charge = charge;
item.enchantmentCharge = enchantmentCharge;
netActor->getNetCreature()->inventoryChanges.items.emplace_back(item, mwmp::InventoryChanges::Action::Add);
if (netActor->isPlayer())
netActor->toPlayer()->addToUpdateQueue();
inventoryChanged = true;
}
void Inventory::removeItem(const std::string &refId, unsigned short count)
{
if (!inventoryChanged)
resetInventoryFlag();
mwmp::Item item;
item.refId = refId;
item.count = count;
netActor->getNetCreature()->inventoryChanges.items.emplace_back(item, mwmp::InventoryChanges::Action::Remove);
if (netActor->isPlayer())
netActor->toPlayer()->addToUpdateQueue();
inventoryChanged = true;
}
bool Inventory::hasItemEquipped(const std::string &refId) const
{
for (const auto &equipmentItem : netActor->getNetCreature()->equipmentItems)
if (Misc::StringUtils::ciEqual(equipmentItem.refId, refId))
return true;
return false;
}
std::tuple<std::string, int, int, double> Inventory::getEquipmentItem(unsigned short slot) const
{
const auto &item = netActor->getNetCreature()->equipmentItems[slot];
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
}
std::tuple<std::string, int, int, double> Inventory::getInventoryItem(unsigned int slot) const
{
const auto &item = netActor->getNetCreature()->inventoryChanges.items.at(slot).first;
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
}
void Inventory::resetEquipmentFlag()
{
equipmentChanged = false;
netActor->getNetCreature()->equipmentIndexChanges.clear();
}
bool Inventory::isEquipmentChanged()
{
return equipmentChanged;
}
bool Inventory::isInventoryChanged()
{
return inventoryChanged;
}
void Inventory::resetInventoryFlag()
{
inventoryChanged = false;
netActor->getNetCreature()->inventoryChanges.items.clear();
}

@ -0,0 +1,59 @@
//
// Created by koncord on 12.08.17.
//
#pragma once
#include <string>
#include <tuple>
class LuaState;
class NetActor;
class Inventory
{
public:
static void Init(LuaState &lua);
bool isEquipmentChanged();
void resetEquipmentFlag();
bool isInventoryChanged();
void resetInventoryFlag();
public:
explicit Inventory(NetActor *netActor);
~Inventory();
void update();
//inventory
int getChangesSize() const;
void addItem(const std::string& refId, unsigned int count, int charge, float enchantmentCharge);
void removeItem(const std::string& refId, unsigned short count);
/**
*
* @param slot
* @return refid, count, charge, enchantmentCharge
*/
std::tuple<std::string,int, int, double> getInventoryItem(unsigned int slot) const;
// equipment
void equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, float enchantmentCharge);
void unequipItem(unsigned short slot);
bool hasItemEquipped(const std::string& refId) const;
/**
*
* @param slot
* @return refid, count, charge, enchantmentCharge
*/
std::tuple<std::string,int, int, double> getEquipmentItem(unsigned short slot) const;
private:
// not controlled pointer
NetActor *netActor;
bool equipmentChanged;
bool inventoryChanged;
};

@ -2,18 +2,23 @@
// Created by koncord on 14.08.16.
//
#include <RakSleep.h>
#include <Getche.h>
#include <sstream>
#include <iostream>
#include <sstream>
#include <thread>
#include <Getche.h>
#include <RakPeerInterface.h>
#include "MasterClient.hpp"
#include <RakSleep.h>
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include "MasterClient.hpp"
#include "Networking.hpp"
#include "Players.hpp"
using namespace std;
using namespace mwmp;
using namespace RakNet;
@ -32,7 +37,7 @@ MasterClient::MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr
void MasterClient::SetPlayers(unsigned pl)
{
mutexData.lock();
if (queryData.GetPlayers() != pl)
if ((unsigned) queryData.GetPlayers() != pl)
{
queryData.SetPlayers(pl);
updated = true;
@ -43,7 +48,7 @@ void MasterClient::SetPlayers(unsigned pl)
void MasterClient::SetMaxPlayers(unsigned pl)
{
mutexData.lock();
if (queryData.GetMaxPlayers() != pl)
if ((unsigned) queryData.GetMaxPlayers() != pl)
{
queryData.SetMaxPlayers(pl);
updated = true;
@ -63,10 +68,10 @@ void MasterClient::SetHostname(std::string hostname)
mutexData.unlock();
}
void MasterClient::SetModname(std::string modname)
void MasterClient::SetGameModeName(std::string modeName)
{
mutexData.lock();
string substr = modname.substr(0, 200);
string substr = modeName.substr(0, 200);
if (queryData.GetGameMode() != substr)
{
queryData.SetGameMode(substr.c_str());
@ -108,7 +113,7 @@ void MasterClient::SetRuleValue(std::string key, double value)
void MasterClient::PushPlugin(Plugin plugin)
{
mutexData.lock();
queryData.plugins.push_back(plugin);
queryData.plugins.push_back(move(plugin));
updated = true;
mutexData.unlock();
}
@ -128,6 +133,10 @@ bool MasterClient::Process(RakNet::Packet *packet)
case ID_CONNECTION_REQUEST_ACCEPTED:
case ID_DISCONNECTION_NOTIFICATION:
break;
case ID_CONNECTION_BANNED:
Stop();
LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "Your server was banned on the master server. Contact the master server administrator for details.");
break;
case ID_MASTER_QUERY:
break;
case ID_MASTER_ANNOUNCE:
@ -148,7 +157,7 @@ bool MasterClient::Process(RakNet::Packet *packet)
}
break;
default:
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", (int) packet->data[0]);
return false;
}
return true;
@ -195,22 +204,22 @@ void MasterClient::Thread()
queryData.SetPassword((int) Networking::get().isPassworded());
queryData.SetVersion(TES3MP_VERSION);
auto *players = Players::getPlayers();
//auto *players = Players::getPlayers();
while (sRun)
{
SetPlayers((int) players->size());
SetPlayers((unsigned) Players::size());
auto pIt = players->begin();
if (queryData.players.size() != players->size())
auto pIt = Players::begin();
if (queryData.players.size() != Players::size())
{
queryData.players.clear();
updated = true;
}
else
{
for (int i = 0; pIt != players->end(); i++, pIt++)
for (int i = 0; pIt != Players::end(); i++, pIt++)
{
if (queryData.players[i] != pIt->second->npc.mName)
if (queryData.players[i] != (*pIt)->npc.mName)
{
queryData.players.clear();
updated = true;
@ -222,13 +231,12 @@ void MasterClient::Thread()
if (updated)
{
updated = false;
if (pIt != players->end())
if (pIt != Players::end())
{
for (auto player : *players)
{
if (!player.second->npc.mName.empty())
queryData.players.push_back(player.second->npc.mName);
}
Players::for_each([this](auto player) {
if (!player->npc.mName.empty())
queryData.players.push_back(player->npc.mName);
});
}
Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
}

@ -11,6 +11,7 @@
#include <components/openmw-mp/Master/MasterData.hpp>
#include <RakString.h>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include <atomic>
class MasterClient
{
@ -23,7 +24,7 @@ public:
void SetPlayers(unsigned pl);
void SetMaxPlayers(unsigned pl);
void SetHostname(std::string hostname);
void SetModname(std::string hostname);
void SetGameModeName(std::string modeName);
void SetRuleString(std::string key, std::string value);
void SetRuleValue(std::string key, double value);
void PushPlugin(Plugin plugin);
@ -46,7 +47,7 @@ private:
std::thread thrQuery;
mwmp::PacketMasterAnnounce pma;
RakNet::BitStream writeStream;
bool updated;
std::atomic<bool> updated;
};

@ -0,0 +1,143 @@
//
// Created by koncord on 25.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Base/BaseNetCreature.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "NetActor.hpp"
#include "Player.hpp"
using namespace std;
NetActor::NetActor() : inventory(this), cellAPI(this), isActorPlayer(false)
{
}
void NetActor::resetUpdateFlags()
{
baseInfoChanged = false;
shapeshiftChanged = false;
levelChanged = false;
statsChanged = false;
positionChanged = false;
skillsChanged = false;
attributesChanged = false;
}
std::tuple<float, float, float> NetActor::getPosition() const
{
return make_tuple(netCreature->position.pos[0], netCreature->position.pos[1], netCreature->position.pos[2]);
}
void NetActor::setPosition(float x, float y, float z)
{
netCreature->position.pos[0] = x;
netCreature->position.pos[1] = y;
netCreature->position.pos[2] = z;
if (!positionChanged && isPlayer())
toPlayer()->addToUpdateQueue();
positionChanged = true;
}
std::tuple<float, float> NetActor::getRotation() const
{
return make_tuple(netCreature->position.rot[0], netCreature->position.rot[2]);
}
void NetActor::setRotation(float x, float z)
{
netCreature->position.rot[0] = x;
netCreature->position.rot[2] = z;
if (!positionChanged && isPlayer())
toPlayer()->addToUpdateQueue();
positionChanged = true;
}
void NetActor::setMomentum(float x, float y, float z)
{
netCreature->momentum.pos[0] = x;
netCreature->momentum.pos[1] = y;
netCreature->momentum.pos[2] = z;
if (!momentumChanged && isPlayer())
toPlayer()->addToUpdateQueue();
momentumChanged = true;
}
std::tuple<float, float> NetActor::getHealth() const
{
return make_tuple(netCreature->creatureStats.mDynamic[0].mBase, netCreature->creatureStats.mDynamic[0].mCurrent);
}
void NetActor::setHealth(float base, float current)
{
netCreature->creatureStats.mDynamic[0].mBase = base;
netCreature->creatureStats.mDynamic[0].mCurrent = current;
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 0))
netCreature->statsDynamicIndexChanges.push_back(0);
if (!statsChanged && isPlayer())
toPlayer()->addToUpdateQueue();
statsChanged = true;
}
std::tuple<float, float> NetActor::getMagicka() const
{
return make_tuple(netCreature->creatureStats.mDynamic[1].mBase, netCreature->creatureStats.mDynamic[1].mCurrent);
}
void NetActor::setMagicka(float base, float current)
{
netCreature->creatureStats.mDynamic[1].mBase = base;
netCreature->creatureStats.mDynamic[1].mCurrent = current;
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 1))
netCreature->statsDynamicIndexChanges.push_back(1);
if (!statsChanged && isPlayer())
toPlayer()->addToUpdateQueue();
statsChanged = true;
}
std::tuple<float, float> NetActor::getFatigue() const
{
return make_tuple(netCreature->creatureStats.mDynamic[2].mBase, netCreature->creatureStats.mDynamic[2].mCurrent);
}
void NetActor::setFatigue(float base, float current)
{
netCreature->creatureStats.mDynamic[2].mBase = base;
netCreature->creatureStats.mDynamic[2].mCurrent = current;
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 2))
netCreature->statsDynamicIndexChanges.push_back(2);
if (!statsChanged && isPlayer())
toPlayer()->addToUpdateQueue();
statsChanged = true;
}
Inventory &NetActor::getInventory()
{
return inventory;
}
Cells &NetActor::getCell()
{
return cellAPI;
}
Player *NetActor::toPlayer()
{
if (isPlayer())
return dynamic_cast<Player*>(this);
return nullptr;
}

@ -0,0 +1,83 @@
//
// Created by koncord on 25.08.17.
//
#pragma once
#include <RakNetTypes.h>
#include <tuple>
#include "Inventory.hpp"
#include "Cells.hpp"
namespace mwmp
{
class BasePlayer;
class BaseNetCreature;
class BaseActor;
}
class NetActor
{
public:
NetActor();
virtual ~NetActor() = default;
void resetUpdateFlags();
/**
*
* @return x, y, z
*/
std::tuple<float, float, float> getPosition() const;
void setPosition(float x, float y, float z);
/**
*
* @return x, y
*/
std::tuple<float, float> getRotation() const;
void setRotation(float x, float z);
void setMomentum(float x, float y, float z);
/**
*
* @return base, current
*/
std::tuple<float, float> getHealth() const;
void setHealth(float base, float current);
/**
*
* @return base, current
*/
std::tuple<float, float> getMagicka() const;
void setMagicka(float base, float current);
/**
*
* @return base, current
*/
std::tuple<float, float> getFatigue() const;
void setFatigue(float base, float current);
Inventory &getInventory();
Cells &getCell();
mwmp::BaseNetCreature *getNetCreature() { return netCreature; }
bool isPlayer() const { return isActorPlayer; }
Player *toPlayer();
protected:
bool baseInfoChanged, shapeshiftChanged, levelChanged, statsChanged, positionChanged, momentumChanged, attributesChanged, skillsChanged, aiChanged;
mwmp::BasePlayer *basePlayer;
mwmp::BaseNetCreature *netCreature;
Inventory inventory;
Cells cellAPI;
bool isActorPlayer;
};

@ -2,10 +2,12 @@
// Created by koncord on 12.01.16.
//
#include "Player.hpp"
#include "processors/ProcessorInitializer.hpp"
#include <RakPeer.h>
#include <chrono>
#include <iostream>
#include <thread>
#include <Kbhit.h>
#include <RakPeer.h>
#include <components/misc/stringops.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
@ -13,25 +15,24 @@
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
#include <iostream>
#include <Script/Script.hpp>
#include <Script/API/TimerAPI.hpp>
#include <chrono>
#include <thread>
#include <Script/EventController.hpp>
#include "processors/ProcessorInitializer.hpp"
#include "processors/PlayerProcessor.hpp"
#include "processors/ActorProcessor.hpp"
#include "processors/ObjectProcessor.hpp"
#include "processors/WorldstateProcessor.hpp"
#include "Networking.hpp"
#include "MasterClient.hpp"
#include "Cell.hpp"
#include "CellController.hpp"
#include "processors/PlayerProcessor.hpp"
#include "processors/ActorProcessor.hpp"
#include "processors/ObjectProcessor.hpp"
#include "processors/WorldstateProcessor.hpp"
#include "Players.hpp"
using namespace mwmp;
using namespace std;
Networking *Networking::sThis = 0;
Networking *Networking::sThis = nullptr;
static int currentMpNum = 0;
static bool pluginEnforcementState = true;
@ -40,42 +41,30 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
{
sThis = this;
this->peer = peer;
players = Players::getPlayers();
CellController::create();
playerPacketController = new PlayerPacketController(peer);
actorPacketController = new ActorPacketController(peer);
objectPacketController = new ObjectPacketController(peer);
worldstatePacketController = new WorldstatePacketController(peer);
playerPacketController = make_unique<PlayerPacketController>(peer);
actorPacketController = make_unique<ActorPacketController>(peer);
objectPacketController = make_unique<ObjectPacketController>(peer);
worldstatePacketController = make_unique<WorldstatePacketController>(peer);
// Set send stream
playerPacketController->SetStream(0, &bsOut);
actorPacketController->SetStream(0, &bsOut);
objectPacketController->SetStream(0, &bsOut);
worldstatePacketController->SetStream(0, &bsOut);
playerPacketController->SetStream(nullptr, &bsOut);
actorPacketController->SetStream(nullptr, &bsOut);
objectPacketController->SetStream(nullptr, &bsOut);
worldstatePacketController->SetStream(nullptr, &bsOut);
running = true;
exitCode = 0;
Script::Call<Script::CallbackIdentity("OnServerInit")>();
serverPassword = TES3MP_DEFAULT_PASSW;
ProcessorInitializer();
createChannel(); // create Default channel
}
Networking::~Networking()
{
Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
CellController::destroy();
sThis = 0;
delete playerPacketController;
delete actorPacketController;
delete objectPacketController;
delete worldstatePacketController;
luaState.getEventCtrl().Call<CoreEvent::ON_EXIT>(false);
}
void Networking::setServerPassword(std::string passw) noexcept
@ -90,13 +79,13 @@ bool Networking::isPassworded() const
void Networking::processPlayerPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
PlayerPacket *myPacket = playerPacketController->GetPacket(packet->data[0]);
if (packet->data[0] == ID_HANDSHAKE)
{
myPacket->setPlayer(player);
myPacket->setPlayer(player.get());
myPacket->Read();
if (player->isHandshaked())
@ -136,16 +125,16 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (packet->data[0] == ID_LOADED)
{
player->setLoadState(Player::LOADED);
player->joinChannel(0, "Default");
static constexpr unsigned int ident = Script::CallbackIdentity("OnPlayerConnect");
Script::CallBackReturn<ident> result = true;
Script::Call<ident>(result, Players::getPlayer(packet->guid)->getId());
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_CONNECT>(player.get());
if (!result)
if (peer->GetConnectionState(player->guid) != RakNet::ConnectionState::IS_CONNECTED)
{
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(Players::getPlayer(packet->guid));
LOG_MESSAGE(Log::LOG_TRACE, "Player \"%s\" Disconnected by ON_PLAYER_CONNECT event", player->getName().c_str());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(false);
Players::deletePlayer(packet->guid);
Players::deletePlayerByGUID(packet->guid);
return;
}
}
@ -153,7 +142,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_BASEINFO about %s", player->npc.mName.c_str());
myPacket->setPlayer(player);
myPacket->setPlayer(player.get());
myPacket->Read();
myPacket->Send(true);
}
@ -169,37 +158,37 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!PlayerProcessor::Process(*packet))
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", (int) packet->data[0]);
}
void Networking::processActorPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
return;
if (!ActorProcessor::Process(*packet, baseActorList))
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", (int) packet->data[0]);
}
void Networking::processObjectPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
return;
if (!ObjectProcessor::Process(*packet, baseObjectList))
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ObjectPacket with identifier %i has arrived", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ObjectPacket with identifier %i has arrived", (int) packet->data[0]);
}
void Networking::processWorldstatePacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
return;
@ -209,15 +198,15 @@ void Networking::processWorldstatePacket(RakNet::Packet *packet)
}
void Networking::update(RakNet::Packet *packet)
bool Networking::update(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
bsIn.IgnoreBytes((unsigned int) RakNet::RakNetGUID::size()); // Ignore GUID from received packet
if (player == 0)
if (player == nullptr)
{
if (packet->data[0] == ID_GAME_PREINIT)
{
@ -247,7 +236,6 @@ void Networking::update(RakNet::Packet *packet)
// the server
if (it == hashList.end())
break;
}
else // name is incorrect
break;
@ -271,38 +259,42 @@ void Networking::update(RakNet::Packet *packet)
packetPreInit.setChecksums(&tmp);
packetPreInit.Send(packet->systemAddress);
}
return;
return false;
}
playerPacketController->SetStream(&bsIn, 0);
playerPacketController->SetStream(&bsIn, nullptr);
playerPacketController->GetPacket(ID_HANDSHAKE)->RequestData(packet->guid);
Players::newPlayer(packet->guid);
player = Players::getPlayer(packet->guid);
return;
player = Players::addPlayer(packet->guid);
return false;
}
else if (playerPacketController->ContainsPacket(packet->data[0]))
if (playerPacketController->ContainsPacket(packet->data[0]))
{
playerPacketController->SetStream(&bsIn, 0);
playerPacketController->SetStream(&bsIn, nullptr);
processPlayerPacket(packet);
}
else if (actorPacketController->ContainsPacket(packet->data[0]))
{
actorPacketController->SetStream(&bsIn, 0);
actorPacketController->SetStream(&bsIn, nullptr);
processActorPacket(packet);
}
else if (objectPacketController->ContainsPacket(packet->data[0]))
{
objectPacketController->SetStream(&bsIn, 0);
objectPacketController->SetStream(&bsIn, nullptr);
processObjectPacket(packet);
}
else if (worldstatePacketController->ContainsPacket(packet->data[0]))
{
worldstatePacketController->SetStream(&bsIn, 0);
worldstatePacketController->SetStream(&bsIn, nullptr);
processWorldstatePacket(packet);
}
else
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled RakNet packet with identifier %i has arrived", packet->data[0]);
{
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled RakNet packet with identifier %i has arrived", (int) packet->data[0]);
return false;
}
return true;
}
void Networking::newPlayer(RakNet::RakNetGUID guid)
@ -315,27 +307,24 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Sending info about other players to %lu", guid.g);
for (TPlayers::iterator pl = players->begin(); pl != players->end(); pl++) //sending other players to new player
Players::for_each([this, &guid](auto pl) //sending other players to new player
{
// If we are iterating over the new player, don't send the packets below
if (pl->first == guid) continue;
if (pl->guid == guid) return;
// If an invalid key makes it into the Players map, ignore it
else if (pl->first == RakNet::UNASSIGNED_CRABNET_GUID) continue;
// if player not fully connected
else if (pl->second == nullptr) continue;
else if (pl->guid == RakNet::UNASSIGNED_CRABNET_GUID) return;
// If we are iterating over a player who has inputted their name, proceed
else if (pl->second->getLoadState() == Player::POSTLOADED)
else if (pl->getLoadState() == Player::POSTLOADED)
{
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl);
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->Send(guid);
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(guid);
@ -345,7 +334,7 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(guid);
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(guid);
}
}
});
LOG_APPEND(Log::LOG_WARN, "- Done");
@ -353,34 +342,35 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
void Networking::disconnectPlayer(RakNet::RakNetGUID guid)
{
Player *player = Players::getPlayer(guid);
if (!player)
auto player = Players::getPlayerByGUID(guid);
if (player == nullptr)
return;
Script::Call<Script::CallbackIdentity("OnPlayerDisconnect")>(player->getId());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player);
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_DISCONNECT>(player.get());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);
Players::deletePlayer(guid);
Players::deletePlayerByGUID(guid);
}
PlayerPacketController *Networking::getPlayerPacketController() const
{
return playerPacketController;
return playerPacketController.get();
}
ActorPacketController *Networking::getActorPacketController() const
{
return actorPacketController;
return actorPacketController.get();
}
ObjectPacketController *Networking::getObjectPacketController() const
{
return objectPacketController;
return objectPacketController.get();
}
WorldstatePacketController *Networking::getWorldstatePacketController() const
{
return worldstatePacketController;
return worldstatePacketController.get();
}
BaseActorList *Networking::getLastActorList()
@ -398,6 +388,11 @@ BaseWorldstate *Networking::getLastWorldstate()
return &baseWorldstate;
}
Worldstate *Networking::getServerWorldstate()
{
return &serverWorldstate;
}
int Networking::getCurrentMpNum()
{
return currentMpNum;
@ -411,7 +406,7 @@ void Networking::setCurrentMpNum(int value)
int Networking::incrementMpNum()
{
currentMpNum++;
Script::Call<Script::CallbackIdentity("OnMpNumIncrement")>(currentMpNum);
luaState.getEventCtrl().Call<CoreEvent::ON_MP_REFNUM>(currentMpNum);
return currentMpNum;
}
@ -425,12 +420,11 @@ void Networking::setPluginEnforcementState(bool state)
pluginEnforcementState = state;
}
const Networking &Networking::get()
Networking &Networking::get()
{
return *sThis;
}
Networking *Networking::getPtr()
{
return sThis;
@ -448,20 +442,18 @@ PacketPreInit::PluginContainer Networking::getPluginListSample()
while (true)
{
unsigned field = 0;
auto name = "";
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(name, id, field++);
if (strlen(name) == 0)
auto name = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, false, string>(id, field++);
if (name.empty())
break;
PacketPreInit::HashList hashList;
while (true)
{
auto hash = "";
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(hash, id, field++);
if (strlen(hash) == 0)
auto hash = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, false, string>(id, field++);
if (hash.empty())
break;
hashList.push_back((unsigned)stoul(hash));
}
pls.push_back({name, hashList});
pls.emplace_back(name, hashList);
id++;
}
return pls;
@ -473,12 +465,46 @@ void Networking::stopServer(int code)
exitCode = code;
}
using hrclock = chrono::high_resolution_clock;
template<typename _Rep, typename _Period>
hrclock::time_point limitTPS(const chrono::duration<_Rep, _Period> &limit)
{
static hrclock::time_point now = hrclock::now();
static auto last = now;
now = hrclock::now();
hrclock::duration delta = now - last;
auto tmp = last;
last = now;
if (delta < limit)
this_thread::sleep_for(limit - delta);
return tmp;
}
int Networking::mainLoop()
{
RakNet::Packet *packet;
auto &timerCtrl = luaState.getTimerCtrl();
auto OneSecTimer = []() {
static hrclock::time_point time;
time = hrclock::now();
static auto last2 = time;
hrclock::duration delta = time - last2;
if (delta >= 1s)
{
last2 = time;
return true;
}
return false;
};
while (running)
{
bool updated = false;
if (kbhit() && getch() == '\n')
break;
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
@ -521,15 +547,27 @@ int Networking::mainLoop()
case ID_UNCONNECTED_PING:
break;
default:
update(packet);
updated = update(packet);
break;
}
}
TimerAPI::Tick();
this_thread::sleep_for(chrono::milliseconds(1));
timerCtrl.tick();
if (updated)
{
Players::processUpdated();
serverWorldstate.update();
}
const int limit = 60;
auto t = limitTPS(1.0s / limit); // 60 ticks per 1 second
if (OneSecTimer())
printf("TPS %.1Lf / %d \n", 1.0s / (hrclock::now() - t), limit);
}
TimerAPI::Terminate();
timerCtrl.terminate();
return exitCode;
}
@ -565,17 +603,17 @@ int Networking::getAvgPing(RakNet::AddressOrGUID addr) const
MasterClient *Networking::getMasterClient()
{
return mclient;
return mclient.get();
}
void Networking::InitQuery(std::string queryAddr, unsigned short queryPort)
void Networking::InitQuery(const std::string &queryAddr, unsigned short queryPort)
{
mclient = new MasterClient(peer, queryAddr, queryPort);
mclient = make_unique<MasterClient>(peer, queryAddr, queryPort);
}
void Networking::postInit()
{
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
luaState.getEventCtrl().Call<CoreEvent::ON_POST_INIT>();
samples = getPluginListSample();
if (mclient)
{
@ -588,3 +626,41 @@ void Networking::postInit()
}
}
}
std::shared_ptr<ChatChannel> Networking::getChannel(unsigned id)
{
auto it = chatChannels.find(id);
if (it != chatChannels.end())
return it->second;
else
return nullptr;
}
unsigned Networking::createChannel()
{
static unsigned lastChatId = 0;
unsigned id = 0;
for (auto &channel : chatChannels)
{
if (channel.second == nullptr)
id = channel.first;
}
if (id == 0)
id = lastChatId++;
chatChannels[id] = make_shared<ChatChannel>();
return id;
}
bool Networking::closeChannel(unsigned id)
{
auto it = chatChannels.find(id);
if (it != chatChannels.end())
{
it->second = nullptr;
return true;
}
return false;
}

@ -6,11 +6,20 @@
#include <components/openmw-mp/Controllers/ObjectPacketController.hpp>
#include <components/openmw-mp/Controllers/WorldstatePacketController.hpp>
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
#include "Player.hpp"
#include <apps/openmw-mp/Script/LuaState.hpp>
#include <apps/openmw-mp/Worldstate.hpp>
class MasterClient;
namespace mwmp
{
struct ChatChannel
{
ChatChannel()
{
}
std::vector<std::weak_ptr<Player>> members;
};
class Networking
{
public:
@ -29,7 +38,7 @@ namespace mwmp
void processActorPacket(RakNet::Packet *packet);
void processObjectPacket(RakNet::Packet *packet);
void processWorldstatePacket(RakNet::Packet *packet);
void update(RakNet::Packet *packet);
bool update(RakNet::Packet *packet);
unsigned short numberOfConnections() const;
unsigned int maxConnections() const;
@ -44,9 +53,12 @@ namespace mwmp
ObjectPacketController *getObjectPacketController() const;
WorldstatePacketController *getWorldstatePacketController() const;
LuaState &getState() {return luaState;}
BaseActorList *getLastActorList();
BaseObjectList *getLastObjectList();
BaseWorldstate *getLastWorldstate();
Worldstate *getServerWorldstate();
int getCurrentMpNum();
void setCurrentMpNum(int value);
@ -56,36 +68,45 @@ namespace mwmp
void setPluginEnforcementState(bool state);
MasterClient *getMasterClient();
void InitQuery(std::string queryAddr, unsigned short queryPort);
void InitQuery(const std::string &queryAddr, unsigned short queryPort);
void setServerPassword(std::string passw) noexcept;
bool isPassworded() const;
static const Networking &get();
static Networking &get();
static Networking *getPtr();
void postInit();
std::shared_ptr<ChatChannel> getChannel(unsigned id);
unsigned createChannel();
bool closeChannel(unsigned id);
private:
LuaState luaState;
PacketPreInit::PluginContainer getPluginListSample();
std::string serverPassword;
static Networking *sThis;
RakNet::RakPeerInterface *peer;
RakNet::BitStream bsOut;
TPlayers *players;
MasterClient *mclient;
std::unique_ptr<MasterClient> mclient;
BaseActorList baseActorList;
BaseObjectList baseObjectList;
BaseWorldstate baseWorldstate;
PlayerPacketController *playerPacketController;
ActorPacketController *actorPacketController;
ObjectPacketController *objectPacketController;
WorldstatePacketController *worldstatePacketController;
Worldstate serverWorldstate;
std::unique_ptr<PlayerPacketController> playerPacketController;
std::unique_ptr<ActorPacketController> actorPacketController;
std::unique_ptr<ObjectPacketController> objectPacketController;
std::unique_ptr<WorldstatePacketController> worldstatePacketController;
bool running;
int exitCode;
PacketPreInit::PluginContainer samples;
std::unordered_map<unsigned, std::shared_ptr<ChatChannel>> chatChannels;
};
}

@ -0,0 +1,595 @@
//
// Created by koncord on 26.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Networking.hpp"
#include "Object.hpp"
#include "Player.hpp"
using namespace std;
void Object::Init(LuaState &lua)
{
lua.getState()->new_usertype<Object>("Object",
"refId", sol::property(&BaseObject::getRefId, &BaseObject::setRefId),
"refNum", sol::property(&BaseObject::getRefNum, &BaseObject::setRefNum),
"mpNum", sol::property(&BaseObject::getMpNum, &BaseObject::setMpNum),
"guid", sol::property(&BaseObject::getGuid, &BaseObject::setGuid),
"getPosition", &Object::getPosition,
"setPosition", &Object::setPosition,
"getRotation", &Object::getRotation,
"setRotation", &Object::setRotation,
"count", sol::property(&Object::getCount, &Object::setCount),
"goldValue", sol::property(&Object::getGoldValue, &Object::setGoldValue),
"scale", sol::property(&Object::getScale, &Object::setScale),
"state", sol::property(&Object::getState, &Object::setState),
"lockLevel", sol::property(&Object::getLockLevel, &Object::setLockLevel),
"doorState", sol::property(&Object::getDoorState, &Object::setDoorState),
"hasContainer", &Object::hasContainer,
"setTeleportState", &Object::setTeleportState,
"setDoorDestination", &Object::setDoorDestination,
"setDisarmState", &Object::setDisarmState,
"setMasterState", &Object::setMasterState
);
}
Object::Object()
{
}
Object::~Object()
{
}
void Object::update()
{
}
tuple<float, float, float> Object::getPosition() const
{
return make_tuple(object.position.pos[0], object.position.pos[1], object.position.pos[2]);
}
void Object::setPosition(float x, float y, float z)
{
object.position.pos[0] = x;
object.position.pos[1] = y;
object.position.pos[2] = z;
object.droppedByPlayer = false;
changedObjectPlace = true;
}
tuple<float, float, float> Object::getRotation() const
{
return make_tuple(object.position.rot[0], object.position.rot[1], object.position.rot[2]);
}
void Object::setRotation(float x, float y, float z)
{
object.position.rot[0] = x;
object.position.rot[1] = y;
object.position.rot[2] = z;
changedObjectPlace = true;
}
BaseObject::BaseObject(): changedBase(false), copied(false)
{
}
string BaseObject::getRefId() const
{
return object.refId;
}
void BaseObject::setRefId(const string &refId)
{
changedBase = true;
object.refId = refId;
}
unsigned BaseObject::getRefNum() const
{
return object.refNumIndex;
}
void BaseObject::setRefNum(unsigned refNum)
{
changedBase = true;
object.refNumIndex = refNum;
}
unsigned BaseObject::getMpNum() const
{
return object.mpNum;
}
void BaseObject::setMpNum(unsigned mpNum)
{
changedBase = true;
object.mpNum = mpNum;
}
RakNet::RakNetGUID BaseObject::getGuid() const
{
return object.guid;
}
void BaseObject::setGuid(const RakNet::RakNetGUID &guid)
{
changedBase = true;
object.guid = guid;
object.isPlayer = true;
}
int Object::getCount() const
{
return object.count;
}
void Object::setCount(int count)
{
changedObjectPlace = true;
object.count = count;
}
int Object::getCharge() const
{
return object.charge;
}
void Object::setCharge(int charge)
{
changedObjectPlace = true;
object.charge = charge;
}
float Object::getEnchantmentCharge() const
{
return object.enchantmentCharge;
}
void Object::setEnchantmentCharge(float enchantmentCharge)
{
changedObjectPlace = true;
object.enchantmentCharge = enchantmentCharge;
}
int Object::getGoldValue() const
{
return object.goldValue;
}
void Object::setGoldValue(int gold)
{
changedObjectPlace = true;
object.goldValue = gold;
}
float Object::getScale() const
{
return object.scale;
}
void Object::setScale(float scale)
{
changedObjectScale = true;
object.scale = scale;
}
bool Object::getState() const
{
return object.objectState;
}
void Object::setState(bool state)
{
changedObjectState = true;
object.objectState = state;
}
int Object::getDoorState() const
{
return object.doorState;
}
void Object::setDoorState(int state)
{
changedDoorState = true;
object.doorState = state;
}
int Object::getLockLevel() const
{
return object.lockLevel;
}
void Object::setLockLevel(int locklevel)
{
changedObjectLock = true;
object.lockLevel = locklevel;
}
bool Object::hasContainer() const
{
return object.hasContainer;
}
void Object::setTeleportState(bool state)
{
changedDoorDestination = true;
object.teleportState = state;
}
void Object::setDoorDestination(const std::string &cellDescription, float posX, float posY, float posZ, float rotX, float rotY, float rotZ)
{
changedDoorDestination = true;
object.destinationCell = Utils::getCellFromDescription(cellDescription);
object.destinationPosition.pos[0] = posX;
object.destinationPosition.pos[1] = posY;
object.destinationPosition.pos[2] = posZ;
object.destinationPosition.rot[0] = rotX;
object.destinationPosition.rot[1] = rotY;
object.destinationPosition.rot[2] = rotZ;
}
void Object::setDisarmState(bool state)
{
changedObjectTrap = true;
object.isDisarmed = state;
}
void Object::setMasterState(bool state)
{
changedObjectSpawn = true;
object.hasMaster = state;
}
// TODO: Make this actually reflect the capabilities offered by containers in 0.6.3
void Container::Init(LuaState &lua)
{
lua.getState()->new_usertype<Container>("Container",
"refId", sol::property(&BaseObject::getRefId, &BaseObject::setRefId),
"refNum", sol::property(&BaseObject::getRefNum, &BaseObject::setRefNum),
"mpNum", sol::property(&BaseObject::getMpNum, &BaseObject::setMpNum),
"getItem", &Container::getItem,
"addItem", &Container::addItem,
"setItem", &Container::setItem,
"getActionCount", &Container::getActionCount
);
}
Container::Container()
{
}
tuple<string, int, int, double> Container::getItem(int i) const
{
auto &item = object.containerItems.at(i);
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
}
void Container::setItem(int i, const string &refId, int count, int charge, float enchantmentCharge)
{
auto &item = object.containerItems.at(i);
item.refId = refId;
item.count = count;
item.charge = charge;
item.enchantmentCharge = enchantmentCharge;
changed = true;
}
void Container::addItem(const string &refId, int count, int charge, float enchantmentCharge)
{
mwmp::ContainerItem item;
item.refId = refId;
item.count = count;
item.charge = charge;
item.enchantmentCharge = enchantmentCharge;
object.containerItems.push_back(item);
changed = true;
}
int Container::getActionCount(int i) const
{
return object.containerItems.at(i).actionCount;
}
size_t Container::size() const
{
return object.containerItems.size();
}
void ObjectController::Init(LuaState &lua)
{
sol::table objectCtrl = lua.getState()->create_table("ObjectCtrl");
objectCtrl.set_function("sendObjects", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
const std::string &cellDescription) {
return lua.getObjectCtrl().sendObjects(player, objects, Utils::getCellFromDescription(cellDescription));
});
objectCtrl.set_function("sendContainers", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Container>>> objects,
const std::string &cellDescription) {
return lua.getObjectCtrl().sendContainers(player, objects, Utils::getCellFromDescription(cellDescription));
});
objectCtrl.set_function("sendConsoleCommand", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
const std::string &cellDescription, const std::string &command,
bool broadcast) {
return lua.getObjectCtrl().sendConsoleCommand(player, objects, Utils::getCellFromDescription(cellDescription),
command, broadcast);
});
objectCtrl.set_function("requestContainers", [&lua](shared_ptr<Player> player) {
lua.getObjectCtrl().requestContainers(player);
});
}
shared_ptr<vector<shared_ptr<Object>>> ObjectController::copyObjects(mwmp::BaseObjectList &objectList)
{
auto objects = make_shared<vector<shared_ptr<Object>>>();
for (auto &obj : objectList.baseObjects)
{
auto object = new Object;
object->copied = true;
object->object = obj;
objects->emplace_back(object);
}
return objects;
}
shared_ptr<vector<shared_ptr<Container>>> ObjectController::copyContainers(mwmp::BaseObjectList &objectList)
{
auto containers = make_shared<vector<shared_ptr<Container>>>();
for (auto &obj : objectList.baseObjects)
{
auto container = new Container;
container->copied = true;
container->object = obj;
containers->emplace_back(container);
}
return containers;
}
void ObjectController::sendObjects(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects, const ESM::Cell &cell, bool broadcast)
{
enum Type
{
DOOR_STATE = 0,
DOOR_DESTINATION,
OBJECT_STATE,
OBJECT_SCALE,
OBJECT_TRAP,
OBJECT_LOCK,
OBJECT_DELETE,
OBJECT_SPAWN,
OBJECT_PLACE,
LAST
};
mwmp::BaseObjectList objectLists[Type::LAST];
bool changed[Type::LAST];
for (auto &objectList : objectLists)
{
objectList.action = mwmp::BaseObjectList::Action::Set;
objectList.guid = player->guid;
objectList.cell = cell;
}
for (auto &object : *objects)
{
//sendObject(player.get(), object.get());
bool validNewObjOrCopy = (!object->copied && object->changedBase) || object->copied;
if (object->changedDoorState && validNewObjOrCopy)
{
changed[Type::DOOR_STATE] = true;
objectLists[Type::DOOR_STATE].baseObjects.push_back(object->object);
}
if (object->changedDoorDestination && validNewObjOrCopy)
{
changed[Type::DOOR_DESTINATION] = true;
objectLists[Type::DOOR_DESTINATION].baseObjects.push_back(object->object);
}
if (object->changedObjectState && validNewObjOrCopy)
{
changed[Type::OBJECT_STATE] = true;
objectLists[Type::OBJECT_STATE].baseObjects.push_back(object->object);
}
if (object->changedObjectScale && validNewObjOrCopy)
{
changed[Type::OBJECT_SCALE] = true;
objectLists[Type::OBJECT_SCALE].baseObjects.push_back(object->object);
}
if (object->changedObjectTrap && validNewObjOrCopy)
{
changed[Type::OBJECT_TRAP] = true;
objectLists[Type::OBJECT_TRAP].baseObjects.push_back(object->object);
}
if (object->changedObjectLock && validNewObjOrCopy)
{
changed[Type::OBJECT_LOCK] = true;
objectLists[Type::OBJECT_LOCK].baseObjects.push_back(object->object);
}
if (object->changedObjectDelete && validNewObjOrCopy)
{
changed[Type::OBJECT_DELETE] = true;
objectLists[Type::OBJECT_DELETE].baseObjects.push_back(object->object);
}
if (object->changedObjectSpawn && validNewObjOrCopy)
{
changed[Type::OBJECT_SPAWN] = true;
objectLists[Type::OBJECT_SPAWN].baseObjects.push_back(object->object);
}
if (object->changedObjectPlace && validNewObjOrCopy)
{
changed[Type::OBJECT_PLACE] = true;
objectLists[Type::OBJECT_PLACE].baseObjects.push_back(object->object);
}
}
auto worldCtrl = mwmp::Networking::get().getObjectPacketController();
if (changed[Type::DOOR_STATE])
{
auto packet = worldCtrl->GetPacket(ID_DOOR_STATE);
auto &objectList = objectLists[Type::DOOR_STATE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::DOOR_DESTINATION])
{
auto packet = worldCtrl->GetPacket(ID_DOOR_DESTINATION);
auto &objectList = objectLists[Type::DOOR_DESTINATION];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_STATE])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_STATE);
auto &objectList = objectLists[Type::OBJECT_STATE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_SCALE])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_SCALE);
auto &objectList = objectLists[Type::OBJECT_SCALE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_TRAP])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_TRAP);
auto &objectList = objectLists[Type::OBJECT_TRAP];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_LOCK])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_LOCK);
auto &objectList = objectLists[Type::OBJECT_LOCK];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_DELETE])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_DELETE);
auto &objectList = objectLists[Type::OBJECT_DELETE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_SCALE])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_SPAWN);
auto &objectList = objectLists[Type::OBJECT_SCALE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
if (changed[Type::OBJECT_PLACE])
{
auto packet = worldCtrl->GetPacket(ID_OBJECT_PLACE);
auto &objectList = objectLists[Type::OBJECT_PLACE];
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
}
void ObjectController::sendConsoleCommand(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast)
{
mwmp::BaseObjectList objectList;
objectList.cell = cell;
objectList.consoleCommand = consoleCommand;
objectList.guid = player->guid;
for (auto &object : *objects)
objectList.baseObjects.push_back(object->object);
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONSOLE_COMMAND);
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
void ObjectController::sendContainers(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Container>>> objects,
const ESM::Cell &cell, bool broadcast)
{
mwmp::BaseObjectList objectList;
objectList.cell = cell;
objectList.action = mwmp::BaseObjectList::Action::Set;
objectList.guid = player->guid;
for (auto &object : *objects)
{
bool validNewObjOrCopy = (!object->copied && object->changedBase) || object->copied;
if (object->changed && validNewObjOrCopy)
objectList.baseObjects.push_back(object->object);
}
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONTAINER);
packet->setObjectList(&objectList);
packet->Send(false);
if (broadcast)
packet->Send(true);
}
void ObjectController::requestContainers(shared_ptr<Player> player)
{
mwmp::BaseObjectList objectList;
objectList.action = mwmp::BaseObjectList::Action::Request;
objectList.guid = player->guid;
objectList.cell = player->cell;
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONTAINER);
packet->setObjectList(&objectList);
packet->Send(objectList.guid);
}

@ -0,0 +1,130 @@
#pragma once
#include <tuple>
#include <components/openmw-mp/Base/BaseObject.hpp>
#include <memory>
class LuaState;
class Player;
class BaseObject
{
public:
BaseObject();
std::string getRefId() const;
void setRefId(const std::string &refId);
unsigned getRefNum() const;
void setRefNum(unsigned refNum);
unsigned getMpNum() const;
void setMpNum(unsigned mpNum);
RakNet::RakNetGUID getGuid() const;
void setGuid(const RakNet::RakNetGUID &guid);
//void setEventCell(const std::string &cellDescription);
mwmp::BaseObject object;
bool changedBase;
bool copied;
};
class Object : public BaseObject
{
public:
static void Init(LuaState &lua);
public:
Object();
~Object();
void update();
/**
*
* @return x, y, z
*/
std::tuple<float, float, float> getPosition() const;
void setPosition(float x, float y, float z);
/**
*
* @return x, y, z
*/
std::tuple<float, float, float> getRotation() const;
void setRotation(float x, float y, float z);
int getCount() const;
void setCount(int count);
int getCharge() const;
void setCharge(int charge);
float getEnchantmentCharge() const;
void setEnchantmentCharge(float enchantmentCharge);
int getGoldValue() const;
void setGoldValue(int gold);
float getScale() const;
void setScale(float scale);
bool getState() const;
void setState(bool state);
int getDoorState() const;
void setDoorState(int state);
int getLockLevel() const;
void setLockLevel(int locklevel);
bool hasContainer() const;
void setTeleportState(bool state);
void setDoorDestination(const std::string &cellDescription, float posX, float posY, float posZ, float rotX, float rotY, float rotZ);
void setDisarmState(bool state);
void setMasterState(bool state);
bool changedDoorState, changedDoorDestination, changedObjectState, changedObjectScale, changedObjectTrap, changedObjectLock,
changedObjectDelete, changedObjectSpawn, changedObjectPlace;
};
class Container : public BaseObject
{
public:
static void Init(LuaState &lua);
public:
Container();
std::tuple<std::string, int, int, double> getItem(int i) const;
void addItem(const std::string &refId, int count, int charge, float enchantmentCharge);
void setItem(int i, const std::string &refId, int count, int charge, float enchantmentCharge);
int getActionCount(int i) const;
size_t size() const;
bool changed;
};
class ObjectController
{
public:
static void Init(LuaState &lua);
public:
std::shared_ptr<std::vector<std::shared_ptr<Object>>> copyObjects(mwmp::BaseObjectList &objectList);
std::shared_ptr<std::vector<std::shared_ptr<Container>>> copyContainers(mwmp::BaseObjectList &objectList);
void sendObjects(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Object>>> objects,
const ESM::Cell &cell, bool broadcast = false);
void sendConsoleCommand(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Object>>> objects,
const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast = false);
void sendContainers(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Container>>> objects,
const ESM::Cell &cell, bool broadcast = false);
void requestContainers(std::shared_ptr<Player> player);
};

File diff suppressed because it is too large Load Diff

@ -8,6 +8,7 @@
#include <map>
#include <string>
#include <chrono>
#include <memory>
#include <RakNetTypes.h>
#include <components/esm/npcstats.hpp>
@ -18,32 +19,31 @@
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Base/BasePlayer.hpp>
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
#include <apps/openmw-mp/Script/LuaState.hpp>
#include "Cell.hpp"
#include "CellController.hpp"
struct Player;
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
typedef std::map<unsigned short, Player*> TSlots;
class Players
{
public:
static void newPlayer(RakNet::RakNetGUID guid);
static void deletePlayer(RakNet::RakNetGUID guid);
static Player *getPlayer(RakNet::RakNetGUID guid);
static Player *getPlayer(unsigned short id);
static TPlayers *getPlayers();
static unsigned short getLastPlayerId();
private:
static TPlayers players;
static TSlots slots;
};
class Player : public mwmp::BasePlayer
#include "CharClass.hpp"
#include "Inventory.hpp"
#include "Settings.hpp"
#include "Books.hpp"
#include "GUI.hpp"
#include "Dialogue.hpp"
#include "Factions.hpp"
#include "Cells.hpp"
#include "Quests.hpp"
#include "Spells.hpp"
#include "NetActor.hpp"
#include "CellState.hpp"
#include "Weather.hpp"
class Player : public mwmp::BasePlayer, public NetActor
{
friend class Cell;
friend class Players;
unsigned short id;
uint64_t getGUID() const {return guid.g;}
public:
static void Init(LuaState &lua);
public:
enum
@ -52,7 +52,7 @@ public:
LOADED,
POSTLOADED
};
Player(RakNet::RakNetGUID guid);
explicit Player(RakNet::RakNetGUID guid);
unsigned short getId();
void setId(unsigned short id);
@ -68,15 +68,163 @@ public:
virtual ~Player();
CellController::TContainer *getCells();
void sendToLoaded(mwmp::PlayerPacket *myPacket);
void sendToLoaded(mwmp::PlayerPacket &myPacket);
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
void update();
public:
void kick() const;
void ban() const;
void cleanChannel(unsigned channelId);
void message(unsigned channelId, const std::string &message, bool toAll = false);
bool joinChannel(unsigned channelId, const std::string &name);
void renameChannel(unsigned channelId, const std::string &name);
void closeChannel(unsigned channelId);
void leaveChannel(unsigned channelId);
void setChannel(unsigned channelId);
bool isChannelOpen(unsigned channelId);
int getAvgPing();
std::string getName();
void setName(const std::string &name);
void setCharGenStages(int currentStage, int endStage);
int getGender() const;
void setGender(int gender);
std::string getRace() const;
void setRace(const std::string &race);
std::string getHead() const;
void setHead(const std::string &head);
std::string getHair() const;
void setHair(const std::string &hair);
std::string getBirthsign() const;
void setBirthsign(const std::string &sign);
void setResetStats(bool state);
int getBounty() const;
void setBounty(int bounty);
int getReputation() const;
void setReputation(int reputation, bool toOthers);
int getLevel() const;
void setLevel(int level);
int getLevelProgress() const;
void setLevelProgress(int progress);
/**
* \brief Send a PlayerResurrect packet about a player.
*
* This sends the packet to all players connected to the server.
*
* \param type The type of resurrection (0 for REGULAR, 1 for IMPERIAL_SHRINE, 2 for TRIBUNAL_TEMPLE).
*/
void resurrect(unsigned int type);
/**
* \brief Send a PlayerJail packet about a player.
*
* This is similar to the player being jailed by a guard, but provides extra parameters for
* increased flexibility.
*
* It is only sent to the player being jailed, as the other players will be informed of the
* jailing's actual consequences via other packets sent by the affected client.
*
* \param jailDays The number of days to spend jailed, where each day affects one skill point.
* \param ignoreJailTeleportation Whether the player being teleported to the nearest jail
* marker should be overridden.
* \param ignoreJailSkillIncrease Whether the player's Sneak and Security skills should be
* prevented from increasing as a result of the jailing,
* overriding default behavior.
* \param jailProgressText The text that should be displayed while jailed.
* \param jailEndText The text that should be displayed once the jailing period is over.
*/
void jail(int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,
const std::string &jailProgressText, const std::string &jailEndText);
bool getWerewolfState() const;
void setWerewolfState(bool state);
float getScale() const;
void setScale(float newScale);
std::string getCreatureRefId() const;
void setCreatureRefId(const std::string &model);
bool getCreatureNameDisplayState() const;
void setCreatureNameDisplayState(bool useName);
std::string getIP() const;
/**
*
* @return x, y, z
*/
std::tuple<float, float, float> getPreviousCellPos() const;
/**
*
* @return base, current
*/
std::tuple<int, int> getAttribute(unsigned short id) const;
void setAttribute(unsigned short id, int base, bool clearModifier);
/**
*
* @return base, current, progress, increase
*/
std::tuple<int, int, float> getSkill(unsigned short id) const;
void setSkill(unsigned short id, int base, bool clearModifier, float progress);
int getSkillIncrease(unsigned short attributeId) const;
void setSkillIncrease(unsigned short attributeId, int increase);
void setMark(float x, float y, float z, float xRot, float zRot, const std::string &cellDescription);
std::tuple<float, float, float, float, float, std::string> getMark();
std::string getSelectedSpell();
void setSelectedSpell(const std::string &newSelectedSpellId);
size_t cellStateSize() const;
CellState getCellState(int i);
CharClass &getCharClass(sol::this_state thisState);
GameSettings &getSettings();
Books &getBooks();
GUI &getGUI();
Dialogue &getDialogue();
Factions &getFactions();
Quests &getQuests();
Spells &getSpells();
QuickKeys &getQuickKeys();
WeatherMgr &getWeatherMgr();
void setAuthority();
bool isMarkedForDeletion() const;
void addToUpdateQueue();
private:
CellController::TContainer cells;
int loadState;
int handshakeCounter;
bool /*statsChanged, attributesChanged, skillsChanged, baseInfoChanged, positionChanged,*/ changedMarkLocation, changedSelectedSpell;
CharClass cClass;
GameSettings settings;
Books books;
GUI gui;
Dialogue dialogue;
Factions factions;
Quests quests;
Spells spells;
QuickKeys quickKeys;
WeatherMgr weatherMgr;
sol::table storedData;
sol::table customData;
bool markedForDeletion;
bool inUpdateQueue;
};
#endif //OPENMW_PLAYER_HPP

@ -0,0 +1,148 @@
//
// Created by koncord on 12.08.17.
//
#include "Players.hpp"
#include "Networking.hpp"
using namespace std;
Players::Store Players::store;
std::queue<Player*> Players::updateQueue;
void Players::Init(LuaState &lua)
{
sol::table playersTable = lua.getState()->create_named_table("Players");
playersTable.set_function("getByPID", [](int pid) { return Players::getPlayerByPID(pid).get(); });
playersTable.set_function("getByGUID", [](RakNet::RakNetGUID guid) { return Players::getPlayerByGUID(guid).get(); });
playersTable.set_function("for_each", [](sol::function func)
{
for (shared_ptr<Player> player : store)
func(player);
});
playersTable.set_function("size", &Players::size);
}
std::shared_ptr<Player> Players::getPlayerByPID(int pid)
{
const auto &ls = store.get<ByID>();
auto it = ls.find(pid);
if (it != ls.end())
return *it;
return nullptr;
}
std::shared_ptr<Player> Players::getPlayerByGUID(RakNet::RakNetGUID guid)
{
const auto &ls = store.get<ByGUID>();
auto it = ls.find(guid.g);
if (it != ls.end())
{
LOG_MESSAGE_SIMPLE(Log::LOG_TRACE, "%d references: %d", guid.g, it->use_count());
return *it;
}
return nullptr;
}
void deleter(Player *pl)
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Player %lu deleted", pl->guid.g);
delete pl;
}
std::shared_ptr<Player> Players::addPlayer(RakNet::RakNetGUID guid)
{
const int maxConnections = 65535;
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Creating new player with guid %lu", guid.g);
auto player = shared_ptr<Player>(new Player(guid), deleter);
unsigned short findPid = 0;
const auto &ls = store.get<ByID>();
for (; findPid < maxConnections; ++findPid) // find empty slot
{
auto it = ls.find(findPid);
if (it == ls.end())
break;
}
if (findPid >= maxConnections)
return nullptr;
LOG_APPEND(Log::LOG_INFO, "- Storing in slot %i", findPid);
player->id = findPid;
player->guid = guid;
store.push_back(player);
return player;
}
void Players::deletePlayerByPID(int pid)
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (pid %i) for deletion", pid);
auto &ls = store.get<ByID>();
auto it = ls.find(pid);
if (it != ls.end())
{
(*it)->markedForDeletion = true;
mwmp::Networking::get().getState().getState()->collect_garbage();
size_t useCount = it->use_count();
ls.erase(it);
LOG_APPEND(Log::LOG_TRACE, "- references: %d", useCount - 1);
}
}
void Players::deletePlayerByGUID(RakNet::RakNetGUID guid)
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (guid %lu) for deletion", guid.g);
auto &ls = store.get<ByGUID>();
auto it = ls.find(guid.g);
if (it != ls.end())
{
(*it)->markedForDeletion = true;
mwmp::Networking::get().getState().getState()->collect_garbage();
size_t useCount = it->use_count();
ls.erase(it);
LOG_APPEND(Log::LOG_TRACE, "- references: %d", useCount - 1);
}
}
void Players::for_each(std::function<void (Player *)> func)
{
for (auto &player : store)
func(player.get());
}
Players::Store::const_iterator Players::begin()
{
return store.cbegin();
}
Players::Store::const_iterator Players::end()
{
return store.cend();
}
size_t Players::size()
{
return store.size();
}
void Players::processUpdated()
{
while (!updateQueue.empty())
{
Player *player = updateQueue.front();
updateQueue.pop();
player->update();
}
}
void Players::addToQueue(Player *player)
{
updateQueue.push(player);
}

@ -0,0 +1,59 @@
//
// Created by koncord on 12.08.17.
//
#pragma once
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/global_fun.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <queue>
#include "Player.hpp"
class LuaState;
class Players
{
friend class Player;
public:
static void Init(LuaState &lua);
public:
Players() = delete; // static class
protected:
struct ByID {};
struct ByGUID {};
public:
typedef boost::multi_index_container<std::shared_ptr<Player>,
boost::multi_index::indexed_by<
boost::multi_index::random_access<>,
boost::multi_index::ordered_unique<boost::multi_index::tag<ByGUID>, BOOST_MULTI_INDEX_CONST_MEM_FUN(Player, uint64_t, getGUID)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<ByID>, boost::multi_index::member<Player, unsigned short, &Player::id> >
> > Store;
static std::shared_ptr<Player> getPlayerByPID(int pid);
static std::shared_ptr<Player> getPlayerByGUID(RakNet::RakNetGUID guid);
static std::shared_ptr<Player> addPlayer(RakNet::RakNetGUID guid);
static void deletePlayerByPID(int pid);
static void deletePlayerByGUID(RakNet::RakNetGUID guid);
static Store::const_iterator begin();
static Store::const_iterator end();
static size_t size();
static void for_each(std::function<void(Player *)> func);
static void processUpdated();
private:
static void addToQueue(Player *player);
static Store store;
static std::queue<Player*> updateQueue;
};

@ -0,0 +1,154 @@
//
// Created by koncord on 25.08.17.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Script/LuaState.hpp"
#include "Networking.hpp"
#include "Quests.hpp"
#include "Player.hpp"
void JournalItem::Init(LuaState &lua)
{
lua.getState()->new_usertype<JournalItem>("JournalItem",
"quest", sol::property(&JournalItem::getQuest, &JournalItem::setQuest),
"index", sol::property(&JournalItem::getIndex, &JournalItem::setIndex),
"actorRefId", sol::property(&JournalItem::getActorRefId, &JournalItem::setActorRefId),
"type", sol::property(&JournalItem::getType, &JournalItem::setType)
);
}
JournalItem::JournalItem(mwmp::JournalItem item) : item(item)
{
}
JournalItem::~JournalItem()
{
}
std::string JournalItem::getQuest() const
{
return item.quest;
}
void JournalItem::setQuest(const std::string &quest)
{
item.quest = quest;
}
int JournalItem::getIndex() const
{
return item.index;
}
void JournalItem::setIndex(int index)
{
item.index = index;
}
mwmp::JournalItem::Type JournalItem::getType() const
{
return item.type;
}
void JournalItem::setType(mwmp::JournalItem::Type type)
{
item.type = type;
}
std::string JournalItem::getActorRefId() const
{
return item.actorRefId;
}
void JournalItem::setActorRefId(const std::string &refid)
{
item.actorRefId = refid;
}
void Quests::Init(LuaState &lua)
{
lua.getState()->new_usertype<Quests>("Quests",
"getJournalChangesSize", &Quests::getJournalChangesSize,
"getKillChangesSize", &Quests::getKillChangesSize,
"addJournalItem", &Quests::addJournalItem,
"setJournalItem", &Quests::setJournalItem,
"getJournalItem", &Quests::getJournalItem,
"addKill", &Quests::addKill,
"getKill", &Quests::getKill
);
}
Quests::Quests(Player *player) : BaseMgr(player), changedKills(false), changedJournal(false)
{
}
void Quests::processUpdate()
{
if (changedJournal)
{
changedJournal = false;
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JOURNAL);
packet->setPlayer(player);
packet->Send(/*toOthers*/ false);
}
if (changedKills)
{
changedKills = false;
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_KILL_COUNT);
packet->setPlayer(player);
packet->Send(/*toOthers*/ false);
}
}
size_t Quests::getJournalChangesSize() const
{
return player->journalChanges.journalItems.size();
}
size_t Quests::getKillChangesSize() const
{
return player->killChanges.kills.size();
}
void Quests::addJournalItem(JournalItem item)
{
player->journalChanges.journalItems.push_back(item.item);
changedJournal = true;
setChanged();
}
void Quests::setJournalItem(unsigned int id, JournalItem item)
{
player->journalChanges.journalItems.at(id) = item.item;
changedJournal = true;
setChanged();
}
JournalItem Quests::getJournalItem(unsigned int id)
{
return JournalItem(player->journalChanges.journalItems.at(id));
}
void Quests::addKill(const std::string &refId, int number)
{
player->killChanges.kills.push_back({refId, number});
changedKills = true;
setChanged();
}
std::tuple<std::string, int> Quests::getKill(unsigned int i) const
{
auto & kill = player->killChanges.kills.at(i);
return std::make_tuple(kill.refId, kill.number);
}

@ -0,0 +1,61 @@
//
// Created by koncord on 25.08.17.
//
#pragma once
#include <cstddef>
#include <components/openmw-mp/Base/BasePlayer.hpp>
#include "BaseMgr.hpp"
class LuaState;
class Player;
class JournalItem
{
public:
static void Init(LuaState &lua);
public:
explicit JournalItem(mwmp::JournalItem item);
~JournalItem();
std::string getQuest() const;
void setQuest(const std::string &quest);
int getIndex() const;
void setIndex(int index);
mwmp::JournalItem::Type getType() const;
void setType(mwmp::JournalItem::Type type);
std::string getActorRefId() const;
void setActorRefId(const std::string &refid);
mwmp::JournalItem item;
};
class Quests final: public BaseMgr
{
public:
static void Init(LuaState &lua);
public:
explicit Quests(Player *player);
size_t getJournalChangesSize() const;
size_t getKillChangesSize() const;
void addJournalItem(JournalItem item);
void setJournalItem(unsigned int id, JournalItem item);
JournalItem getJournalItem(unsigned int id);
void addKill(const std::string &refId, int number);
std::tuple<std::string, int> getKill(unsigned int i) const;
private:
void processUpdate() final;
bool changedKills, changedJournal;
};

@ -1,93 +0,0 @@
//
// Created by koncord on 14.05.16.
//
#include <Script/ScriptFunction.hpp>
#include "PublicFnAPI.hpp"
using namespace std;
unordered_map<string, Public *> Public::publics;
Public::~Public()
{
}
Public::Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(_public, ret_type, def)
{
publics.emplace(name, this);
}
Public::Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(
_public, lua, ret_type, def)
{
publics.emplace(name, this);
}
#if defined(ENABLE_PAWN)
Public::Public(ScriptFuncPAWN _public, AMX* amx, const std::string& name, char ret_type, const std::string& def): ScriptFunction(_public, amx, ret_type, def)
{
publics.emplace(name, this);
}
#endif
boost::any Public::Call(const std::string &name, const std::vector<boost::any> &args)
{
auto it = publics.find(name);
if (it == publics.end())
throw runtime_error("Public with name \"" + name + "\" does not exist");
return it->second->ScriptFunction::Call(args);
}
const std::string &Public::GetDefinition(const std::string &name)
{
auto it = publics.find(name);
if (it == publics.end())
throw runtime_error("Public with name \"" + name + "\" does not exist");
return it->second->def;
}
bool Public::IsLua(const std::string &name)
{
#if !defined(ENABLE_LUA)
return false;
#else
auto it = publics.find(name);
if (it == publics.end())
throw runtime_error("Public with name \"" + name + "\" does not exist");
return it->second->script_type == SCRIPT_LUA;
#endif
}
bool Public::IsPAWN(const std::string &name)
{
#if !defined(ENABLE_PAWN)
return false;
#else
auto it = publics.find(name);
if (it == publics.end())
throw runtime_error("Public with name \"" + name + "\" does not exist");
return it->second->script_type == SCRIPT_PAWN;
#endif
}
void Public::DeleteAll()
{
for (auto it = publics.begin(); it != publics.end(); it++)
{
Public *_public = it->second;
delete _public;
publics.erase(it);
}
}

@ -1,42 +0,0 @@
//
// Created by koncord on 14.05.16.
//
#ifndef PLUGINSYSTEM3_PUBLICFNAPI_HPP
#define PLUGINSYSTEM3_PUBLICFNAPI_HPP
#include <unordered_map>
#include <Script/ScriptFunction.hpp>
class Public : public ScriptFunction
{
private:
~Public();
static std::unordered_map<std::string, Public *> publics;
Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def);
#if defined(ENABLE_PAWN)
Public(ScriptFuncPAWN _public, AMX* amx, const std::string& name, char ret_type, const std::string& def);
#endif
#if defined(ENABLE_LUA)
Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def);
#endif
public:
template<typename... Args>
static void MakePublic(Args &&... args)
{ new Public(std::forward<Args>(args)...); }
static boost::any Call(const std::string &name, const std::vector<boost::any> &args);
static const std::string& GetDefinition(const std::string& name);
static bool IsPAWN(const std::string &name);
static bool IsLua(const std::string &name);
static void DeleteAll();
};
#endif //PLUGINSYSTEM3_PUBLICFNAPI_HPP

@ -1,238 +0,0 @@
//
// Created by koncord on 15.03.16.
//
#include "TimerAPI.hpp"
#include <chrono>
#include <iostream>
using namespace mwmp;
using namespace std;
Timer::Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args) : ScriptFunction(callback, 'v', def)
{
targetMsec = msec;
this->args = args;
end = true;
}
#if defined(ENABLE_PAWN)
Timer::Timer(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string &def, std::vector<boost::any> args): ScriptFunction(callback, amx, 'v', def)
{
targetMsec = msec;
this->args = args;
end = true;
}
#endif
#if defined(ENABLE_LUA)
Timer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args): ScriptFunction(callback, lua, 'v', def)
{
targetMsec = msec;
this->args = args;
end = true;
}
#endif
void Timer::Tick()
{
if (end)
return;
const auto duration = chrono::system_clock::now().time_since_epoch();
const auto time = chrono::duration_cast<chrono::milliseconds>(duration).count();
if (time - startTime >= targetMsec)
{
end = true;
Call(args);
}
}
bool Timer::IsEnd()
{
return end;
}
void Timer::Stop()
{
end = true;
}
void Timer::Restart(int msec)
{
targetMsec = msec;
Start();
}
void Timer::Start()
{
end = false;
const auto duration = chrono::system_clock::now().time_since_epoch();
const auto msec = chrono::duration_cast<chrono::milliseconds>(duration).count();
startTime = msec;
}
int TimerAPI::pointer = 0;
std::unordered_map<int, Timer* > TimerAPI::timers;
#if defined(ENABLE_PAWN)
int TimerAPI::CreateTimerPAWN(AMX *amx, ScriptFuncPAWN callback, long msec, const string& def, std::vector<boost::any> args)
{
int id = -1;
for (auto timer : timers)
{
if (timer.second != nullptr)
continue;
timer.second = new Timer(amx, callback, msec, def, args);
id = timer.first;
}
if (id == -1)
{
timers[pointer] = new Timer(amx, callback, msec, def, args);
id = pointer;
pointer++;
}
return id;
}
#endif
#if defined(ENABLE_LUA)
int TimerAPI::CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args)
{
int id = -1;
for (auto timer : timers)
{
if (timer.second != nullptr)
continue;
timer.second = new Timer(lua, callback, msec, def, args);
id = timer.first;
}
if (id == -1)
{
timers[pointer] = new Timer(lua, callback, msec, def, args);
id = pointer;
pointer++;
}
return id;
}
#endif
int TimerAPI::CreateTimer(ScriptFunc callback, long msec, const std::string &def, std::vector<boost::any> args)
{
int id = -1;
for (auto timer : timers)
{
if (timer.second != nullptr)
continue;
timer.second = new Timer(callback, msec, def, args);
id = timer.first;
}
if (id == -1)
{
timers[pointer] = new Timer(callback, msec, def, args);
id = pointer;
pointer++;
}
return id;
}
void TimerAPI::FreeTimer(int timerid)
{
try
{
if (timers.at(timerid) != nullptr)
{
delete timers[timerid];
timers[timerid] = nullptr;
}
}
catch(...)
{
std::cerr << "Timer " << timerid << " not found!" << endl;
}
}
void TimerAPI::ResetTimer(int timerid, long msec)
{
try
{
timers.at(timerid)->Restart(msec);
}
catch(...)
{
std::cerr << "Timer " << timerid << " not found!" << endl;
}
}
void TimerAPI::StartTimer(int timerid)
{
try
{
Timer *timer = timers.at(timerid);
if (timer == nullptr)
throw 1;
timer->Start();
}
catch(...)
{
std::cerr << "Timer " << timerid << " not found!" << endl;
}
}
void TimerAPI::StopTimer(int timerid)
{
try
{
timers.at(timerid)->Stop();
}
catch(...)
{
std::cerr << "Timer " << timerid << " not found!" << endl;
}
}
bool TimerAPI::IsEndTimer(int timerid)
{
bool ret = false;
try
{
ret = timers.at(timerid)->IsEnd();
}
catch(...)
{
std::cerr << "Timer " << timerid << " not found!" << endl;
}
return ret;
}
void TimerAPI::Terminate()
{
for (auto timer : timers)
{
if (timer.second != nullptr)
delete timer.second;
timer.second = nullptr;
}
}
void TimerAPI::Tick()
{
for (auto timer : timers)
{
if (timer.second != nullptr)
timer.second->Tick();
}
}

@ -1,70 +0,0 @@
//
// Created by koncord on 15.03.16.
//
#ifndef OPENMW_TIMERAPI_HPP
#define OPENMW_TIMERAPI_HPP
#include <string>
#include <Script/Script.hpp>
#include <Script/ScriptFunction.hpp>
namespace mwmp
{
class TimerAPI;
class Timer: public ScriptFunction
{
friend class TimerAPI;
public:
Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);
#if defined(ENABLE_PAWN)
Timer(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string& def, std::vector<boost::any> args);
#endif
#if defined(ENABLE_LUA)
Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);
#endif
void Tick();
bool IsEnd();
void Stop();
void Start();
void Restart(int msec);
private:
double startTime, targetMsec;
std::string publ, arg_types;
std::vector<boost::any> args;
Script *scr;
bool end;
};
class TimerAPI
{
public:
#if defined(ENABLE_PAWN)
static int CreateTimerPAWN(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string& def, std::vector<boost::any> args);
#endif
#if defined(ENABLE_LUA)
static int CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);
#endif
static int CreateTimer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);
static void FreeTimer(int timerid);
static void ResetTimer(int timerid, long msec);
static void StartTimer(int timerid);
static void StopTimer(int timerid);
static bool IsEndTimer(int timerid);
static void Terminate();
static void Tick();
private:
static std::unordered_map<int, Timer* > timers;
static int pointer;
};
}
#endif //OPENMW_TIMERAPI_HPP

@ -0,0 +1,103 @@
//
// Created by koncord on 08.08.17.
//
#include "CommandController.hpp"
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <components/misc/stringops.hpp>
#include "Player.hpp"
#include "LuaState.hpp"
using namespace std;
void CommandController::Init(LuaState &lua)
{
sol::table cmdCtrlTable = lua.getState()->create_named_table("CommandController");
cmdCtrlTable["registerCommand"] = [&lua](const std::string &command, sol::function func, const std::string &helpMessage) {
return lua.getCmdCtrl().registerCommand(Misc::StringUtils::lowerCase(command), func, helpMessage);
};
cmdCtrlTable["unregisterCommand"] = [&lua](const std::string &command) {
lua.getCmdCtrl().unregisterCommand(Misc::StringUtils::lowerCase(command));
};
cmdCtrlTable["hasCommand"] = [&lua](const std::string &command) {
return lua.getCmdCtrl().hasCommand(Misc::StringUtils::lowerCase(command));
};
cmdCtrlTable["getHelpStrings"] = [&lua]() {
auto &commands = lua.getCmdCtrl().commands;
sol::table helpTable = lua.getState()->create_table(commands.size(), 0);
for (const auto &cmd : commands)
helpTable.add(cmd.first, cmd.second.helpMessage);
return helpTable;
};
}
bool CommandController::registerCommand(const std::string &command, sol::function func, const std::string &helpMessage)
{
auto iter = commands.find(command);
if (iter == commands.end())
{
commands.emplace(command, Command {std::move(func), helpMessage});
return true;
}
return false;
}
void CommandController::unregisterCommand(const std::string &command)
{
commands.erase(command);
}
bool CommandController::hasCommand(const std::string &command)
{
return commands.find(command) != commands.end();
}
std::pair<CommandController::ExecResult, std::string> CommandController::exec(const std::shared_ptr<Player> &player,
const std::string &message, unsigned channel)
{
char cmdChar = message[0];
if (message.size() < 2 || (cmdChar != '/' && cmdChar != '!'))
return make_pair(ExecResult::NOT_CMD, "");
auto tokens = cmdParser(message);
auto cmd = commands.find(Misc::StringUtils::lowerCase(&tokens[0][1]));
if (cmd != commands.end())
{
tokens.pop_front();
bool result = cmd->second.func(player, sol::as_table(move(tokens)), channel);
if (result)
return make_pair(ExecResult::SUCCESS, "");
return make_pair(ExecResult::FAIL, cmd->second.helpMessage);
}
return make_pair(ExecResult::NOT_FOUND, "");
}
std::deque<std::string> CommandController::cmdParser(const std::string &message)
{
deque<string> ret;
namespace x3 = boost::spirit::x3;
auto const sep = ' ';
auto const quoted = '"' >> *~x3::char_('"') >> '"';
auto const unquoted = *~x3::char_(sep);
auto const arguments = (quoted | unquoted) % sep;
/*auto const command = '/' >> *~x3::char_(sep) >> sep;
if (!x3::phrase_parse(message.cbegin(), message.cend(), arguments, command, ret))
Utils::throwError("failed to parse message: "+ message);*/
if (!x3::parse(message.cbegin(), message.cend(), arguments, ret))
Utils::throwError("failed to parse message: " + message);
return ret;
}

@ -0,0 +1,63 @@
//
// Created by koncord on 08.08.17.
//
#pragma once
#include "sol.hpp"
#include "Utils.hpp"
#include <string>
#include <deque>
class Player;
class LuaState;
struct Command
{
sol::function func;
std::string helpMessage;
};
class CommandController
{
typedef std::unordered_map<std::string, Command> Container;
typedef Container::iterator Iter;
typedef Container::const_iterator CIter;
public:
static void Init(LuaState &lua);
public:
enum class ExecResult : int
{
NOT_FOUND,
SUCCESS,
FAIL,
NOT_CMD
};
/**
* Register new command. Only unique commands are allowed.
* @param command name of command. Case sensitive. No need in command prefix ('/' or '!').
* @param helpMessage help message. Shows in the '/help' command also appears if exec() fails.
* @param callback Will be called when command is called.
* @return false if the command already registered.
*/
bool registerCommand(const std::string &command, sol::function callback, const std::string &helpMessage);
/**
* Removes a registered command
* @param command name of command.
*/
void unregisterCommand(const std::string &command);
/**
* Check a command is exist.
* @param command name of command
* @return false if the command did not exist.
*/
bool hasCommand(const std::string &command);
std::pair<ExecResult, std::string> exec(const std::shared_ptr<Player> &player, const std::string &message, unsigned channel);
private:
std::deque<std::string> cmdParser(const std::string &message);
Container commands;
};

@ -0,0 +1,158 @@
//
// Created by koncord on 30.07.17.
//
#include <iostream>
#include "EventController.hpp"
using namespace std;
#define ADD_CORE_EVENT(event) #event, CoreEvent::event
void EventController::Init(LuaState &lua)
{
sol::table eventsTable = lua.getState()->create_named_table("Event");
eventsTable["register"] = [&lua](int event, sol::function func, sol::this_environment te) {
sol::environment& env = te;
lua.getEventCtrl().registerEvent(event, env, func, false);
};
eventsTable["stop"] = [&lua](int event) {
lua.getEventCtrl().stop(event);
};
eventsTable["create"] = [&lua]() {
return lua.getEventCtrl().createEvent();
};
eventsTable["raise"] = [&lua](unsigned event, sol::table data) {
lua.getEventCtrl().raiseEvent(event, data);
};
eventsTable["raiseSpecified"] = [&lua](unsigned event, const std::string &moduleName, sol::table data) {
lua.getEventCtrl().raiseEvent(event, data, moduleName);
};
}
EventController::EventController(LuaState *luaCtrl)
{
this->luaCtrl = luaCtrl;
#ifdef SERVER_DEBUG
luaCtrl->getState()->new_enum<false>
#else
luaCtrl->getState()->new_enum
#endif
("Events",
ADD_CORE_EVENT(ON_POST_INIT),
ADD_CORE_EVENT(ON_EXIT),
ADD_CORE_EVENT(ON_PLAYER_CONNECT),
ADD_CORE_EVENT(ON_PLAYER_DISCONNECT),
ADD_CORE_EVENT(ON_PLAYER_DEATH),
ADD_CORE_EVENT(ON_PLAYER_RESURRECT),
ADD_CORE_EVENT(ON_PLAYER_CELLCHANGE),
ADD_CORE_EVENT(ON_PLAYER_KILLCOUNT),
ADD_CORE_EVENT(ON_PLAYER_ATTRIBUTE),
ADD_CORE_EVENT(ON_PLAYER_SKILL),
ADD_CORE_EVENT(ON_PLAYER_LEVEL),
ADD_CORE_EVENT(ON_PLAYER_BOUNTY),
ADD_CORE_EVENT(ON_PLAYER_REPUTATION),
ADD_CORE_EVENT(ON_PLAYER_EQUIPMENT),
ADD_CORE_EVENT(ON_PLAYER_INVENTORY),
ADD_CORE_EVENT(ON_PLAYER_JOURNAL),
ADD_CORE_EVENT(ON_PLAYER_FACTION),
ADD_CORE_EVENT(ON_PLAYER_SHAPESHIFT),
ADD_CORE_EVENT(ON_PLAYER_SPELLBOOK),
ADD_CORE_EVENT(ON_PLAYER_QUICKKEYS),
ADD_CORE_EVENT(ON_PLAYER_TOPIC),
ADD_CORE_EVENT(ON_PLAYER_DISPOSITION),
ADD_CORE_EVENT(ON_PLAYER_BOOK),
ADD_CORE_EVENT(ON_PLAYER_MISCELLANEOUS),
ADD_CORE_EVENT(ON_PLAYER_INTERACTION),
ADD_CORE_EVENT(ON_PLAYER_REST),
ADD_CORE_EVENT(ON_PLAYER_SENDMESSAGE),
ADD_CORE_EVENT(ON_PLAYER_ENDCHARGEN),
ADD_CORE_EVENT(ON_PLAYER_WEATHER),
ADD_CORE_EVENT(ON_RECORD_DYNAMIC),
ADD_CORE_EVENT(ON_CHANNEL_ACTION),
ADD_CORE_EVENT(ON_REQUEST_PLUGIN_LIST),
ADD_CORE_EVENT(ON_MP_REFNUM),
ADD_CORE_EVENT(ON_ACTOR_EQUIPMENT),
ADD_CORE_EVENT(ON_ACTOR_CELL_CHANGE),
ADD_CORE_EVENT(ON_ACTOR_LIST),
ADD_CORE_EVENT(ON_ACTOR_TEST),
ADD_CORE_EVENT(ON_CELL_LOAD),
ADD_CORE_EVENT(ON_CELL_UNLOAD),
ADD_CORE_EVENT(ON_CELL_DELETION),
ADD_CORE_EVENT(ON_CONTAINER),
ADD_CORE_EVENT(ON_DOOR_STATE),
ADD_CORE_EVENT(ON_OBJECT_PLACE),
ADD_CORE_EVENT(ON_OBJECT_STATE),
ADD_CORE_EVENT(ON_OBJECT_SPAWN),
ADD_CORE_EVENT(ON_OBJECT_DELETE),
ADD_CORE_EVENT(ON_OBJECT_LOCK),
ADD_CORE_EVENT(ON_OBJECT_SCALE),
ADD_CORE_EVENT(ON_OBJECT_TRAP),
ADD_CORE_EVENT(ON_WORLD_MAP)
);
sol::state &state = *luaCtrl->getState();
sol::table eventsEnum = state["Events"];
for (int i = CoreEvent::FIRST; i < CoreEvent::LAST; ++i)
{
#ifdef SERVER_DEBUG
bool found = false;
eventsEnum.for_each([&found, &i](sol::object key, sol::object value){
if (value.as<int>() == i)
{
found = true;
return;
}
});
if (!found)
Utils::throwError("Event " + to_string(i) + " is not registered");
#endif
events[i]; // create core event
}
}
void EventController::registerEvent(int event, sol::environment &env, sol::function& func, bool needsOldState)
{
auto iter = events.find(event);
if (iter != events.end())
iter->second.push(env, func, needsOldState);
}
void EventController::stop(int event)
{
printf("EventController::stop\n");
auto iter = events.find(event);
if (iter != events.end())
iter->second.stop();
}
CallbackCollection &EventController::GetEvents(Event event)
{
return events.at(event);
}
Event EventController::createEvent()
{
events[lastEvent];
return lastEvent++;
}
void EventController::raiseEvent(Event id, sol::table data, const string &moduleName)
{
auto iter = events.find(id);
if (iter != events.end())
{
if (!moduleName.empty())
{
auto f = std::find_if(iter->second.begin(), iter->second.end(), [&moduleName](const auto &item){
return item.env["ModuleName"]["name"] == moduleName;
});
if (f != iter->second.end())
f->fn.call(data); // call only specified mod
}
iter->second.call(CallbackCollection::type_tag<void>(), false, data); // call all registered events with this id
}
else
cerr << "Event with id: " << id << " is not registered" << endl;
}

@ -0,0 +1,204 @@
//
// Created by koncord on 30.07.17.
//
#pragma once
#include "sol.hpp"
#include "Utils.hpp"
#include "LuaState.hpp"
typedef unsigned Event;
namespace CoreEvent
{
enum
{
ON_EXIT = 0,
ON_POST_INIT,
ON_REQUEST_PLUGIN_LIST,
ON_PLAYER_CONNECT,
ON_PLAYER_DISCONNECT,
ON_PLAYER_DEATH,
ON_PLAYER_RESURRECT,
ON_PLAYER_CELLCHANGE,
ON_PLAYER_KILLCOUNT,
ON_PLAYER_ATTRIBUTE,
ON_PLAYER_SKILL,
ON_PLAYER_LEVEL,
ON_PLAYER_BOUNTY,
ON_PLAYER_REPUTATION,
ON_PLAYER_EQUIPMENT,
ON_PLAYER_INVENTORY,
ON_PLAYER_JOURNAL,
ON_PLAYER_FACTION,
ON_PLAYER_SHAPESHIFT,
ON_PLAYER_SPELLBOOK,
ON_PLAYER_QUICKKEYS,
ON_PLAYER_TOPIC,
ON_PLAYER_DISPOSITION,
ON_PLAYER_BOOK,
ON_PLAYER_MISCELLANEOUS,
ON_PLAYER_INTERACTION,
ON_PLAYER_REST,
ON_PLAYER_SENDMESSAGE,
ON_PLAYER_ENDCHARGEN,
ON_PLAYER_WEATHER,
ON_RECORD_DYNAMIC,
ON_CHANNEL_ACTION,
ON_MP_REFNUM,
ON_ACTOR_EQUIPMENT,
ON_ACTOR_CELL_CHANGE,
ON_ACTOR_LIST,
ON_ACTOR_TEST,
ON_CELL_LOAD,
ON_CELL_UNLOAD,
ON_CELL_DELETION,
ON_CONTAINER,
ON_DOOR_STATE,
ON_OBJECT_PLACE,
ON_OBJECT_STATE,
ON_OBJECT_SPAWN,
ON_OBJECT_DELETE,
ON_OBJECT_LOCK,
ON_OBJECT_SCALE,
ON_OBJECT_TRAP,
ON_WORLD_MAP,
LAST,
};
const int FIRST = ON_EXIT;
}
class CallbackCollection // todo: add sort by dependencies
{
public:
struct CBType
{
sol::environment env;
sol::function fn;
bool needsOldState;
CBType(sol::environment _env, sol::function _fn, bool _needsOldState): env(_env), fn(_fn), needsOldState(_needsOldState) {}
};
typedef std::vector<CBType> Container;
typedef Container::iterator Iterator;
typedef Container::const_iterator CIterator;
template<typename> struct type_tag {};
void push(sol::environment &env, sol::function &func, bool needsOldState) { functions.emplace_back(env, func, needsOldState); }
CIterator begin() const { return functions.begin(); }
CIterator end() const { return functions.end(); }
void stop() {_stop = true;}
bool isStoped() const {return _stop;}
CIterator stopedAt() const { return lastCalled; }
template<typename... Args, typename OldData>
void callWOld(const OldData &oldData, Args&&... args)
{
lastCalled = functions.end();
_stop = false;
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
{
if (!_stop)
{
if (!iter->needsOldState)
{
iter->fn.call(std::forward<Args>(args)...);
continue;
}
iter->fn.call(std::forward<Args>(args)..., oldData);
}
else
{
lastCalled = iter;
break;
}
}
}
template<typename... Args>
void call(type_tag<void>, Args&&... args)
{
lastCalled = functions.end();
_stop = false;
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
{
if (!_stop)
iter->fn.call(std::forward<Args>(args)...);
else
{
lastCalled = iter;
break;
}
}
}
template<typename R, typename... Args>
decltype(auto) call(type_tag<R>, Args&&... args)
{
R ret;
lastCalled = functions.end();
_stop = false;
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
{
if (!_stop)
ret = iter->fn.call(std::forward<Args>(args)...);
else
{
lastCalled = iter;
break;
}
}
return ret;
}
private:
Container functions;
CIterator lastCalled;
bool _stop = false;
};
class EventController
{
public:
static void Init(LuaState &lua);
public:
explicit EventController(LuaState *luaCtrl);
typedef std::unordered_map<int, CallbackCollection> Container;
void registerEvent(int event, sol::environment &env, sol::function& func, bool needsOldState);
CallbackCollection& GetEvents(Event event);
Event createEvent();
void raiseEvent(Event id, sol::table data, const std::string &moduleName = "");
void stop(int event);
template<Event event, bool canProvideOldState = false, typename R = void, typename... Args>
R Call(Args&&... args)
{
if (canProvideOldState)
events.at(event).callWOld(std::forward<Args>(args)...);
else
return events.at(event).call(CallbackCollection::type_tag<R>{}, std::forward<Args>(args)...);
}
template<Event event, bool canProvideOldState = false, typename R = void>
R Call()
{
return events.at(event).call(CallbackCollection::type_tag<R>{});
}
private:
Container events;
Event lastEvent = CoreEvent::LAST;
LuaState *luaCtrl;
};

@ -1,367 +0,0 @@
#include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Base/BaseActor.hpp>
#include <apps/openmw-mp/Networking.hpp>
#include <apps/openmw-mp/Player.hpp>
#include <apps/openmw-mp/Utils.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <components/esm/creaturestats.hpp>
#include "Actors.hpp"
using namespace mwmp;
BaseActorList *readActorList;
BaseActorList writeActorList;
BaseActor tempActor;
const BaseActor emptyActor = {};
static std::string tempCellDescription;
void ActorFunctions::ReadLastActorList() noexcept
{
readActorList = mwmp::Networking::getPtr()->getLastActorList();
}
void ActorFunctions::ReadCellActorList(const char* cellDescription) noexcept
{
ESM::Cell esmCell = Utils::getCellFromDescription(cellDescription);
Cell *serverCell = CellController::get()->getCell(&esmCell);
readActorList = serverCell->getActorList();
}
void ActorFunctions::InitializeActorList(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
writeActorList.cell.blank();
writeActorList.baseActors.clear();
writeActorList.guid = player->guid;
}
unsigned int ActorFunctions::GetActorListSize() noexcept
{
return readActorList->count;
}
unsigned char ActorFunctions::GetActorListAction() noexcept
{
return readActorList->action;
}
const char *ActorFunctions::GetActorCell(unsigned int i) noexcept
{
tempCellDescription = readActorList->baseActors.at(i).cell.getDescription();
return tempCellDescription.c_str();
}
const char *ActorFunctions::GetActorRefId(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).refId.c_str();
}
int ActorFunctions::GetActorRefNumIndex(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).refNumIndex;
}
int ActorFunctions::GetActorMpNum(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).mpNum;
}
double ActorFunctions::GetActorPosX(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.pos[0];
}
double ActorFunctions::GetActorPosY(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.pos[1];
}
double ActorFunctions::GetActorPosZ(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.pos[2];
}
double ActorFunctions::GetActorRotX(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.rot[0];
}
double ActorFunctions::GetActorRotY(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.rot[1];
}
double ActorFunctions::GetActorRotZ(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).position.rot[2];
}
double ActorFunctions::GetActorHealthBase(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mBase;
}
double ActorFunctions::GetActorHealthCurrent(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mCurrent;
}
double ActorFunctions::GetActorHealthModified(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mMod;
}
double ActorFunctions::GetActorMagickaBase(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mBase;
}
double ActorFunctions::GetActorMagickaCurrent(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mCurrent;
}
double ActorFunctions::GetActorMagickaModified(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mMod;
}
double ActorFunctions::GetActorFatigueBase(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mBase;
}
double ActorFunctions::GetActorFatigueCurrent(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mCurrent;
}
double ActorFunctions::GetActorFatigueModified(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mMod;
}
const char *ActorFunctions::GetActorEquipmentItemRefId(unsigned int i, unsigned short slot) noexcept
{
return readActorList->baseActors.at(i).equipmentItems[slot].refId.c_str();
}
int ActorFunctions::GetActorEquipmentItemCount(unsigned int i, unsigned short slot) noexcept
{
return readActorList->baseActors.at(i).equipmentItems[slot].count;
}
int ActorFunctions::GetActorEquipmentItemCharge(unsigned int i, unsigned short slot) noexcept
{
return readActorList->baseActors.at(i).equipmentItems[slot].charge;
}
double ActorFunctions::GetActorEquipmentItemEnchantmentCharge(unsigned int i, unsigned short slot) noexcept
{
return readActorList->baseActors.at(i).equipmentItems[slot].enchantmentCharge;
}
bool ActorFunctions::DoesActorHavePosition(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).hasPositionData;
}
bool ActorFunctions::DoesActorHaveStatsDynamic(unsigned int i) noexcept
{
return readActorList->baseActors.at(i).hasStatsDynamicData;
}
void ActorFunctions::SetActorListCell(const char* cellDescription) noexcept
{
writeActorList.cell = Utils::getCellFromDescription(cellDescription);
}
void ActorFunctions::SetActorListAction(unsigned char action) noexcept
{
writeActorList.action = action;
}
void ActorFunctions::SetActorCell(const char* cellDescription) noexcept
{
tempActor.cell = Utils::getCellFromDescription(cellDescription);
}
void ActorFunctions::SetActorRefId(const char* refId) noexcept
{
tempActor.refId = refId;
}
void ActorFunctions::SetActorRefNumIndex(int refNumIndex) noexcept
{
tempActor.refNumIndex = refNumIndex;
}
void ActorFunctions::SetActorMpNum(int mpNum) noexcept
{
tempActor.mpNum = mpNum;
}
void ActorFunctions::SetActorPosition(double x, double y, double z) noexcept
{
tempActor.position.pos[0] = x;
tempActor.position.pos[1] = y;
tempActor.position.pos[2] = z;
}
void ActorFunctions::SetActorRotation(double x, double y, double z) noexcept
{
tempActor.position.rot[0] = x;
tempActor.position.rot[1] = y;
tempActor.position.rot[2] = z;
}
void ActorFunctions::SetActorHealthBase(double value) noexcept
{
tempActor.creatureStats.mDynamic[0].mBase = value;
}
void ActorFunctions::SetActorHealthCurrent(double value) noexcept
{
tempActor.creatureStats.mDynamic[0].mCurrent = value;
}
void ActorFunctions::SetActorHealthModified(double value) noexcept
{
tempActor.creatureStats.mDynamic[0].mMod = value;
}
void ActorFunctions::SetActorMagickaBase(double value) noexcept
{
tempActor.creatureStats.mDynamic[1].mBase = value;
}
void ActorFunctions::SetActorMagickaCurrent(double value) noexcept
{
tempActor.creatureStats.mDynamic[1].mCurrent = value;
}
void ActorFunctions::SetActorMagickaModified(double value) noexcept
{
tempActor.creatureStats.mDynamic[1].mMod = value;
}
void ActorFunctions::SetActorFatigueBase(double value) noexcept
{
tempActor.creatureStats.mDynamic[2].mBase = value;
}
void ActorFunctions::SetActorFatigueCurrent(double value) noexcept
{
tempActor.creatureStats.mDynamic[2].mCurrent = value;
}
void ActorFunctions::SetActorFatigueModified(double value) noexcept
{
tempActor.creatureStats.mDynamic[2].mMod = value;
}
void ActorFunctions::SetActorAIAction(unsigned int action) noexcept
{
tempActor.aiAction = action;
}
void ActorFunctions::SetActorAITargetToPlayer(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
tempActor.hasAiTarget = true;
tempActor.aiTarget.isPlayer = true;
tempActor.aiTarget.guid = player->guid;
}
void ActorFunctions::SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept
{
tempActor.hasAiTarget = true;
tempActor.aiTarget.isPlayer = false;
tempActor.aiTarget.refNumIndex = refNumIndex;
tempActor.aiTarget.mpNum = mpNum;
}
void ActorFunctions::EquipActorItem(unsigned short slot, const char *refId, unsigned int count, int charge, double enchantmentCharge) noexcept
{
tempActor.equipmentItems[slot].refId = refId;
tempActor.equipmentItems[slot].count = count;
tempActor.equipmentItems[slot].charge = charge;
tempActor.equipmentItems[slot].enchantmentCharge = enchantmentCharge;
}
void ActorFunctions::UnequipActorItem(unsigned short slot) noexcept
{
ActorFunctions::EquipActorItem(slot, "", 0, -1, -1);
}
void ActorFunctions::AddActor() noexcept
{
writeActorList.baseActors.push_back(tempActor);
tempActor = emptyActor;
}
void ActorFunctions::SendActorList() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST)->Send(writeActorList.guid);
}
void ActorFunctions::SendActorAuthority() noexcept
{
Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);
if (serverCell != nullptr)
{
serverCell->setAuthority(writeActorList.guid);
mwmp::ActorPacket *authorityPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AUTHORITY);
authorityPacket->setActorList(&writeActorList);
authorityPacket->Send(writeActorList.guid);
// Also send this to everyone else who has the cell loaded
serverCell->sendToLoaded(authorityPacket, &writeActorList);
}
}
void ActorFunctions::SendActorPosition() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_POSITION)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_POSITION)->Send(writeActorList.guid);
}
void ActorFunctions::SendActorStatsDynamic() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_STATS_DYNAMIC)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_STATS_DYNAMIC)->Send(writeActorList.guid);
}
void ActorFunctions::SendActorEquipment() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT)->Send(writeActorList.guid);
}
void ActorFunctions::SendActorAI() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->Send(writeActorList.guid);
}
void ActorFunctions::SendActorCellChange() noexcept
{
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE)->setActorList(&writeActorList);
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE)->Send(writeActorList.guid);
}

@ -1,614 +0,0 @@
#ifndef OPENMW_ACTORAPI_HPP
#define OPENMW_ACTORAPI_HPP
#define ACTORAPI \
{"ReadLastActorList", ActorFunctions::ReadLastActorList},\
{"ReadCellActorList", ActorFunctions::ReadCellActorList},\
{"InitializeActorList", ActorFunctions::InitializeActorList},\
\
{"GetActorListSize", ActorFunctions::GetActorListSize},\
{"GetActorListAction", ActorFunctions::GetActorListAction},\
\
{"GetActorCell", ActorFunctions::GetActorCell},\
{"GetActorRefId", ActorFunctions::GetActorRefId},\
{"GetActorRefNumIndex", ActorFunctions::GetActorRefNumIndex},\
{"GetActorMpNum", ActorFunctions::GetActorMpNum},\
\
{"GetActorPosX", ActorFunctions::GetActorPosX},\
{"GetActorPosY", ActorFunctions::GetActorPosY},\
{"GetActorPosZ", ActorFunctions::GetActorPosZ},\
{"GetActorRotX", ActorFunctions::GetActorRotX},\
{"GetActorRotY", ActorFunctions::GetActorRotY},\
{"GetActorRotZ", ActorFunctions::GetActorRotZ},\
\
{"GetActorHealthBase", ActorFunctions::GetActorHealthBase},\
{"GetActorHealthCurrent", ActorFunctions::GetActorHealthCurrent},\
{"GetActorHealthModified", ActorFunctions::GetActorHealthModified},\
{"GetActorMagickaBase", ActorFunctions::GetActorMagickaBase},\
{"GetActorMagickaCurrent", ActorFunctions::GetActorMagickaCurrent},\
{"GetActorMagickaModified", ActorFunctions::GetActorMagickaModified},\
{"GetActorFatigueBase", ActorFunctions::GetActorFatigueBase},\
{"GetActorFatigueCurrent", ActorFunctions::GetActorFatigueCurrent},\
{"GetActorFatigueModified", ActorFunctions::GetActorFatigueModified},\
\
{"GetActorEquipmentItemRefId", ActorFunctions::GetActorEquipmentItemRefId},\
{"GetActorEquipmentItemCount", ActorFunctions::GetActorEquipmentItemCount},\
{"GetActorEquipmentItemCharge", ActorFunctions::GetActorEquipmentItemCharge},\
{"GetActorEquipmentItemEnchantmentCharge", ActorFunctions::GetActorEquipmentItemEnchantmentCharge},\
\
{"DoesActorHavePosition", ActorFunctions::DoesActorHavePosition},\
{"DoesActorHaveStatsDynamic", ActorFunctions::DoesActorHaveStatsDynamic},\
\
{"SetActorListCell", ActorFunctions::SetActorListCell},\
{"SetActorListAction", ActorFunctions::SetActorListAction},\
\
{"SetActorCell", ActorFunctions::SetActorCell},\
{"SetActorRefId", ActorFunctions::SetActorRefId},\
{"SetActorRefNumIndex", ActorFunctions::SetActorRefNumIndex},\
{"SetActorMpNum", ActorFunctions::SetActorMpNum},\
\
{"SetActorPosition", ActorFunctions::SetActorPosition},\
{"SetActorRotation", ActorFunctions::SetActorRotation},\
\
{"SetActorHealthBase", ActorFunctions::SetActorHealthBase},\
{"SetActorHealthCurrent", ActorFunctions::SetActorHealthCurrent},\
{"SetActorHealthModified", ActorFunctions::SetActorHealthModified},\
{"SetActorMagickaBase", ActorFunctions::SetActorMagickaBase},\
{"SetActorMagickaCurrent", ActorFunctions::SetActorMagickaCurrent},\
{"SetActorMagickaModified", ActorFunctions::SetActorMagickaModified},\
{"SetActorFatigueBase", ActorFunctions::SetActorFatigueBase},\
{"SetActorFatigueCurrent", ActorFunctions::SetActorFatigueCurrent},\
{"SetActorFatigueModified", ActorFunctions::SetActorFatigueModified},\
\
{"SetActorAIAction", ActorFunctions::SetActorAIAction},\
{"SetActorAITargetToPlayer", ActorFunctions::SetActorAITargetToPlayer},\
{"SetActorAITargetToActor", ActorFunctions::SetActorAITargetToActor},\
\
{"EquipActorItem", ActorFunctions::EquipActorItem},\
{"UnequipActorItem", ActorFunctions::UnequipActorItem},\
\
{"AddActor", ActorFunctions::AddActor},\
\
{"SendActorList", ActorFunctions::SendActorList},\
{"SendActorAuthority", ActorFunctions::SendActorAuthority},\
{"SendActorPosition", ActorFunctions::SendActorPosition},\
{"SendActorStatsDynamic", ActorFunctions::SendActorStatsDynamic},\
{"SendActorEquipment", ActorFunctions::SendActorEquipment},\
{"SendActorAI", ActorFunctions::SendActorAI},\
{"SendActorCellChange", ActorFunctions::SendActorCellChange}
class ActorFunctions
{
public:
/**
* \brief Use the last actor list received by the server as the one being read.
*
* \return void
*/
static void ReadLastActorList() noexcept;
/**
* \brief Use the temporary actor list stored for a cell as the one being read.
*
* This type of actor list is used to store actor positions and dynamic stats and is deleted
* when the cell is unloaded.
*
* \param cellDescription The description of the cell whose actor list should be read.
* \return void
*/
static void ReadCellActorList(const char* cellDescription) noexcept;
/**
* \brief Clear the data from the last actor list sent by the server.
*
* This is used to initialize the sending of new Actor packets.
*
* \param pid The player ID to whom the actor list should be attached.
* \return void
*/
static void InitializeActorList(unsigned short pid) noexcept;
/**
* \brief Get the number of indexes in the read actor list.
*
* \return The number of indexes.
*/
static unsigned int GetActorListSize() noexcept;
/**
* \brief Get the action type used in the read actor list.
*
* \return The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).
*/
static unsigned char GetActorListAction() noexcept;
/**
* \brief Get the cell description of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The cell description.
*/
static const char *GetActorCell(unsigned int i) noexcept;
/**
* \brief Get the refId of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The refId.
*/
static const char *GetActorRefId(unsigned int i) noexcept;
/**
* \brief Get the refNumIndex of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The refNumIndex.
*/
static int GetActorRefNumIndex(unsigned int i) noexcept;
/**
* \brief Get the mpNum of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The mpNum.
*/
static int GetActorMpNum(unsigned int i) noexcept;
/**
* \brief Get the X position of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The X position.
*/
static double GetActorPosX(unsigned int i) noexcept;
/**
* \brief Get the Y position of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The Y position.
*/
static double GetActorPosY(unsigned int i) noexcept;
/**
* \brief Get the Z position of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The Z position.
*/
static double GetActorPosZ(unsigned int i) noexcept;
/**
* \brief Get the X rotation of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The X rotation.
*/
static double GetActorRotX(unsigned int i) noexcept;
/**
* \brief Get the Y rotation of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The Y rotation.
*/
static double GetActorRotY(unsigned int i) noexcept;
/**
* \brief Get the Z rotation of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The Z rotation.
*/
static double GetActorRotZ(unsigned int i) noexcept;
/**
* \brief Get the base health of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The base health.
*/
static double GetActorHealthBase(unsigned int i) noexcept;
/**
* \brief Get the current health of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The current health.
*/
static double GetActorHealthCurrent(unsigned int i) noexcept;
/**
* \brief Get the modified health of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The modified health.
*/
static double GetActorHealthModified(unsigned int i) noexcept;
/**
* \brief Get the base magicka of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The base magicka.
*/
static double GetActorMagickaBase(unsigned int i) noexcept;
/**
* \brief Get the current magicka of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The current magicka.
*/
static double GetActorMagickaCurrent(unsigned int i) noexcept;
/**
* \brief Get the modified magicka of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The modified magicka.
*/
static double GetActorMagickaModified(unsigned int i) noexcept;
/**
* \brief Get the base fatigue of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The base fatigue.
*/
static double GetActorFatigueBase(unsigned int i) noexcept;
/**
* \brief Get the current fatigue of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The current fatigue.
*/
static double GetActorFatigueCurrent(unsigned int i) noexcept;
/**
* \brief Get the modified fatigue of the actor at a certain index in the read actor list.
*
* \param i The index of the actor.
* \return The modified fatigue.
*/
static double GetActorFatigueModified(unsigned int i) noexcept;
/**
* \brief Get the refId of the item in a certain slot of the equipment of the actor at a
* certain index in the read actor list.
*
* \param i The index of the actor.
* \param slot The slot of the equipment item.
* \return The refId.
*/
static const char *GetActorEquipmentItemRefId(unsigned int i, unsigned short slot) noexcept;
/**
* \brief Get the count of the item in a certain slot of the equipment of the actor at a
* certain index in the read actor list.
*
* \param i The index of the actor.
* \param slot The slot of the equipment item.
* \return The item count.
*/
static int GetActorEquipmentItemCount(unsigned int i, unsigned short slot) noexcept;
/**
* \brief Get the charge of the item in a certain slot of the equipment of the actor at a
* certain index in the read actor list.
*
* \param i The index of the actor.
* \param slot The slot of the equipment item.
* \return The charge.
*/
static int GetActorEquipmentItemCharge(unsigned int i, unsigned short slot) noexcept;
/**
* \brief Get the enchantment charge of the item in a certain slot of the equipment of the actor at a
* certain index in the read actor list.
*
* \param i The index of the actor.
* \param slot The slot of the equipment item.
* \return The enchantment charge.
*/
static double GetActorEquipmentItemEnchantmentCharge(unsigned int i, unsigned short slot) noexcept;
/**
* \brief Check whether there is any positional data for the actor at a certain index in
* the read actor list.
*
* This is only useful when reading the actor list data recorded for a particular cell.
*
* \param i The index of the actor.
* \return Whether the read actor list contains positional data.
*/
static bool DoesActorHavePosition(unsigned int i) noexcept;
/**
* \brief Check whether there is any dynamic stats data for the actor at a certain index in
* the read actor list.
*
* This is only useful when reading the actor list data recorded for a particular cell.
*
* \param i The index of the actor.
* \return Whether the read actor list contains dynamic stats data.
*/
static bool DoesActorHaveStatsDynamic(unsigned int i) noexcept;
/**
* \brief Set the cell of the temporary actor list stored on the server.
*
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
* by a comma followed by another number.
*
* \param cellDescription The description of the cell.
* \return void
*/
static void SetActorListCell(const char* cellDescription) noexcept;
/**
* \brief Set the action type of the temporary actor list stored on the server.
*
* \param action The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).
* \return void
*/
static void SetActorListAction(unsigned char action) noexcept;
/**
* \brief Set the cell of the temporary actor stored on the server.
*
* Used for ActorCellChange packets, where a specific actor's cell now differs from that of the
* actor list.
*
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
* by a comma followed by another number.
*
* \param cellDescription The description of the cell.
* \return void
*/
static void SetActorCell(const char* cellDescription) noexcept;
/**
* \brief Set the refId of the temporary actor stored on the server.
*
* \param refId The refId.
* \return void
*/
static void SetActorRefId(const char* refId) noexcept;
/**
* \brief Set the refNumIndex of the temporary actor stored on the server.
*
* \param refNumIndex The refNumIndex.
* \return void
*/
static void SetActorRefNumIndex(int refNumIndex) noexcept;
/**
* \brief Set the mpNum of the temporary actor stored on the server.
*
* \param mpNum The mpNum.
* \return void
*/
static void SetActorMpNum(int mpNum) noexcept;
/**
* \brief Set the position of the temporary actor stored on the server.
*
* \param x The X position.
* \param y The Y position.
* \param z The Z position.
* \return void
*/
static void SetActorPosition(double x, double y, double z) noexcept;
/**
* \brief Set the rotation of the temporary actor stored on the server.
*
* \param x The X rotation.
* \param y The Y rotation.
* \param z The Z rotation.
* \return void
*/
static void SetActorRotation(double x, double y, double z) noexcept;
/**
* \brief Set the base health of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorHealthBase(double value) noexcept;
/**
* \brief Set the current health of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorHealthCurrent(double value) noexcept;
/**
* \brief Set the modified health of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorHealthModified(double value) noexcept;
/**
* \brief Set the base magicka of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorMagickaBase(double value) noexcept;
/**
* \brief Set the current magicka of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorMagickaCurrent(double value) noexcept;
/**
* \brief Set the modified magicka of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorMagickaModified(double value) noexcept;
/**
* \brief Set the base fatigue of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorFatigueBase(double value) noexcept;
/**
* \brief Set the current fatigue of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorFatigueCurrent(double value) noexcept;
/**
* \brief Set the modified fatigue of the temporary actor stored on the server.
*
* \param value The new value.
* \return void
*/
static void SetActorFatigueModified(double value) noexcept;
/**
* \brief Set the AI action of the temporary actor stored on the server.
*
* \param action The new action.
* \return void
*/
static void SetActorAIAction(unsigned int action) noexcept;
/**
* \brief Set a player as the AI target of the temporary actor stored on the server.
*
* \param pid The player ID.
* \return void
*/
static void SetActorAITargetToPlayer(unsigned short pid) noexcept;
/**
* \brief Set another actor as the AI target of the temporary actor stored on the server.
*
* \param refNumIndex The refNumIndex of the target actor.
* \param mpNum The mpNum of the target actor.
* \return void
*/
static void SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept;
/**
* \brief Equip an item in a certain slot of the equipment of the temporary actor stored
* on the server.
*
* \param slot The equipment slot.
* \param refId The refId of the item.
* \param count The count of the item.
* \param charge The charge of the item.
* \param enchantmentCharge The enchantment charge of the item.
* \return void
*/
static void EquipActorItem(unsigned short slot, const char* refId, unsigned int count, int charge, double enchantmentCharge = -1) noexcept;
/**
* \brief Unequip the item in a certain slot of the equipment of the temporary actor stored
* on the server.
*
* \param slot The equipment slot.
* \return void
*/
static void UnequipActorItem(unsigned short slot) noexcept;
/**
* \brief Add a copy of the server's temporary actor to the server's temporary actor list.
*
* In the process, the server's temporary actor will automatically be cleared so a new
* one can be set up.
*
* \return void
*/
static void AddActor() noexcept;
/**
* \brief Send an ActorList packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorList() noexcept;
/**
* \brief Send an ActorAuthority packet.
*
* The player for whom the current actor list was initialized is recorded in the server memory
* as the new actor authority for the actor list's cell.
*
* The packet is sent to that player as well as all other players who have the cell loaded.
*
* \return void
*/
static void SendActorAuthority() noexcept;
/**
* \brief Send an ActorPosition packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorPosition() noexcept;
/**
* \brief Send an ActorStatsDynamic packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorStatsDynamic() noexcept;
/**
* \brief Send an ActorEquipment packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorEquipment() noexcept;
/**
* \brief Send an ActorAI packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorAI() noexcept;
/**
* \brief Send an ActorCellChange packet.
*
* It is sent only to the player for whom the current actor list was initialized.
*
* \return void
*/
static void SendActorCellChange() noexcept;
};
#endif //OPENMW_ACTORAPI_HPP

@ -1,55 +0,0 @@
#include "Books.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <apps/openmw-mp/Networking.hpp>
using namespace mwmp;
void BookFunctions::InitializeBookChanges(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
return player->bookChanges.books.clear();
}
unsigned int BookFunctions::GetBookChangesSize(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->bookChanges.count;
}
void BookFunctions::AddBook(unsigned short pid, const char* bookId) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Book book;
book.bookId = bookId;
player->bookChanges.books.push_back(book);
}
const char *BookFunctions::GetBookId(unsigned short pid, unsigned int i) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
if (i >= player->bookChanges.count)
return "invalid";
return player->bookChanges.books.at(i).bookId.c_str();
}
void BookFunctions::SendBookChanges(unsigned short pid, bool toOthers) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK)->Send(toOthers);
}

@ -1,69 +0,0 @@
#ifndef OPENMW_BOOKAPI_HPP
#define OPENMW_BOOKAPI_HPP
#define BOOKAPI \
{"InitializeBookChanges", BookFunctions::InitializeBookChanges},\
\
{"GetBookChangesSize", BookFunctions::GetBookChangesSize},\
\
{"AddBook", BookFunctions::AddBook},\
\
{"GetBookId", BookFunctions::GetBookId},\
\
{"SendBookChanges", BookFunctions::SendBookChanges}
class BookFunctions
{
public:
/**
* \brief Clear the last recorded book changes for a player.
*
* This is used to initialize the sending of new PlayerBook packets.
*
* \param pid The player ID whose book changes should be used.
* \return void
*/
static void InitializeBookChanges(unsigned short pid) noexcept;
/**
* \brief Get the number of indexes in a player's latest book changes.
*
* \param pid The player ID whose book changes should be used.
* \return The number of indexes.
*/
static unsigned int GetBookChangesSize(unsigned short pid) noexcept;
/**
* \brief Add a new book to the book changes for a player.
*
* \param pid The player ID whose book changes should be used.
* \param bookId The bookId of the book.
* \return void
*/
static void AddBook(unsigned short pid, const char* bookId) noexcept;
/**
* \brief Get the bookId at a certain index in a player's latest book changes.
*
* \param pid The player ID whose book changes should be used.
* \param i The index of the book.
* \return The bookId.
*/
static const char *GetBookId(unsigned short pid, unsigned int i) noexcept;
/**
* \brief Send a PlayerBook packet with a player's recorded book changes.
*
* \param pid The player ID whose book changes should be used.
* \param toOthers Whether this packet should be sent only to other players or
* only to the player it is about.
* \return void
*/
static void SendBookChanges(unsigned short pid, bool toOthers = false) noexcept;
private:
};
#endif //OPENMW_BOOKAPI_HPP

@ -1,125 +0,0 @@
#include "Cells.hpp"
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <apps/openmw-mp/Player.hpp>
#include <apps/openmw-mp/Networking.hpp>
#include <iostream>
using namespace std;
static std::string tempCellDescription;
unsigned int CellFunctions::GetCellStateChangesSize(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->cellStateChanges.count;
}
unsigned int CellFunctions::GetCellStateType(unsigned short pid, unsigned int i) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->cellStateChanges.cellStates.at(i).type;
}
const char *CellFunctions::GetCellStateDescription(unsigned short pid, unsigned int i) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
if (i >= player->cellStateChanges.count)
return "invalid";
tempCellDescription = player->cellStateChanges.cellStates.at(i).cell.getDescription();
return tempCellDescription.c_str();
}
const char *CellFunctions::GetCell(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
tempCellDescription = player->cell.getDescription().c_str();
return tempCellDescription.c_str();
}
int CellFunctions::GetExteriorX(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->cell.mData.mX;
}
int CellFunctions::GetExteriorY(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->cell.mData.mY;
}
bool CellFunctions::IsInExterior(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, false);
return player->cell.isExterior();
}
const char *CellFunctions::GetRegion(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->cell.mRegion.c_str();
}
bool CellFunctions::IsChangingRegion(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, false);
return player->isChangingRegion;
}
void CellFunctions::SetCell(unsigned short pid, const char *cellDescription) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %s", player->npc.mName.c_str(),
player->cell.getDescription().c_str(), cellDescription);
player->cell = Utils::getCellFromDescription(cellDescription);
}
void CellFunctions::SetExteriorCell(unsigned short pid, int x, int y) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %i,%i", player->npc.mName.c_str(),
player->cell.getDescription().c_str(), x, y);
// If the player is currently in an interior, turn off the interior flag
// from the cell
if (!player->cell.isExterior())
player->cell.mData.mFlags &= ~ESM::Cell::Interior;
player->cell.mData.mX = x;
player->cell.mData.mY = y;
}
void CellFunctions::SendCell(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(false);
}

@ -1,146 +0,0 @@
#ifndef OPENMW_CELLAPI_HPP
#define OPENMW_CELLAPI_HPP
#include "../Types.hpp"
#define CELLAPI \
{"GetCellStateChangesSize", CellFunctions::GetCellStateChangesSize},\
\
{"GetCellStateType", CellFunctions::GetCellStateType},\
{"GetCellStateDescription", CellFunctions::GetCellStateDescription},\
\
{"GetCell", CellFunctions::GetCell},\
{"GetExteriorX", CellFunctions::GetExteriorX},\
{"GetExteriorY", CellFunctions::GetExteriorY},\
{"IsInExterior", CellFunctions::IsInExterior},\
\
{"GetRegion", CellFunctions::GetRegion},\
{"IsChangingRegion", CellFunctions::IsChangingRegion},\
\
{"SetCell", CellFunctions::SetCell},\
{"SetExteriorCell", CellFunctions::SetExteriorCell},\
\
{"SendCell", CellFunctions::SendCell}
class CellFunctions
{
public:
/**
* \brief Get the number of indexes in a player's latest cell state changes.
*
* \param pid The player ID whose cell state changes should be used.
* \return The number of indexes.
*/
static unsigned int GetCellStateChangesSize(unsigned short pid) noexcept;
/**
* \brief Get the cell state type at a certain index in a player's latest cell state changes.
*
* \param pid The player ID whose cell state changes should be used.
* \param i The index of the cell state.
* \return The cell state type (0 for LOAD, 1 for UNLOAD).
*/
static unsigned int GetCellStateType(unsigned short pid, unsigned int i) noexcept;
/**
* \brief Get the cell description at a certain index in a player's latest cell state changes.
*
* \param pid The player ID whose cell state changes should be used.
* \param i The index of the cell state.
* \return The cell description.
*/
static const char *GetCellStateDescription(unsigned short pid, unsigned int i) noexcept;
/**
* \brief Get the cell description of a player's cell.
*
* \param pid The player ID.
* \return The cell description.
*/
static const char *GetCell(unsigned short pid) noexcept;
/**
* \brief Get the X coordinate of the player's exterior cell.
*
* \param pid The player ID.
* \return The X coordinate of the cell.
*/
static int GetExteriorX(unsigned short pid) noexcept;
/**
* \brief Get the Y coordinate of the player's exterior cell.
*
* \param pid The player ID.
* \return The Y coordinate of the cell.
*/
static int GetExteriorY(unsigned short pid) noexcept;
/**
* \brief Check whether the player is in an exterior cell or not.
*
* \param pid The player ID.
* \return Whether the player is in an exterior cell.
*/
static bool IsInExterior(unsigned short pid) noexcept;
/**
* \brief Get the region of the player's exterior cell.
*
* A blank value will be returned if the player is in an interior.
*
* \param pid The player ID.
* \return The region.
*/
static const char *GetRegion(unsigned short pid) noexcept;
/**
* \brief Check whether the player's last cell change has involved a region change.
*
* \param pid The player ID.
* \return Whether the player has changed their region.
*/
static bool IsChangingRegion(unsigned short pid) noexcept;
/**
* \brief Set the cell of a player.
*
* This changes the cell recorded for that player in the server memory, but does not by itself
* send a packet.
*
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
* by a comma followed by another number.
*
* \param pid The player ID.
* \param cellDescription The cell description.
* \return void
*/
static void SetCell(unsigned short pid, const char *cellDescription) noexcept;
/**
* \brief Set the cell of a player to an exterior cell.
*
* This changes the cell recorded for that player in the server memory, but does not by itself
* send a packet.
*
* \param pid The player ID.
* \param x The X coordinate of the cell.
* \param y The Y coordinate of the cell.
* \return void
*/
static void SetExteriorCell(unsigned short pid, int x, int y) noexcept;
/**
* \brief Send a PlayerCellChange packet about a player.
*
* It is only sent to the affected player.
*
* \param pid The player ID.
* \return void
*/
static void SendCell(unsigned short pid) noexcept;
};
#endif //OPENMW_CELLAPI_HPP

@ -1,137 +0,0 @@
//
// Created by koncord on 29.08.16.
//
#include "CharClass.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
#include <apps/openmw-mp/Networking.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
using namespace std;
using namespace ESM;
const char *CharClassFunctions::GetDefaultClass(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
return player->charClass.mId.c_str();
}
const char *CharClassFunctions::GetClassName(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
return player->charClass.mName.c_str();
}
const char *CharClassFunctions::GetClassDesc(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
return player->charClass.mDescription.c_str();
}
int CharClassFunctions::GetClassMajorAttribute(unsigned short pid, unsigned char slot) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
if (slot > 1)
throw invalid_argument("Incorrect attribute slot id");
return player->charClass.mData.mAttribute[slot];
}
int CharClassFunctions::GetClassSpecialization(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->charClass.mData.mSpecialization;
}
int CharClassFunctions::GetClassMajorSkill(unsigned short pid, unsigned char slot) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
if (slot > 4)
throw invalid_argument("Incorrect skill slot id");
return player->charClass.mData.mSkills[slot][1];
}
int CharClassFunctions::GetClassMinorSkill(unsigned short pid, unsigned char slot) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
if (slot > 4)
throw invalid_argument("Incorrect skill slot id");
return player->charClass.mData.mSkills[slot][0];
}
int CharClassFunctions::IsClassDefault(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return !player->charClass.mId.empty(); // true if default
}
void CharClassFunctions::SetDefaultClass(unsigned short pid, const char *id) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
player->charClass.mId = id;
}
void CharClassFunctions::SetClassName(unsigned short pid, const char *name) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
player->charClass.mName = name;
player->charClass.mId = "";
}
void CharClassFunctions::SetClassDesc(unsigned short pid, const char *desc) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
player->charClass.mDescription = desc;
}
void CharClassFunctions::SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
if (slot > 1)
throw invalid_argument("Incorrect attribute slot id");
player->charClass.mData.mAttribute[slot] = attrId;
}
void CharClassFunctions::SetClassSpecialization(unsigned short pid, int spec) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
player->charClass.mData.mSpecialization = spec;
}
void CharClassFunctions::SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
if (slot > 4)
throw invalid_argument("Incorrect skill slot id");
player->charClass.mData.mSkills[slot][1] = skillId;
}
void CharClassFunctions::SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
if (slot > 4)
throw invalid_argument("Incorrect skill slot id");
player->charClass.mData.mSkills[slot][0] = skillId;
}
void CharClassFunctions::SendClass(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS)->Send(false);
}

@ -1,181 +0,0 @@
//
// Created by koncord on 29.08.16.
//
#ifndef OPENMW_CHARCLASSAPI_HPP
#define OPENMW_CHARCLASSAPI_HPP
#include "../Types.hpp"
#define CHARCLASSAPI \
{"GetDefaultClass", CharClassFunctions::GetDefaultClass},\
{"GetClassName", CharClassFunctions::GetClassName},\
{"GetClassDesc", CharClassFunctions::GetClassDesc},\
{"GetClassMajorAttribute", CharClassFunctions::GetClassMajorAttribute},\
{"GetClassSpecialization", CharClassFunctions::GetClassSpecialization},\
{"GetClassMajorSkill", CharClassFunctions::GetClassMajorSkill},\
{"GetClassMinorSkill", CharClassFunctions::GetClassMinorSkill},\
{"IsClassDefault", CharClassFunctions::IsClassDefault},\
\
{"SetDefaultClass", CharClassFunctions::SetDefaultClass},\
{"SetClassName", CharClassFunctions::SetClassName},\
{"SetClassDesc", CharClassFunctions::SetClassDesc},\
{"SetClassMajorAttribute", CharClassFunctions::SetClassMajorAttribute},\
{"SetClassSpecialization", CharClassFunctions::SetClassSpecialization},\
{"SetClassMajorSkill", CharClassFunctions::SetClassMajorSkill},\
{"SetClassMinorSkill", CharClassFunctions::SetClassMinorSkill},\
\
{"SendClass", CharClassFunctions::SendClass}
class CharClassFunctions
{
public:
/**
* \brief Get the default class used by a player.
*
* \param pid The player ID.
* \return The ID of the default class.
*/
static const char *GetDefaultClass(unsigned short pid) noexcept;
/**
* \brief Get the name of the custom class used by a player.
*
* \param pid The player ID.
* \return The name of the custom class.
*/
static const char *GetClassName(unsigned short pid) noexcept;
/**
* \brief Get the description of the custom class used by a player.
*
* \param pid The player ID.
* \return The description of the custom class.
*/
static const char *GetClassDesc(unsigned short pid) noexcept;
/**
* \brief Get the ID of one of the two major attributes of a custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the major attribute (0 or 1).
* \return The ID of the major attribute.
*/
static int GetClassMajorAttribute(unsigned short pid, unsigned char slot) noexcept;
/**
* \brief Get the specialization ID of the custom class used by a player.
*
* \param pid The player ID.
* \return The specialization ID of the custom class (0 for Combat, 1 for Magic, 2 for Stealth).
*/
static int GetClassSpecialization(unsigned short pid) noexcept;
/**
* \brief Get the ID of one of the five major skills of a custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the major skill (0 to 4).
* \return The ID of the major skill.
*/
static int GetClassMajorSkill(unsigned short pid, unsigned char slot) noexcept;
/**
* \brief Get the ID of one of the five minor skills of a custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the minor skill (0 to 4).
* \return The ID of the minor skill.
*/
static int GetClassMinorSkill(unsigned short pid, unsigned char slot) noexcept;
/**
* \brief Check whether the player is using a default class instead of a custom one.
*
* \param pid The player ID.
* \return Whether the player is using a default class.
*/
static int IsClassDefault(unsigned short pid) noexcept;
/**
* \brief Set the default class used by a player.
*
* If this is left blank, the custom class data set for the player will be used instead.
*
* \param pid The player ID.
* \param id The ID of the default class.
* \return void
*/
static void SetDefaultClass(unsigned short pid, const char *id) noexcept;
/**
* \brief Set the name of the custom class used by a player.
*
* \param pid The player ID.
* \param name The name of the custom class.
* \return void
*/
static void SetClassName(unsigned short pid, const char *name) noexcept;
/**
* \brief Set the description of the custom class used by a player.
*
* \param pid The player ID.
* \param desc The description of the custom class.
* \return void
*/
static void SetClassDesc(unsigned short pid, const char *desc) noexcept;
/**
* \brief Set the ID of one of the two major attributes of the custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the major attribute (0 or 1).
* \param attrId The ID to use for the attribute.
* \return void
*/
static void SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId) noexcept;
/**
* \brief Set the specialization of the custom class used by a player.
*
* \param pid The player ID.
* \param spec The specialization ID to use (0 for Combat, 1 for Magic, 2 for Stealth).
* \return void
*/
static void SetClassSpecialization(unsigned short pid, int spec) noexcept;
/**
* \brief Set the ID of one of the five major skills of the custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the major skill (0 to 4).
* \param skillId The ID to use for the skill.
* \return void
*/
static void SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept;
/**
* \brief Set the ID of one of the five minor skills of the custom class used by a player.
*
* \param pid The player ID.
* \param slot The slot of the minor skill (0 to 4).
* \param skillId The ID to use for the skill.
* \return void
*/
static void SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept;
/**
* \brief Send a PlayerCharClass packet about a player.
*
* It is only sent to the affected player.
*
* \param pid The player ID.
* \return void
*/
static void SendClass(unsigned short pid) noexcept;
};
#endif //OPENMW_CHARCLASSAPI_HPP

@ -1,47 +0,0 @@
//
// Created by koncord on 29.04.16.
//
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <apps/openmw-mp/Networking.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
void ScriptFunctions::SendMessage(unsigned short pid, const char *message, bool broadcast) noexcept
{
Player *player;
GET_PLAYER(pid, player,);
player->chatMessage = message;
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "System: %s", message);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
if (broadcast)
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(true);
}
void ScriptFunctions::CleanChatForPid(unsigned short pid)
{
Player *player;
GET_PLAYER(pid, player,);
player->chatMessage.clear();
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
}
void ScriptFunctions::CleanChat()
{
for (auto player : *Players::getPlayers())
{
player.second->chatMessage.clear();
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player.second);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
}
}

@ -1,84 +0,0 @@
#include "Dialogue.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <apps/openmw-mp/Networking.hpp>
using namespace mwmp;
void DialogueFunctions::InitializeTopicChanges(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
player->topicChanges.topics.clear();
}
unsigned int DialogueFunctions::GetTopicChangesSize(unsigned short pid) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
return player->topicChanges.count;
}
void DialogueFunctions::AddTopic(unsigned short pid, const char* topicId) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Topic topic;
topic.topicId = topicId;
player->topicChanges.topics.push_back(topic);
}
const char *DialogueFunctions::GetTopicId(unsigned short pid, unsigned int i) noexcept
{
Player *player;
GET_PLAYER(pid, player, "");
if (i >= player->topicChanges.count)
return "invalid";
return player->topicChanges.topics.at(i).topicId.c_str();
}
void DialogueFunctions::SendTopicChanges(unsigned short pid, bool toOthers) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC)->setPlayer(player);
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC)->Send(toOthers);
}
void DialogueFunctions::PlayAnimation(unsigned short pid, const char* groupname, int mode, int count, bool persist) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
player->animation.groupname = groupname;
player->animation.mode = mode;
player->animation.count = count;
player->animation.persist = persist;
mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY);
packet->setPlayer(player);
packet->Send(false);
player->sendToLoaded(packet);
}
void DialogueFunctions::PlaySpeech(unsigned short pid, const char* sound) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
player->sound = sound;
mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH);
packet->setPlayer(player);
packet->Send(false);
player->sendToLoaded(packet);
}

@ -1,75 +0,0 @@
#ifndef OPENMW_DIALOGUEAPI_HPP
#define OPENMW_DIALOGUEAPI_HPP
#define DIALOGUEAPI \
{"InitializeTopicChanges", DialogueFunctions::InitializeTopicChanges},\
\
{"GetTopicChangesSize", DialogueFunctions::GetTopicChangesSize},\
\
{"AddTopic", DialogueFunctions::AddTopic},\
\
{"GetTopicId", DialogueFunctions::GetTopicId},\
\
{"SendTopicChanges", DialogueFunctions::SendTopicChanges},\
\
{"PlayAnimation", DialogueFunctions::PlayAnimation},\
{"PlaySpeech", DialogueFunctions::PlaySpeech}
class DialogueFunctions
{
public:
/**
* \brief Clear the last recorded topic changes for a player.
*
* This is used to initialize the sending of new PlayerTopic packets.
*
* \param pid The player ID whose topic changes should be used.
* \return void
*/
static void InitializeTopicChanges(unsigned short pid) noexcept;
/**
* \brief Get the number of indexes in a player's latest topic changes.
*
* \param pid The player ID whose topic changes should be used.
* \return The number of indexes.
*/
static unsigned int GetTopicChangesSize(unsigned short pid) noexcept;
/**
* \brief Add a new topic to the topic changes for a player.
*
* \param pid The player ID whose topic changes should be used.
* \param topicId The topicId of the topic.
* \return void
*/
static void AddTopic(unsigned short pid, const char* topicId) noexcept;
/**
* \brief Get the topicId at a certain index in a player's latest topic changes.
*
* \param pid The player ID whose topic changes should be used.
* \param i The index of the topic.
* \return The topicId.
*/
static const char *GetTopicId(unsigned short pid, unsigned int i) noexcept;
/**
* \brief Send a PlayerTopic packet with a player's recorded topic changes.
*
* \param pid The player ID whose topic changes should be used.
* \param toOthers Whether this packet should be sent only to other players or
* only to the player it is about.
* \return void
*/
static void SendTopicChanges(unsigned short pid, bool toOthers = false) noexcept;
static void PlayAnimation(unsigned short pid, const char* groupname, int mode = 0, int count = 1, bool persist = false) noexcept;
static void PlaySpeech(unsigned short pid, const char* sound) noexcept;
private:
};
#endif //OPENMW_DIALOGUEAPI_HPP

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

Loading…
Cancel
Save