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
2018-10-16 23:00:02 +03:00
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.
2018-10-15 22:43:33 +02:00
David Cernat
cf33695447 [General] Change rewrite's version to TBD-rewrite 2018-07-11 17:24:07 +03:00
David Cernat
dbd6ccaf97
Merge pull request #455 from testman42/patch-1
Rename "loglevel" to "logLevel"
2018-07-02 02:35:42 +03:00
Testman
702d5b44c9
Rename "loglevel" to "logLevel"
Which should make server browser and not crash the client.
2018-07-02 01:12:26 +02:00
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
2018-06-27 14:53:46 +03:00
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
2018-06-25 04:44:30 +03:00
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
2018-06-21 22:31:10 +03:00
David Cernat
b3c398605f
Merge pull request #448 from TES3MP/0.6.3
Add 0.6.3 commits up to 8 Jun 2018
2018-06-08 06:03:03 +03:00
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
2018-06-07 23:23:19 +03:00
David Cernat
6bb2a7e24a Merge pull request #444 from TES3MP/0.6.3 while resolving conflicts 2018-06-06 08:26:14 +03:00
David Cernat
058d67dbd0 [Server] Use consistent code style and make all files end in newlines 2018-06-05 09:40:47 +03:00
David Cernat
49bf605e8e [Server] Create Worldstate class and implement associated table 2018-06-04 18:51:17 +03:00
David Cernat
07b781cd32 [Server] Fix variable name for actor table 2018-06-04 16:11:37 +03:00
Stanislav Zhukov
08915c44a0
Merge pull request #431 from Aesylwinn/Travis-master
Fix travis build on master
2018-05-28 13:39:40 +08:00
Kyle Cooley
e12330b038 Undo increase to gcc/clang template depth 2018-05-27 22:48:09 -05:00
David Cernat
5c41350532 [Server] Fix function name for setting timeScale 2018-05-28 05:13:26 +03:00
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
2018-05-28 05:11:34 +03:00
David Cernat
569243c987 [Browser] Use better phrasing for disclaimer 2018-05-27 15:27:50 +03:00
Koncord
7180b009bd [Browser] Add disclaimer to the main window
Add forgotten default Browser section in the client config
2018-05-27 11:10:37 +08:00
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
2018-05-25 06:34:07 +03:00
David Cernat
7083cb0359 Merge pull request #437 from TES3MP/0.6.3 while resolving conflicts 2018-05-23 06:41:23 +03:00
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
2018-05-21 09:26:08 +03:00
Kyle Cooley
83fe29d10f Get rid of tab 2018-05-20 15:02:04 -05:00
Kyle Cooley
3a68f4bd8f Fix travis build on master
- add luajit
- remove terra/callff
- fix template issue
- add missing include
- search for sol in extern
2018-05-20 14:26:25 -05:00
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
2018-05-18 08:12:28 +03:00
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
2018-05-16 06:28:24 +03:00
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
2018-05-15 21:10:47 +03:00
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
2018-05-13 03:20:01 +03:00
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
2018-05-12 20:35:34 +03:00
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
2018-05-12 18:01:01 +03:00
David Cernat
c5f33e451f Merge pull request #418 from TES3MP/0.6.3 while resolving conflicts 2018-05-10 22:33:36 +03:00
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
2018-05-02 23:15:39 +03:00
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
2018-04-29 06:46:28 +03:00
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
2018-04-21 16:55:05 +03:00
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
2018-04-18 18:29:00 +03:00
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
2018-04-13 18:05:38 +03:00
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
2018-04-10 08:20:23 +03:00
David Cernat
c3b44e11fb
Merge pull request #402 from TES3MP/0.6.3
Add 0.6.3 commits up to 5 Apr 2018
2018-04-05 12:53:38 +03:00
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
2018-04-03 02:17:12 +03:00
Koncord
6131ada0ba [Browser] Add compatibility compilation mode 2018-04-02 14:04:48 +08:00
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
2018-04-01 11:34:04 +03:00
Koncord
2aaf9105af [Browser] Add Client Settings and Server Settings Tabs.
Save Browser state to the client config
2018-03-31 21:10:32 +08:00
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
2018-03-30 09:32:43 +03:00
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
2018-03-10 20:03:03 +02:00
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.
2018-03-09 21:14:56 +02:00
Koncord
2ac01dc02a [Server] Add previous state to ON_RECORD_DYNAMIC 2018-03-05 15:40:38 +08:00
Koncord
4aff1f1833 [Server] Add previous state to ON_PLAYER_SKILL
That is array of struct with next content: {base, current, mod, damage, progress}
2018-03-05 15:30:24 +08:00
Koncord
017956366f [Server] Add previous state argument to ON_PLAYER_ATTRIBUTE event
That is array of struct with next content: {base, current, mod, damage}
2018-03-05 15:19:09 +08:00
Koncord
afbafdf806 [Server] Add PreReading virtual method
add "const" qualifier to exteriorCellPattern
2018-03-05 15:15:39 +08:00
Koncord
d0eef7c98e [Server] Add possibility to use previous state of data in events 2018-03-05 04:41:21 +08:00
Koncord
7deff7a42a [Server] Use "else if" instead "if" + "return" 2018-02-26 21:10:48 +08:00
Koncord
23da0b16ea [Server] Fix build 2018-02-26 21:03:42 +08:00
Koncord
6f7771d97e [General] Move similar functions to BasePacketController
Simplify ContainsPacket and fix GetPacket
2018-02-26 21:03:08 +08:00
Koncord
24ba4ae404 [Server] Delete ON_GUI_ACTION 2018-02-17 14:24:57 +08:00
Koncord
73d030b779 [Server] Remove return value from ON_PLAYER_CONNECT event 2018-02-17 14:24:38 +08:00
Koncord
4e869a2974 [General] Use correct versions of C++ per target
C++11 for client, components and launcher
C++14 for server, browser and masterserver
2018-02-16 16:31:45 +08:00
Koncord
e85d0db771 [Server] Code style 2018-02-16 12:21:20 +08:00
Koncord
44c549211e [Server] use std::unique_ptr for packet controllers and MasterClient 2018-02-16 12:14:51 +08:00
Koncord
2bfd4627ed [Server] Use queue for MessageBox packets 2018-02-16 11:28:47 +08:00
Koncord
9dae748a76 [General] Change type of GUIMessageBox::id to uint64_t 2018-02-16 11:27:53 +08:00
Koncord
bb7c5ee34c [Server] Allow nil as callback 2018-02-16 06:28:35 +08:00
Koncord
54945b537d [Server] Get rid handwritten ids in GUI API 2018-02-16 06:08:36 +08:00
Koncord
4bde7d80f5 [Server] Use regular Ptrs where possible to avoid seizure Ptrs by lua 2018-02-16 05:12:01 +08:00
Koncord
f2a88e6a37 [Server] Use multiple parameters instead functions for Mark functions 2018-02-16 04:58:37 +08:00
Koncord
410eb353e8 [Server] Call GC after deleting player 2018-02-16 04:41:19 +08:00
Koncord
a9614ad28e [Server] Fix invalid read 2018-02-16 04:38:42 +08:00
Koncord
69436714f9 [Server] Fix uninitialized variable 2018-02-16 04:36:44 +08:00
David Cernat
3b865244d0 [Server] Fix typo related to player deletion 2018-02-12 07:23:52 +02:00
David Cernat
4e9cac96c7 [Server] Add scale and selected spell functions to Player in Sol 2018-02-12 07:08:47 +02:00
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
2018-02-12 06:50:52 +02:00
David Cernat
bdafa8e9ab
Merge pull request #380 from TES3MP/0.6.3
Add 0.6.3 commits up to 30 Jan 2018
2018-01-30 16:05:24 +02:00
David Cernat
f1ba9253b0 [Client] Fix build 2018-01-30 14:06:07 +02:00
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
2018-01-30 00:54:56 +02:00
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
2018-01-29 02:57:48 +02:00
David Cernat
569911121d [Client] Fix build 2018-01-27 22:03:21 +02:00
David Cernat
5cf71a4e67 Merge branch 'master' of https://github.com/TES3MP/openmw-tes3mp 2018-01-27 21:14:33 +02:00
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
2018-01-27 21:14:24 +02:00
Koncord
6197636fac [General] Fix "using after move" warning 2018-01-20 21:02:41 +08:00
Koncord
edd883853d [Server] Add sendConsoleCommand function to lua api 2018-01-20 20:46:14 +08:00
Koncord
0e97b769f9 [General] Change type of Object::enchantmentCharge 2018-01-20 20:07:29 +08:00
Koncord
4cfb04aa7f [Server] Fix setBedRestAllowed 2018-01-20 20:06:41 +08:00
Koncord
7e5b929ea2 [General] Rework PacketPlayerFaction 2018-01-20 19:45:24 +08:00
Koncord
29ba07fe8c [General] Rework PacketPlayerInventory
Save the Action for each item. Now you can add or remove multiple items
2018-01-20 18:40:23 +08:00
Koncord
de0bb3cdab [General] Change type of Item::enchantmentCharge to float 2018-01-20 18:32:26 +08:00
Koncord
cb86557aca [Server] Init Player::inUpdateQueue 2018-01-20 15:46:25 +08:00
Koncord
0780329a59 [General] Fix typo 2018-01-19 19:32:51 +08:00
Koncord
1f1cbf53f9 [Client] Mark trivial destructor as default, remove "virtual" keyword
Mark constructor as explicit
2018-01-19 15:56:01 +08:00
Koncord
b63bf258ff [Client] Remove "virtual" keyword for getLocalActor & getDedicatedActor 2018-01-19 15:52:05 +08:00
Koncord
c2578918f2 [Client] Do not copy strings in getLocalActor and getDedicatedActor 2018-01-19 15:50:44 +08:00
Koncord
e2e197d84a [Client] Fix getting element of shared_ptr by reference 2018-01-19 15:45:20 +08:00
Koncord
f1e2bc01f6 [General] Use floats instead of doubles for enchantmentCharge 2018-01-19 15:41:29 +08:00
Koncord
1e25a122c9 [General] Use RELIABLE_ORDERED only when jumping 2018-01-19 15:36:57 +08:00
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
2018-01-18 13:40:13 +02:00
Koncord
05fac2f67d [Browser] Disable connect button by default 2018-01-11 20:35:37 +08:00
Koncord
043eb224e2 [Browser] Cast QStrings to UTF-8 instead Latin1 2018-01-11 20:35:13 +08:00
Koncord
35b771b19e [Browser] Rework Connect window 2018-01-11 20:34:17 +08:00
David Cernat
92060bd6b6
Merge pull request #370 from TES3MP/0.6.2
Add 0.6.2 commits up to 10 Jan 2018
2018-01-10 03:51:07 +02:00
Koncord
1de9f30449 [Server] Fix iterating killed timers when kill() called in the callback
Instance new timers before new tick
2018-01-08 12:24:36 +08:00
David Cernat
45d3b24c17
Merge pull request #367 from Aesylwinn/LibIssues
Refine CMake scripts and fix g++ 5 build
2018-01-07 00:48:35 +02:00
Kyle Cooley
7248a5d037 Remove some duplication, standardize sol2 header inclusion 2018-01-05 20:25:57 -05:00
Kyle Cooley
ba4d2bd5fe Merge remote-tracking branch 'origin/master' into LibIssues 2018-01-05 17:35:47 -05:00
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
2018-01-05 21:24:14 +02:00
Kyle Cooley
fcd4d8b842 Fix build for gcc-5.4.0 2018-01-04 21:41:00 -05:00
Kyle Cooley
e2103d0bea Clean up find file for LuaJit, add one for Sol2 2018-01-04 21:40:17 -05:00
Koncord
35922e4898 [General] Change "enum ACTOR_ACTION" to "enum class Action" 2018-01-05 09:40:11 +08:00
Koncord
ef0384b296 [General] Simplify ProxyMasterPacket 2018-01-05 09:06:24 +08:00
Koncord
5b8f4f3e92 [Browser] Stop PingHelper on refresh 2018-01-05 08:38:40 +08:00
Koncord
647661daf9 [General] Minor fixes in Master packets 2018-01-05 08:38:40 +08:00
David Cernat
2f6e3b4cda [Documentation] Clarify status of version 0.7 2018-01-04 10:54:40 +02:00
Koncord
c4949ac5d9 [General] Change regular enums to enum class 2018-01-02 12:44:53 +08:00
Koncord
8f5d31cb03 [Server] Iterate only through updated players on each frame 2018-01-02 11:57:32 +08:00
Koncord
4ab338bbb1 [Server] Register ON_PLAYER_QUICKKEYS event 2018-01-02 11:53:03 +08:00
Koncord
5777759aae [General] Change enum QuickKey::QUICKKEY_TYPE to enum class 2018-01-02 11:52:38 +08:00
Koncord
2019128d92 [Server] Change signature of TimerController::kill, validate callback 2018-01-02 08:17:26 +08:00
David Cernat
51a92bcf8f [Client] Fix build 2018-01-01 13:10:57 +02:00
David Cernat
09958681cd Merge pull request #363 from TES3MP/0.6.2 while resolving conflicts, 2nd try 2018-01-01 12:04:25 +02:00
David Cernat
8bb764c6d6 [Build] Use C++14 instead of C++11, as the server requires the former 2017-12-27 21:09:16 +02:00
David Cernat
dddd2f1cc7 Merge pull request #361 from OpenMW/master while resolving conflicts
# Conflicts:
#	.travis.yml
2017-12-25 07:37:54 +02:00
David Cernat
a84c4c7ecc [Server] Print module order on startup 2017-12-10 15:57:19 +02:00
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.
2017-12-10 10:06:27 +02:00
Koncord
077a3d06b3 [General] Change type of BasePlayer::resurrectType to ResurrectType 2017-12-10 12:01:04 +08:00
Koncord
f9c4b847aa [Server] Make argument const reference 2017-12-10 11:46:50 +08:00
Koncord
c5388e49f2 [Server] Remove Pawn switcher in cmake 2017-12-10 10:29:10 +08:00
Koncord
ba07d7820f [Server] Remove Terra 2017-12-10 10:20:24 +08:00
Koncord
77d14211c9 [Server] Add Weather.cpp to build 2017-12-10 10:16:16 +08:00
Koncord
ecbe0127b0 [Client] Remove unused variable 2017-12-10 09:57:18 +08:00
Koncord
fd721143e2 [Client] Implement weather packet 2017-12-10 09:56:55 +08:00
Koncord
25b7095396 [Client] Share WeatherManager to the mwmp::Main class 2017-12-10 09:53:36 +08:00
Koncord
44dc153ebe [Server] Add Weather API 2017-12-10 09:51:03 +08:00
Koncord
1ef6ad6215 [General] Add weather struct and packet 2017-12-10 09:48:29 +08:00
Koncord
aff1859759 [Server] Add new line for help strings 2017-12-10 09:47:35 +08:00
Koncord
122a30c183 [General] Change type of refNumIndex & mpNum to unsigned 2017-12-10 09:20:13 +08:00
Koncord
2726d94d10 [Client] Disallow to load save game via launch options 2017-12-09 18:18:08 +08:00
Koncord
d98bb74b80 [Client] Disable autosave on waiting 2017-12-09 18:17:29 +08:00
Koncord
bfdf348a6c [Server] Fix Undefined behavior 2017-12-09 18:01:15 +08:00
Koncord
f11473da87 [Client] Remove WorldEvent::addObject(), use move semantics 2017-12-09 15:05:47 +08:00
Koncord
4d0072a74c [Server] Use foreach loop 2017-12-09 15:04:09 +08:00
Koncord
cd620e17ec [Server] Use std::move(tokens) 2017-12-09 15:03:44 +08:00
Koncord
dd352f0a91 [Server] Improve Player::sendToLoaded() 2017-12-09 15:03:06 +08:00
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
2017-12-09 14:59:41 +08:00
Koncord
585c24cee8 [Server] Optimize CellController 2017-12-09 11:44:56 +08:00
Koncord
ff8b5061b4 [Server] Modernize CellController singleton 2017-12-09 11:08:53 +08:00
Koncord
e97dac7793 [Server] Mark derived processor classes as final 2017-12-09 10:20:55 +08:00
Koncord
7748e582a8 [Client] Mark derived processor classes as final 2017-12-09 10:18:17 +08:00
Koncord
2cb0ea20f0 [General] Modernize packets
Use explicit for constructors
Use override instead virtual for inherited methods
Mark final derived classes as "final"
2017-12-09 10:13:27 +08:00
David Cernat
de77ee3126
Merge pull request #356 from OpenMW/master
Add OpenMW commits up to 7 Dec 2017
2017-12-08 23:08:34 +02:00
David Cernat
ad61d88cb1 [General] Fix typos and use consistent style for recent additions 2017-12-08 18:02:23 +02:00
Koncord
051f65a4d5 [Client] Make channel buttons blinking on new messages 2017-12-08 22:53:22 +08:00
Koncord
7eecbfd08e [Client] Fix saving channel history 2017-12-08 22:36:16 +08:00
Koncord
1c7330635b [Server] Add & register ON_PLAYER_WEATHER event 2017-12-08 20:38:36 +08:00
Koncord
392e645fe5 [Server] Add isMarkedForDeleteion function 2017-12-08 20:37:22 +08:00
Koncord
91398c5dcc [Server] Implicitly join to "Default" channel 2017-12-08 20:35:19 +08:00
Koncord
aa183e6844 [General] Introduce chat channels 2017-12-08 07:43:29 +08:00
Koncord
c55f0f73b8 [Server] Pass shared_ptr<Player> by reference 2017-12-08 07:32:49 +08:00
David Cernat
85a9181c12
Merge pull request #355 from OpenMW/master
Add OpenMW commits up to 6 Dec 2017
2017-12-06 18:32:59 +02:00
Koncord
94f3eaa980 [Server] Fix warning "implicit fallthrough" 2017-12-06 13:30:39 +08:00
David Cernat
456bcee68a [Server] Use Utils function as workaround for "bad exception" on Windows 2017-12-04 14:32:23 +02:00
David Cernat
cfb5835e17 [Server] Clean up method and variable names 2017-12-04 10:39:20 +02:00
Koncord
cd03d59056 [Client] Fix invalid read (memcheck warning) 2017-12-04 16:25:57 +08:00
David Cernat
2bc79fcdf4
Merge pull request #309 from TES3MP/new-script-api
Add new-script-api commits
2017-12-04 08:18:41 +02:00
David Cernat
c2f330d4f1
Merge pull request #354 from TES3MP/master
Add master commits up to 4 Dec 2017
2017-12-04 04:39:08 +02:00
David Cernat
fc5e883160 [General] Rework PlayerStatsDynamic packets so they are of minimal size 2017-12-02 18:29:30 +02:00
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.
2017-12-02 15:39:08 +02:00
David Cernat
d1ad0c91f8 [General] Rework PlayerEquipment packets so they are of minimal size
Moreover, rename BaseNetCreature's equipedItems into equipmentItems.
2017-11-30 12:31:54 +02:00
David Cernat
7e322f1f8b Merge pull request #352 from TES3MP/master while resolving conflicts
# Conflicts:
#	README.md
2017-11-30 10:10:38 +02:00
David Cernat
bd9e8bd10f [General] Simplify storing of attribute and skill index changes 2017-11-29 16:55:51 +02:00
David Cernat
711bdf187a [General] Add BaseNetCreature to CMakeLists in components 2017-11-29 16:11:15 +02:00
David Cernat
720ef5f6c5 [General] Use consistent code style 2017-11-29 13:00:22 +02:00
David Cernat
eff3504b05
Merge pull request #350 from TES3MP/master
Add master commits up to 29 Nov 2017
2017-11-29 04:02:24 +02:00
Koncord
b55bb445dd [General] Change underlying type of the WidgetType to uint8_t 2017-11-28 22:24:04 +08:00
Koncord
a546d99000 [General] Fix type 2017-11-28 22:10:27 +08:00
Koncord
1841562553 [General] Minor cleanup of the World Packets 2017-11-28 22:05:10 +08:00
Koncord
744b8cf168 [General] Minor cleanup of the Actor Packets 2017-11-28 21:51:34 +08:00
Koncord
e44fcdc0b3 [General] Cleanup Player packets 2017-11-28 21:38:45 +08:00
David Cernat
901fe72471 [Server] Fix variable shadowing in Player 2017-11-26 13:14:53 +02:00
David Cernat
a796f81444 [General] Add and use utility function for int value checks in vectors 2017-11-26 13:03:54 +02:00
Koncord
46d55816b8 [Client] Remove debug code 2017-11-26 02:14:20 +08:00
Koncord
4ebfcc4a21 [Server] Limit handshake attempts 2017-11-26 00:18:19 +08:00
Koncord
729f6e745e [Server] Remove unused get() method from EventController 2017-11-26 00:00:31 +08:00
Koncord
fef3764eb1 [General] Add forgotten files to build 2017-11-25 23:55:05 +08:00
Koncord
382f56178c [Client] Add custom windows 2017-11-25 23:50:14 +08:00
Koncord
e657934cef [Server] Add custom window API 2017-11-25 23:47:02 +08:00
Koncord
c276ff4bd9 [General] Add packet GUIWindow 2017-11-25 23:37:08 +08:00
David Cernat
0a35b897be
Merge pull request #348 from TES3MP/master
Add master commits up to 25 Nov 2017
2017-11-25 08:55:18 +02:00
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.
2017-11-24 14:28:05 +02:00
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.
2017-11-24 12:38:42 +02:00
David Cernat
64b57983f0 [General] Add TRACE log messages in player processors 2017-11-24 09:43:45 +02:00
David Cernat
606ddff813
Merge pull request #346 from TES3MP/master
Add master commits up to 24 Nov 2017
2017-11-24 05:47:33 +02:00
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.
2017-11-23 10:07:14 +02:00
David Cernat
07d75abdf8 [Server] Use consistent order for includes 2017-11-23 08:11:37 +02:00
David Cernat
1ee460bba8
Merge pull request #344 from TES3MP/master
Add master commits up to 23 Nov 2017
2017-11-23 04:53:27 +02:00
David Cernat
d33254f287 [Server] Rename CharClass' isDefault() into isCustom()
It was already using the logic of isCustom() by mistake.
2017-11-19 12:35:11 +02:00
David Cernat
010a80ceca [Server] Place getters and setters in consistent order 2017-11-19 11:34:03 +02:00
David Cernat
947b3f76be [Server] Replace Player's isMale() and setIsMale() with gender property
For simplicity and clarity.
2017-11-19 11:06:35 +02:00
David Cernat
6f822f54aa [Server] Make chat commands case insensitive 2017-11-19 10:12:35 +02:00
David Cernat
a3e2ab4d4e [Server] Send correct packet for inventory changes 2017-11-19 04:24:54 +02:00
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.
2017-11-19 00:59:32 +02:00
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.
2017-11-18 10:02:52 +02:00
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.
2017-11-18 03:55:17 +02:00
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.
2017-11-18 01:47:18 +02:00
David Cernat
ba161ddddd [Server] Make a few function names more consistent and fix typos 2017-11-17 04:12:25 +02:00
David Cernat
7788821a69
Merge pull request #340 from TES3MP/master
Add master commits up to 17 Nov 2017
2017-11-17 01:28:07 +02:00
David Cernat
068f733d1e
Merge pull request #338 from TES3MP/master
Add master commits up to 16 Nov 2017
2017-11-16 08:50:27 +02:00
David Cernat
1272b03f25 [Server] Fix typo in player script function 2017-11-13 07:43:20 +02:00
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.
2017-11-13 05:38:56 +02:00
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.
2017-11-13 05:33:27 +02:00
David Cernat
ac7a588632 [General] Update config files 2017-11-13 02:18:58 +02:00
David Cernat
a21f5d18d6
Merge pull request #336 from TES3MP/master
Add master commits up to 12 Nov 2017
2017-11-12 06:37:34 +02:00
David Cernat
a8261bb385 [General] Fix printing of packet identifiers after changes to logger 2017-11-12 02:36:18 +02:00
David Cernat
700e4d032e
Merge pull request #334 from TES3MP/master
Add master commits up to 11 Nov 2017
2017-11-11 03:47:22 +02:00
David Cernat
4dbada69bf Merge pull request #332 from TES3MP/master while resolving conflicts 2017-11-10 08:50:28 +02:00
Koncord
ca7f3f7450 [Client] Disable focus on <tab> for chat window 2017-11-07 11:44:08 +08:00
Koncord
64b531aa3c [Server] Remove redundant argument 2017-11-07 06:37:09 +08:00
Koncord
f377164db9 [Client] Fix build 2017-11-07 05:35:23 +08:00
Koncord
7ab01b66e4 [General] Rewrite Log class 2017-11-07 05:34:55 +08:00
Koncord
d15c674584 [General] Move getFilenameTimestamp() to Utils 2017-11-07 05:33:10 +08:00
Koncord
0da44f69ad [Server] Isolate getModFolder() & getDataFolder() 2017-11-03 06:36:35 +08:00
Koncord
062d6a1824 [Server] Add sandboxed import() function 2017-11-03 06:36:35 +08:00
David Cernat
29cb51cdce [Server] Enable SOL_SAFE_USERTYPE for both Debug and RelWithDebInfo 2017-11-01 18:09:25 +02:00
Koncord
1d111fdca8 [General] Update sol submodule to 2.18.5 2017-11-01 22:27:52 +08:00
Koncord
bd7082f57e [Server] Use custom Lua error handler not only on Windows
For some reason sol's default error handler does not wroking properly
2017-11-01 21:48:42 +08:00
David Cernat
71c921faa7 [Server] Rename property cell into description, initialize Cells type 2017-11-01 13:22:41 +02:00
David Cernat
5653d07c7b [Server] Fix build on Windows 2017-10-31 22:00:03 +02:00
Koncord
948090676a [Server] Impove Lua Error handler for Windows 2017-11-01 01:17:11 +08:00
David Cernat
61db22f5ae
Merge pull request #329 from TES3MP/master
Add master commits up to 31 Oct 2017
2017-10-31 06:31:49 +02:00
David Cernat
b7e5e77166 [Server] Fix getCaseInsensitiveFilename, simplify Players.size() 2017-10-28 03:58:53 +03:00
David Cernat
378d30834b [Server] Add special error handler for Sol back in, but only for Windows 2017-10-28 01:44:02 +03:00
Koncord
14d47213ef [Server] Add Players.size() to Lua API 2017-10-28 02:34:45 +08:00
Koncord
3495fd43f4 [Client] Add network statistics (CTRL+F2) 2017-10-28 00:39:46 +08:00
Koncord
e7a5919477 [Server] Fix path to native libs 2017-10-27 20:58:02 +08:00
Koncord
1aa630e4a9 [Server] Add StackWalker for Windows 2017-10-27 05:27:07 +08:00
Koncord
e8915f8ec5 [Client] Fix build 2017-10-27 01:33:01 +08:00
Koncord
14fdec2478 [Server] Add forgotten stacktrace.cpp 2017-10-26 23:24:35 +08:00
David Cernat
b801cf2c9e Merge pull request #327 from TES3MP/master
Add master commits up to 26 Oct 2017
2017-10-26 17:34:00 +03:00
David Cernat
878294e4fe Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 2017-10-26 17:25:53 +03:00
Koncord
d44848ecbb [Server] Fix build 2017-10-25 16:05:45 +08:00
Koncord
05abb8ace3 [Server] Add Log level constants to lua 2017-10-25 15:22:07 +08:00
Koncord
04a844a9c0 [Server] Use sol's default_handler 2017-10-25 15:21:11 +08:00
Koncord
dad0b38f25 [Server] Add custom terminate handler with stacktrace 2017-10-25 14:45:36 +08:00
Koncord
a3d5fbbdcd [Server] Add stacktrace 2017-10-25 14:45:36 +08:00
Koncord
916ada108f [General] Modernize Log utility
* Reverse Log levels
* Add LOG_TRACE
* Spawn instance of Log in Get() function
2017-10-25 14:45:36 +08:00
David Cernat
bece095579 [Server] Add getCaseInsensitiveFilename script function back in 2017-10-24 20:25:02 +03:00
David Cernat
d6dc75e94b Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 2017-10-24 19:45:16 +03:00
David Cernat
76a4abd7c0 Merge pull request #325 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/processors/player/ProcessorPlayerTopic.hpp
2017-10-24 19:45:11 +03:00
David Cernat
0e73571111 Merge pull request #322 from TES3MP/master
Add master commits up to 23 Oct 2017
2017-10-23 14:52:13 +03:00
David Cernat
8a93631e08 Merge pull request #320 from TES3MP/master
Add master commits up to 22 Oct 2017
2017-10-22 20:31:43 +03:00
Koncord
ba8613a179 [Browser] Add "no password" filter to browser 2017-10-21 12:42:38 +08:00
David Cernat
fb67180809 [Server] Fix build in Visual Studio 2017-10-19 13:04:37 +03:00
Koncord
4530370e52 [Server] Use old style of Server Plugins location 2017-10-19 11:55:49 +08:00
Koncord
ce6a4e4032 [Server] Fix indents 2017-10-19 10:26:03 +08:00
Koncord
fc3f2483ee [Server] Add manual Server Plugins sort 2017-10-19 10:24:17 +08:00
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, "")
2017-10-18 22:32:03 +08:00
Koncord
7a0b45d456 [Server] Load mods in dependencies order 2017-10-18 21:30:36 +08:00
Koncord
66283943c5 [Browser] Fix Clang warnings 2017-10-18 21:30:36 +08:00
Koncord
d702845026 [General] Fix Clang warnings in Log.hpp 2017-10-18 21:30:36 +08:00
Koncord
0a0c9893b1 [General] Update sol submodule 2017-10-18 21:30:36 +08:00
David Cernat
6e7c033a5d Merge pull request #318 from TES3MP/master
Add master commits up to 18 Oct 2017
2017-10-18 15:14:37 +03:00
David Cernat
bbac26294f [Server] Fix typos and make all files end with newlines 2017-10-17 07:44:10 +03:00
David Cernat
b20df4b7cd Merge pull request #316 from TES3MP/master
Add master commits up to 17 Oct 2017
2017-10-17 05:15:02 +03:00
David Cernat
17a8e32782 Merge pull request #314 from TES3MP/master
Add master commits up to 16 Oct 2017
2017-10-16 07:47:37 +03:00
Koncord
1fd16ba69c [Browser] Correctly init parent class 2017-10-11 06:35:22 +08:00
Koncord
62588ce088 [Server] Minor fixes 2017-10-11 06:26:09 +08:00
Koncord
6cb9c3c713 [General] Remove explicit specifier 2017-10-11 06:17:02 +08:00
Koncord
3839a2dcfd [Browser] Remove unused variables 2017-10-11 05:46:39 +08:00
Koncord
ed75563a94 [Browser] Do not show "Unreachable" servers when ping filter is enabled 2017-10-11 05:36:31 +08:00
Koncord
01a5196a92 [Browser] Mark few strings translatable 2017-10-10 13:11:36 +08:00
Koncord
15723adb9a [Browser] Make sort servers case insensitive 2017-10-10 13:11:36 +08:00
Koncord
57353cdfff [Browser] Mark unreachable servers as "Unreachable" instead 999 2017-10-10 13:11:36 +08:00
David Cernat
fe9a3088bd Merge pull request #312 from TES3MP/master
Add master commits up to 10 Oct 2017
2017-10-10 06:27:35 +03:00
Koncord
5c79e7106f [Browser] Minor improvements 2017-10-10 07:17:41 +08:00
David Cernat
4845599bda Merge pull request #308 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/main.cpp
2017-10-08 06:50:15 +03:00
David Cernat
7a38a0b223 Merge pull request #305 from TES3MP/master
Add master commits up to 6 Oct 2017
2017-10-06 19:19:49 +03:00
Koncord
cbabc91b06 [Server] Stop MasterClient thread and inform server owner on ban 2017-10-03 03:17:25 +08:00
Koncord
846f83e3e4 [Master] Use RakNet ban system instead homebrew 2017-10-03 03:17:25 +08:00
David Cernat
841a4f90c1 Merge pull request #301 from TES3MP/master
Add master commits up to 28 Sep 2017
2017-09-28 11:13:51 +03:00
David Cernat
3284769fef [Server] Add getModFolder() script function and Config environment 2017-09-23 13:52:51 +03:00
David Cernat
b5ce3cebbc Merge pull request #297 from TES3MP/master
Add master commits up to 22 Sep 2017
2017-09-22 19:15:42 +03:00
Koncord
73aa83aa03 [Master] Use RakNet ban system with homebrew bansystem 2017-09-21 16:30:05 +08:00
Koncord
5fcdff843c [Master] Minor fixes in lua scripts 2017-09-21 16:30:05 +08:00
David Cernat
76f1a61538 Merge pull request #295 from TES3MP/master
Add master commits up to 20 Sep 2017
2017-09-20 16:56:46 +03:00
David Cernat
d591180e99 [Server] Clean up logAppend and add new message for server shutdown 2017-09-20 16:30:12 +03:00
David Cernat
381a5fabc4 Merge pull request #291 from TES3MP/master
Add master commits up to 17 Sep 2017
2017-09-17 11:29:40 +03:00
David Cernat
9838cc680a [Server] Enable Lua debugging for RelWithDebInfo builds 2017-09-15 08:19:15 +03:00
David Cernat
98195b5e3c Merge pull request #289 from TES3MP/master
Add master commits up to 15 Sep 2017
2017-09-15 03:32:20 +03:00
Koncord
3124e627cf [General] Move hack back to the right place 2017-09-13 21:30:16 +08:00
Koncord
c2286482ae [Master] Add 'savebans' command to RESTful API 2017-09-13 20:33:42 +08:00
Koncord
f814ae795d [Master] Fix return from isServerValid()
Temporary accept all servers in OnServerAnnnounce
2017-09-13 19:11:58 +08:00
Koncord
50ef9677fe [Master] Fix master.lua
Remove unban() from loadBans()
Disable strict json order from saveBans()
2017-09-13 17:59:07 +08:00
Koncord
838e05521e [Master] Minor fixes 2017-09-13 17:56:38 +08:00
David Cernat
cccbe753d7 [General] Update version to 0.7-alpha 2017-09-13 10:29:36 +03:00
Koncord
6decd148e5 [Server] Fix build on Windows 2017-09-13 11:06:53 +08:00
Koncord
045dc566ea Merge branch 'master' into new-script-api 2017-09-12 21:41:25 +08:00
Koncord
bb183457a6 [Master] Init BanAddress/UnbanAddress before opening script 2017-09-12 21:30:37 +08:00
Koncord
dc18916f46 [Master] Add isServerValid lambda. Add "OnServerAnnounce" callback 2017-09-12 21:29:42 +08:00
Koncord
f99dafbf51 [Master] Add scripts for generating certificates for testing purposes 2017-09-12 21:27:07 +08:00
Koncord
aee6fb1265 [Master] Add master lua scripts 2017-09-12 21:24:06 +08:00
Koncord
d1388cdf84 [Master] Add Admin Rest Server
Add admin rest callback - "OnAdminRequest". Called on every POST request
2017-09-12 20:56:21 +08:00
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
2017-09-12 20:50:28 +08:00
Koncord
0e2817da88 [Master] Move response stuff to ResponseUtils.hpp 2017-09-12 20:39:27 +08:00
Koncord
fc8232f943 [Master] Add ban/unban functions 2017-09-12 20:24:50 +08:00
Koncord
26324c2578 [Master] Change Ban structure to vector<string> 2017-09-12 20:23:17 +08:00
Koncord
4e93905350 [Master] Backport SimpleWeb 2017-09-12 20:21:32 +08:00
Koncord
510e657c93 Merge branch 'master' into new-script-api 2017-09-02 19:48:44 +08:00
Koncord
060ebe3d4a [Server] Init isWerewolf variable
Comment unused code
2017-09-02 19:45:19 +08:00
Koncord
1c0adc47ee [Server] Fix [get/set]MajorSkills and [get/set]MinorSkills 2017-08-31 20:51:05 +08:00
Koncord
66fdba957b [Server] Add getDataFolder() to Script API 2017-08-31 16:25:43 +08:00
Koncord
1d16958910 [Server] Fix multiple calls of ON_POST_INIT 2017-08-31 16:20:50 +08:00
Koncord
b18c6dec9d [Server] Fix sendList()
Remove return from requestContainers()
Rename "Cells" usertype to "Cell"
2017-08-30 22:50:47 +08:00
Koncord
991a1fe8d8 [General] Update submodules 2017-08-30 17:10:37 +08:00
Koncord
60fc0bedb8 [Server] Use lower case for methods 2017-08-30 13:56:53 +08:00
Koncord
7717f9bece [Server] Replace getKillRefId/getKillNumber with getKill 2017-08-30 13:56:22 +08:00
Koncord
fe2dd1bad4 [Server] Use correct cells in "send" functions
Remove unused return type from ON_PLAYER_SENDMESSAGE
2017-08-29 03:34:36 +08:00
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
2017-08-28 00:15:56 +08:00
945 changed files with 17501 additions and 40618 deletions

View file

@ -9,8 +9,3 @@ insert_final_newline = true
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
insert_final_newline = true insert_final_newline = true
[*.glsl]
indent_style = space
indent_size = 4
insert_final_newline = false

View file

@ -1,69 +0,0 @@
stages:
- build
Debian:
tags:
- docker
- linux
image: gcc
cache:
key: apt-cache
paths:
- apt-cache/
before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- apt-get update -yq
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
stage: build
script:
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
- make -j$cores_to_use
- DESTDIR=artifacts make install
artifacts:
paths:
- build/artifacts/
MacOS:
tags:
- macos
- xcode
except:
- branches # because our CI VMs are not public, MRs can't use them and timeout
stage: build
allow_failure: true
script:
- rm -fr build/* # remove anything in the build directory
- CI/before_install.osx.sh
- CI/before_script.osx.sh
- cd build; make -j2 package
artifacts:
paths:
- build/OpenMW-*.dmg
Windows:
tags:
- win10
- msvc2017
except:
- branches # because our CI VMs are not public, MRs can't use them and timeout
stage: build
allow_failure: true
script:
# - env # turn on for debugging
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
cache:
paths:
- deps
artifacts:
paths:
- "*.zip"

3
.gitmodules vendored
View file

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

View file

@ -1,10 +1,6 @@
os:
- linux
# - osx
osx_image: xcode9.4
language: cpp language: cpp
sudo: required sudo: required
dist: xenial dist: trusty
branches: branches:
only: only:
- master - master
@ -15,18 +11,19 @@ env:
global: global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key # via the "travis encrypt" command using the project repo's public key
- secure: 1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ= - secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
- macos_qt_formula=qt
addons: addons:
apt: apt:
sources: sources:
- sourceline: 'ppa:openmw/openmw' - sourceline: 'ppa:openmw/openmw'
- sourceline: 'ppa:rakhimov/boost' - sourceline: 'ppa:rakhimov/boost'
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.8
packages: [ packages: [
# Dev # Dev
cmake, clang-6.0, libunshield-dev, libtinyxml-dev, cmake, clang-3.8, libunshield-dev, libtinyxml-dev,
g++-8, g++-6,
# Tests # Tests
libgtest-dev, google-mock, libgtest-dev, google-mock,
# Boost # Boost
@ -45,7 +42,7 @@ addons:
project: project:
name: "TES3MP/openmw-tes3mp" name: "TES3MP/openmw-tes3mp"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: koncord@tes3mp.com notification_email: stas5978@gmail.com
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE" build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
build_command: "make -j3" build_command: "make -j3"
branch_pattern: coverity_scan branch_pattern: coverity_scan
@ -53,21 +50,19 @@ matrix:
include: include:
- os: linux - os: linux
env: env:
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 " - ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0" - MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
compiler: clang compiler: clang
- os: linux - os: linux
env: env:
- MATRIX_CC="CC=gcc-8 && CXX=g++-8" - MATRIX_CC="CC=gcc-6 && CXX=g++-6"
- os: linux - os: linux
env: env:
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0" - MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
allow_failures: allow_failures:
- env: - env:
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0" - ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
- env: - MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
before_install: before_install:
- ./CI/before_install.${TRAVIS_OS_NAME}.sh - ./CI/before_install.${TRAVIS_OS_NAME}.sh

View file

@ -22,7 +22,6 @@ Programmers
alexanderkjall alexanderkjall
Alexander Nadeau (wareya) Alexander Nadeau (wareya)
Alexander Olofsson (Ace) Alexander Olofsson (Ace)
Alex S (docwest)
Allofich Allofich
Andrei Kortunov (akortunov) Andrei Kortunov (akortunov)
AnyOldName3 AnyOldName3
@ -38,7 +37,6 @@ Programmers
Britt Mathis (galdor557) Britt Mathis (galdor557)
Capostrophic Capostrophic
cc9cii cc9cii
Cédric Mocquillon
Chris Boyce (slothlife) Chris Boyce (slothlife)
Chris Robinson (KittyCat) Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen) Cory F. Cohen (cfcohen)
@ -64,8 +62,6 @@ Programmers
Evgeniy Mineev (sandstranger) Evgeniy Mineev (sandstranger)
Federico Guerra (FedeWar) Federico Guerra (FedeWar)
Fil Krynicki (filkry) Fil Krynicki (filkry)
Finbar Crago(finbar-crago)
Florian Weber (Florianjw)
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
Hallfaer Tuilinn Hallfaer Tuilinn
@ -143,7 +139,6 @@ Programmers
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (kpp) Roman Proskuryakov (kpp)
Roman Siromakha (elsid)
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
scrawl scrawl
@ -176,7 +171,6 @@ Programmers
Documentation Documentation
------------- -------------
Adam Bowen (adamnbowen)
Alejandro Sanchez (HiPhish) Alejandro Sanchez (HiPhish)
Bodillium Bodillium
Bret Curtis (psi29a) Bret Curtis (psi29a)

View file

@ -1,114 +1,8 @@
0.45.0 0.45.0
------ ------
Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
Bug #2626: Resurrecting the player does not resume the game
Bug #2772: Non-existing class or faction freezes the game
Bug #2835: Player able to slowly move when overencumbered
Bug #2852: No murder bounty when a player follower commits murder
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
Bug #2872: Tab completion in console doesn't work with explicit reference
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3249: Fixed revert function not updating views properly
Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work
Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning
Bug #3876: Landscape texture painting is misaligned
Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
Bug #3993: Terrain texture blending map is not upscaled
Bug #3997: Almalexia doesn't pace
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts
Bug #4125: OpenMW logo cropped on bugtracker
Bug #4215: OpenMW shows book text after last EOL tag
Bug #4221: Characters get stuck in V-shaped terrain
Bug #4230: AiTravel package issues break some Tribunal quests
Bug #4251: Stationary NPCs do not return to their position after combat
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
Bug #4286: Scripted animations can be interrupted
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4293: Faction members are not aware of faction ownerships in barter
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4311: OpenMW does not handle RootCollisionNode correctly
Bug #4327: Missing animations during spell/weapon stance switching
Bug #4358: Running animation is interrupted when magic mode is toggled
Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4378: On-self absorb spells restore stats
Bug #4393: NPCs walk back to where they were after using ResetActors
Bug #4416: Handle exception if we try to play non-music file
Bug #4419: MRK NiStringExtraData is handled incorrectly
Bug #4426: RotateWorld behavior is incorrect Bug #4426: RotateWorld behavior is incorrect
Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2
Bug #4431: "Lock 0" console command is a no-op
Bug #4432: Guards behaviour is incorrect if they do not have AI packages
Bug #4433: Guard behaviour is incorrect with Alarm = 0 Bug #4433: Guard behaviour is incorrect with Alarm = 0
Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax
Bug #4452: Default terrain texture bleeds through texture transitions
Bug #4453: Quick keys behaviour is invalid for equipment
Bug #4454: AI opens doors too slow
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
Bug #4458: AiWander console command handles idle chances incorrectly
Bug #4459: NotCell dialogue condition doesn't support partial matches
Bug #4460: Script function "Equip" doesn't bypass beast restrictions
Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
Bug #4489: Goodbye doesn't block dialogue hyperlinks
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
Bug #4494: Training cap based off Base Skill instead of Modified Skill
Bug #4495: Crossbow animations blending is buggy
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
Bug #4497: File names starting with x or X are not classified as animation
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
Bug #4519: Knockdown does not discard movement in the 1st-person mode
Bug #4539: Paper Doll is affected by GUI scaling
Bug #4545: Creatures flee from werewolves
Bug #4551: Replace 0 sound range with default range separately
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed
Bug #4557: Topics with reserved names are handled differently from vanilla
Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive
Bug #4563: Fast travel price logic checks destination cell instead of service actor cell
Bug #4565: Underwater view distance should be limited
Bug #4573: Player uses headtracking in the 1st-person mode
Bug #4574: Player turning animations are twitchy
Bug #4575: Weird result of attack animation blending with movement animations
Bug #4576: Reset of idle animations when attack can not be started
Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script
Feature #3103: Provide option for disposition to get increased by successful trade
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window
Feature #3703: Ranged sneak attack criticals
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
Feature #4488: Make water shader rougher during rain
Feature #4509: Show count of enchanted items in stack in the spells list
Feature #4512: Editor: Use markers for lights and creatures levelled lists
Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill
Feature #4549: Weapon priority: use the actual damage in weapon rating calculations
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test
0.44.0 0.44.0
------ ------
@ -194,7 +88,6 @@
Bug #4412: openmw-iniimporter ignores data paths from config Bug #4412: openmw-iniimporter ignores data paths from config
Bug #4413: Moving with 0 strength uses all of your fatigue Bug #4413: Moving with 0 strength uses all of your fatigue
Bug #4420: Camera flickering when I open up and close menus while sneaking Bug #4420: Camera flickering when I open up and close menus while sneaking
Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK
Bug #4435: Item health is considered a signed integer Bug #4435: Item health is considered a signed integer
Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game
Feature #1786: Round up encumbrance value in the encumbrance bar Feature #1786: Round up encumbrance value in the encumbrance bar

View file

@ -15,8 +15,7 @@ sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
cd ~/ cd ~/
git clone https://github.com/TES3MP/CrabNet git clone https://github.com/TES3MP/RakNet
cd CrabNet cd RakNet
cmake . -DCRABNET_ENABLE_DLL=OFF -DCRABNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
make -j3 make -j3

View file

@ -4,7 +4,7 @@ brew update
brew outdated cmake || brew upgrade cmake brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig brew outdated pkgconfig || brew upgrade pkgconfig
brew install qt brew install $macos_qt_formula
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

View file

@ -9,7 +9,7 @@ if [ ! -z "${MATRIX_CC}" ]; then
eval "${MATRIX_CC}" eval "${MATRIX_CC}"
fi fi
export RAKNET_ROOT=~/CrabNet export RAKNET_ROOT=~/RakNet
export CODE_COVERAGE=0 export CODE_COVERAGE=0
if [ ! -z "${ANALYZE}" ]; then if [ ! -z "${ANALYZE}" ]; then
@ -35,5 +35,5 @@ ${ANALYZE}cmake .. \
-DBINDIR=/usr/games \ -DBINDIR=/usr/games \
-DCMAKE_BUILD_TYPE="None" \ -DCMAKE_BUILD_TYPE="None" \
-DUSE_SYSTEM_TINYXML=TRUE \ -DUSE_SYSTEM_TINYXML=TRUE \
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \ -DRakNet_LIBRARY_RELEASE=~/RakNet/lib/libRakNetLibStatic.a \
-DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a -DRakNet_LIBRARY_DEBUG=~/RakNet/lib/libRakNetLibStatic.a \

View file

@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
# set -x # turn-on for debugging
MISSINGTOOLS=0 MISSINGTOOLS=0
@ -77,6 +76,7 @@ while [ $# -gt 0 ]; do
h ) h )
cat <<EOF cat <<EOF
Usage: $0 [-cdehkpuvV] Usage: $0 [-cdehkpuvV]
Options: Options:
-c <Release/Debug> -c <Release/Debug>
Set the configuration, can also be set with environment variable CONFIGURATION. Set the configuration, can also be set with environment variable CONFIGURATION.
@ -232,9 +232,10 @@ fi
case $VS_VERSION in case $VS_VERSION in
15|15.0|2017 ) 15|15.0|2017 )
GENERATOR="Visual Studio 15 2017" GENERATOR="Visual Studio 15 2017"
TOOLSET="vc141" TOOLSET="vc140"
TOOLSET_REAL="vc141"
MSVC_REAL_VER="15" MSVC_REAL_VER="15"
MSVC_VER="14.1" MSVC_VER="14"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2017" MSVC_DISPLAY_YEAR="2017"
;; ;;
@ -242,8 +243,9 @@ case $VS_VERSION in
14|14.0|2015 ) 14|14.0|2015 )
GENERATOR="Visual Studio 14 2015" GENERATOR="Visual Studio 14 2015"
TOOLSET="vc140" TOOLSET="vc140"
TOOLSET_REAL="vc140"
MSVC_REAL_VER="14" MSVC_REAL_VER="14"
MSVC_VER="14.0" MSVC_VER="14"
MSVC_YEAR="2015" MSVC_YEAR="2015"
MSVC_DISPLAY_YEAR="2015" MSVC_DISPLAY_YEAR="2015"
;; ;;
@ -251,8 +253,9 @@ case $VS_VERSION in
12|12.0|2013 ) 12|12.0|2013 )
GENERATOR="Visual Studio 12 2013" GENERATOR="Visual Studio 12 2013"
TOOLSET="vc120" TOOLSET="vc120"
TOOLSET_REAL="vc120"
MSVC_REAL_VER="12" MSVC_REAL_VER="12"
MSVC_VER="12.0" MSVC_VER="12"
MSVC_YEAR="2013" MSVC_YEAR="2013"
MSVC_DISPLAY_YEAR="2013" MSVC_DISPLAY_YEAR="2013"
;; ;;
@ -322,9 +325,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
download "Boost 1.67.0" \ download "Boost 1.61.0" \
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \ "https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" "boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
fi fi
# Bullet # Bullet
@ -362,8 +365,8 @@ if [ -z $SKIP_DOWNLOAD ]; then
QT_SUFFIX="" QT_SUFFIX=""
fi fi
download "Qt 5.7.0" \ download "Qt 5.7.2" \
"https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ "https://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \ "qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \ "https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
"qt-5-install.qs" "qt-5-install.qs"
@ -400,9 +403,9 @@ echo
# Boost # Boost
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Boost 1.67.0... " printf "Boost 1.61.0... "
else else
if [ $MSVC_VER -eq 12.0 ]; then if [ $MSVC_VER -eq 12 ]; then
printf "Boost 1.58.0 AppVeyor... " printf "Boost 1.58.0 AppVeyor... "
else else
printf "Boost 1.67.0 AppVeyor... " printf "Boost 1.67.0 AppVeyor... "
@ -414,28 +417,17 @@ fi
BOOST_SDK="$(real_pwd)/Boost" BOOST_SDK="$(real_pwd)/Boost"
# Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names if [ -d Boost ] && grep "BOOST_VERSION 106100" Boost/boost/version.hpp > /dev/null; then
# We work around this by installing to root of the current working drive and then move it to our deps
# get the current working drive's root, we'll install to that temporarily
CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp"
CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
if [ -d CWD_DRIVE_ROOT_BASH ]; then
printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
exit 1;
fi
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Boost rm -rf Boost
CI_EXTRA_INNO_OPTIONS="" "${DEPS}/boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe" //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
[ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
"${DEPS}/boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
fi fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done. echo Done.
else else
# Appveyor unstable has all the boost we need already # Appveyor unstable has all the boost we need already
@ -452,17 +444,19 @@ fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET_REAL}"
echo Done. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo echo
# Bullet # Bullet
printf "Bullet 2.86... " printf "Bullet 2.86... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d Bullet ]; then if [ -d Bullet ]; then
printf -- "Exists. (No version checking) " printf -- "Exists. (No version checking) "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
@ -470,38 +464,49 @@ printf "Bullet 2.86... "
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
fi fi
export BULLET_ROOT="$(real_pwd)/Bullet" export BULLET_ROOT="$(real_pwd)/Bullet"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# FFmpeg # FFmpeg
printf "FFmpeg 3.2.4... " printf "FFmpeg 3.2.4... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf FFmpeg rm -rf FFmpeg
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/ cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
rm -rf "ffmpeg-3.2.4-win${BITS}-dev" rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
fi fi
export FFMPEG_HOME="$(real_pwd)/FFmpeg" export FFMPEG_HOME="$(real_pwd)/FFmpeg"
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
if [ $BITS -eq 32 ]; then if [ $BITS -eq 32 ]; then
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\"" add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
fi fi
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# MyGUI # MyGUI
printf "MyGUI 3.2.2... " printf "MyGUI 3.2.2... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d MyGUI ] && \ if [ -d MyGUI ] && \
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \ grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
@ -513,17 +518,21 @@ printf "MyGUI 3.2.2... "
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
fi fi
export MYGUI_HOME="$(real_pwd)/MyGUI" export MYGUI_HOME="$(real_pwd)/MyGUI"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="_d" SUFFIX="_d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll" add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OpenAL # OpenAL
printf "OpenAL-Soft 1.17.2... " printf "OpenAL-Soft 1.17.2... "
{ {
@ -533,18 +542,24 @@ printf "OpenAL-Soft 1.17.2... "
rm -rf openal-soft-1.17.2-bin rm -rf openal-soft-1.17.2-bin
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
fi fi
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin" OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# OSG # OSG
printf "OSG 3.4.1-scrawl... " printf "OSG 3.4.1-scrawl... "
{ {
cd $DEPS_INSTALL cd $DEPS_INSTALL
if [ -d OSG ] && \ if [ -d OSG ] && \
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \ grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
@ -556,21 +571,28 @@ printf "OSG 3.4.1-scrawl... "
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
fi fi
OSG_SDK="$(real_pwd)/OSG" OSG_SDK="$(real_pwd)/OSG"
add_cmake_opts -DOSG_DIR="$OSG_SDK" add_cmake_opts -DOSG_DIR="$OSG_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \ add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll "$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
echo Done. echo Done.
} }
cd $DEPS cd $DEPS
echo echo
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
printf "Qt 5.7.0... " printf "Qt 5.7.0... "
@ -583,53 +605,71 @@ fi
else else
SUFFIX="" SUFFIX=""
fi fi
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
cd $DEPS_INSTALL cd $DEPS_INSTALL
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}" QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
rm -rf Qt rm -rf Qt
cp "${DEPS}/qt-5-install.qs" qt-install.qs cp "${DEPS}/qt-5-install.qs" qt-install.qs
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
printf -- "(Installation might take a while) " printf -- "(Installation might take a while) "
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent "${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
mv qt-install.qs Qt/ mv qt-install.qs Qt/
echo Done. echo Done.
printf " Cleaning up extraneous data... " printf " Cleaning up extraneous data... "
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs} rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
fi fi
cd $QT_SDK cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
else else
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}" QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DDESIRED_QT_VERSION=5 \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
else else
SUFFIX="" SUFFIX=""
fi fi
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,") DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll" add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
echo Done. echo Done.
fi fi
} }
cd $DEPS cd $DEPS
echo echo
# SDL2 # SDL2
printf "SDL 2.0.7... " printf "SDL 2.0.7... "
{ {
@ -639,18 +679,26 @@ printf "SDL 2.0.7... "
rm -rf SDL2-2.0.7 rm -rf SDL2-2.0.7
eval 7z x -y SDL2-2.0.7.zip $STRIP eval 7z x -y SDL2-2.0.7.zip $STRIP
fi fi
export SDL2DIR="$(real_pwd)/SDL2-2.0.7" export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll" add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
echo Done. echo Done.
} }
echo echo
cd $DEPS_INSTALL/.. cd $DEPS_INSTALL/..
echo echo
echo "Setting up OpenMW build..." echo "Setting up OpenMW build..."
add_cmake_opts -DBUILD_BSATOOL=no \ add_cmake_opts -DBUILD_BSATOOL=no \
-DBUILD_ESMTOOL=no \ -DBUILD_ESMTOOL=no \
-DBUILD_MYGUI_PLUGIN=no \ -DBUILD_MYGUI_PLUGIN=no \
-DOPENMW_MP_BUILD=on -DOPENMW_MP_BUILD=on
if [ ! -z $CI ]; then if [ ! -z $CI ]; then
case $STEP in case $STEP in
components ) components )
@ -662,6 +710,7 @@ if [ ! -z $CI ]; then
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
openmw ) openmw )
echo " Building subproject: OpenMW." echo " Building subproject: OpenMW."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
@ -670,6 +719,7 @@ if [ ! -z $CI ]; then
-DBUILD_OPENCS=no \ -DBUILD_OPENCS=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
opencs ) opencs )
echo " Building subproject: OpenCS." echo " Building subproject: OpenCS."
add_cmake_opts -DBUILD_ESSIMPORTER=no \ add_cmake_opts -DBUILD_ESSIMPORTER=no \
@ -678,6 +728,7 @@ if [ ! -z $CI ]; then
-DBUILD_OPENMW=no \ -DBUILD_OPENMW=no \
-DBUILD_WIZARD=no -DBUILD_WIZARD=no
;; ;;
misc ) misc )
echo " Building subprojects: Misc." echo " Building subprojects: Misc."
add_cmake_opts -DBUILD_OPENCS=no \ add_cmake_opts -DBUILD_OPENCS=no \
@ -685,6 +736,7 @@ if [ ! -z $CI ]; then
;; ;;
esac esac
fi fi
# NOTE: Disable this when/if we want to run test cases # NOTE: Disable this when/if we want to run test cases
#if [ -z $CI ]; then #if [ -z $CI ]; then
echo "- Copying Runtime DLLs..." echo "- Copying Runtime DLLs..."
@ -693,13 +745,16 @@ fi
TARGET="$(basename "$DLL")" TARGET="$(basename "$DLL")"
if [[ "$DLL" == *":"* ]]; then if [[ "$DLL" == *":"* ]]; then
IFS=':'; SPLIT=( ${DLL} ); unset IFS IFS=':'; SPLIT=( ${DLL} ); unset IFS
DLL=${SPLIT[0]} DLL=${SPLIT[0]}
TARGET=${SPLIT[1]} TARGET=${SPLIT[1]}
fi fi
echo " ${TARGET}." echo " ${TARGET}."
cp "$DLL" "$BUILD_CONFIG/$TARGET" cp "$DLL" "$BUILD_CONFIG/$TARGET"
done done
echo echo
echo "- OSG Plugin DLLs..." echo "- OSG Plugin DLLs..."
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1 mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
for DLL in $OSG_PLUGINS; do for DLL in $OSG_PLUGINS; do
@ -707,6 +762,7 @@ fi
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1 cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
done done
echo echo
echo "- Qt Platform DLLs..." echo "- Qt Platform DLLs..."
mkdir -p ${BUILD_CONFIG}/platforms mkdir -p ${BUILD_CONFIG}/platforms
for DLL in $QT_PLATFORMS; do for DLL in $QT_PLATFORMS; do
@ -715,13 +771,16 @@ fi
done done
echo echo
#fi #fi
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
printf -- "- Configuring... " printf -- "- Configuring... "
else else
echo "- cmake .. $CMAKE_OPTS" echo "- cmake .. $CMAKE_OPTS"
fi fi
run_cmd cmake .. $CMAKE_OPTS run_cmd cmake .. $CMAKE_OPTS
RET=$? RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then if [ $RET -eq 0 ]; then
echo Done. echo Done.
@ -729,4 +788,5 @@ if [ -z $VERBOSE ]; then
echo Failed. echo Failed.
fi fi
fi fi
exit $RET exit $RET

View file

@ -4,14 +4,14 @@ export CXX=clang++
export CC=clang export CC=clang
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps" DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH=`brew --prefix qt` QT_PATH=`brew --prefix $macos_qt_formula`
mkdir build mkdir build
cd build cd build
cmake \ cmake \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
-D CMAKE_OSX_SYSROOT="macosx10.13" \ -D CMAKE_OSX_SYSROOT="macosx10.12" \
-D CMAKE_BUILD_TYPE=Release \ -D CMAKE_BUILD_TYPE=Release \
-D OPENMW_OSX_DEPLOYMENT=TRUE \ -D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \ -D DESIRED_QT_VERSION=5 \

View file

@ -140,6 +140,16 @@ endif()
find_package(RakNet REQUIRED) find_package(RakNet REQUIRED)
include_directories(${RakNet_INCLUDES}) 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 # Dependencies
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
@ -209,7 +219,6 @@ endif()
IF(BUILD_OPENMW OR BUILD_OPENCS) IF(BUILD_OPENMW OR BUILD_OPENCS)
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX) find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS set(USED_OSG_PLUGINS
osgdb_bmp osgdb_bmp
@ -250,10 +259,11 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED) find_package(OpenAL REQUIRED)
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
ELSE()
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DO NOT MOVE THIS. Used for server only build, kept here to avoid merge conflicts above.
ENDIF(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) set(BOOST_COMPONENTS system filesystem program_options)
if(WIN32) if(WIN32)
@ -666,10 +676,10 @@ if (WIN32)
endif() endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
# Release builds don't use the debug console # Release builds use the debug console
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS") set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif() endif()
# Play a bit with the warning levels # Play a bit with the warning levels
@ -680,8 +690,7 @@ if (WIN32)
# Warnings that aren't enabled normally and don't need to be enabled # Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables # They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file # Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
# Warnings that are thrown on standard libraries and not OpenMW # Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function 4347 # Non-template function with same name and parameter count as template function
@ -702,7 +711,6 @@ if (WIN32)
# caused by MyGUI # caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
4297 # function assumed not to throw an exception but does
# OpenMW specific warnings # OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type 4099 # Type mismatch, declared class or struct is defined with other type
@ -736,10 +744,7 @@ if (WIN32)
endforeach(d) endforeach(d)
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_OPENMW)
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif()
if (BUILD_BSATOOL) if (BUILD_BSATOOL)
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")

View file

@ -3,7 +3,7 @@ How to contribute to OpenMW
Not sure what to do with all your free time? Pick out a task from here: Not sure what to do with all your free time? Pick out a task from here:
https://gitlab.com/OpenMW/openmw/issues https://bugs.openmw.org/
Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first. Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.

View file

@ -1,49 +1,47 @@
TES3MP TES3MP
====== ======
Copyright (c) 2008-2015, OpenMW Team [![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp)
Copyright (c) 2016-2019, Stanislav Zhukov & David Cernat
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=0.7.0)](https://travis-ci.org/TES3MP/openmw-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 is a project adding multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing "The Elder Scrolls III: Morrowind" by Bethesda Softworks. * TES3MP version: TBD-rewrite
* TES3MP version: 0.7.0-alpha
* OpenMW version: 0.44.0 * OpenMW version: 0.44.0
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information) * License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
Font Licenses: Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVu Font License.txt](https://github.com/TES3MP/openmw-tes3mp/blob/master/files/mygui/DejaVu%20Font%20License.txt) for more information) * DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVu Font License.txt](https://github.com/TES3MP/openmw-tes3mp/blob/master/files/mygui/DejaVu%20Font%20License.txt) for more information)
Project status Project Status
-------------- --------------
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md) [Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
As of version 0.7.0, TES3MP is fully playable, providing very extensive player, NPC, world and quest synchronization, as well as state saving and loading, all of which are highly customizable via [serverside Lua scripts](https://github.com/TES3MP/CoreScripts). 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.
Remaining gameplay problems mostly relate to AI and the synchronization of clientside script variables. [Serverside Lua scripts](https://github.com/TES3MP/CoreScripts) are used to save and load the state of most of the aforementioned.
Donations 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.
---------------
You can benefit the project by donating on Patreon to our two developers, [David Cernat](https://www.patreon.com/davidcernat) and [Koncord](https://www.patreon.com/Koncord), as well as by supporting [OpenMW](https://openmw.org).
Contributing Contributing
--------------- --------------
Helping us with documentation, bug hunting and video showcases is always greatly appreciated. Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated.
For code contributions, it's best to start out with modestly sized fixes and features and work your way up. There are so many different possible implementations of more major features many of which would cause undesirable code or vision conflicts with OpenMW that those should be talked over in advance with the existing developers before effort is spent on them. Test sessions are often advertised on [our Discord server](https://discord.gg/ECJk293) or in [our Steam group](https://steamcommunity.com/groups/mwmulti).
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have. Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
Getting started Getting Started
--------------- ---------------
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide) * [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/) * [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=45) * [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
* [Discord server](https://discord.gg/ECJk293)
* [Subreddit](https://www.reddit.com/r/tes3mp) * [Subreddit](https://www.reddit.com/r/tes3mp)
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues) * [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
Donations
---------------
You can benefit the project by contributing to the Patreon pages of our two developers, [Koncord](https://www.patreon.com/Koncord) and [David Cernat](https://www.patreon.com/davidcernat), as well as by supporting [OpenMW](https://openmw.org).

View file

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

View file

@ -13,6 +13,8 @@
#include <QJsonArray> #include <QJsonArray>
#include <QFile> #include <QFile>
#include <QJsonDocument> #include <QJsonDocument>
#include <apps/browser/netutils/Utils.hpp>
#include <QtWidgets/QFileDialog>
using namespace Process; using namespace Process;
@ -31,9 +33,6 @@ MainWindow::MainWindow(QWidget *parent)
tblServerBrowser->setModel(proxyModel); tblServerBrowser->setModel(proxyModel);
tblFavorites->setModel(proxyModel); tblFavorites->setModel(proxyModel);
// Remove Favorites tab while it remains broken
tabWidget->removeTab(1);
tblServerBrowser->hideColumn(ServerData::ADDR); tblServerBrowser->hideColumn(ServerData::ADDR);
tblFavorites->hideColumn(ServerData::ADDR); tblFavorites->hideColumn(ServerData::ADDR);
@ -57,12 +56,31 @@ MainWindow::MainWindow(QWidget *parent)
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool))); connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int))); connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &))); 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(); loadFavorites();
queryHelper->refresh(); 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() MainWindow::~MainWindow()
{ {
settingsMgr.saveBrowserSettings(*this);
delete mGameInvoker; delete mGameInvoker;
} }
@ -71,6 +89,9 @@ void MainWindow::addServerAndUpdate(const QString &addr)
favorites->insertRow(0); favorites->insertRow(0);
QModelIndex mi = favorites->index(0, ServerData::ADDR); QModelIndex mi = favorites->index(0, ServerData::ADDR);
favorites->setData(mi, addr, Qt::EditRole); favorites->setData(mi, addr, Qt::EditRole);
/*auto address = addr.split(":");
auto data = getExtendedData(address[0].toLatin1(), address[1].toUShort());*/
//NetController::get()->updateInfo(favorites, mi); //NetController::get()->updateInfo(favorites, mi);
//QueryClient::Update(RakNet::SystemAddress()) //QueryClient::Update(RakNet::SystemAddress())
/*auto data = QueryClient::Get().Query(); /*auto data = QueryClient::Get().Query();

View file

@ -9,6 +9,7 @@
#include "ui_Main.h" #include "ui_Main.h"
#include "ServerModel.hpp" #include "ServerModel.hpp"
#include "MySortFilterProxyModel.hpp" #include "MySortFilterProxyModel.hpp"
#include "Settings.hpp"
#include <components/process/processinvoker.hpp> #include <components/process/processinvoker.hpp>
class QueryHelper; class QueryHelper;
@ -39,6 +40,7 @@ private:
Process::ProcessInvoker *mGameInvoker; Process::ProcessInvoker *mGameInvoker;
ServerModel *browser, *favorites; ServerModel *browser, *favorites;
MySortFilterProxyModel *proxyModel; MySortFilterProxyModel *proxyModel;
SettingsMgr settingsMgr;
void loadFavorites(); void loadFavorites();
}; };

198
apps/browser/Settings.cpp Normal file
View file

@ -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;
}

32
apps/browser/Settings.hpp Normal file
View file

@ -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();
};

View file

@ -4,47 +4,8 @@
#include <apps/browser/netutils/QueryClient.hpp> #include <apps/browser/netutils/QueryClient.hpp>
#include "MainWindow.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[]) int main(int argc, char *argv[])
{ {
Settings::Manager mgr;
loadSettings(mgr);
std::string addr = mgr.getString("address", "Master");
int port = mgr.getInt("port", "Master");
// Is this an attempt to connect to the official master server at the old port? If so,
// redirect it to the correct port for the currently used fork of RakNet
if (Misc::StringUtils::ciEqual(addr, "master.tes3mp.com") && port == 25560)
port = 25561;
// initialize resources, if needed
// Q_INIT_RESOURCE(resfile);
QueryClient::Get().SetServer(addr, port);
QApplication app(argc, argv); QApplication app(argc, argv);
MainWindow d; MainWindow d;

View file

@ -20,7 +20,6 @@ namespace ESSImport
item.mId = contItem.mItem.toString(); item.mId = contItem.mItem.toString();
item.mCount = contItem.mCount; item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1; item.mRelativeEquipmentSlot = -1;
item.mLockLevel = 0;
unsigned int itemCount = std::abs(item.mCount); unsigned int itemCount = std::abs(item.mCount);
bool separateStacks = false; bool separateStacks = false;

View file

@ -8,7 +8,6 @@ set(LAUNCHER
settingspage.cpp settingspage.cpp
advancedpage.cpp advancedpage.cpp
utils/cellnameloader.cpp
utils/profilescombobox.cpp utils/profilescombobox.cpp
utils/textinputdialog.cpp utils/textinputdialog.cpp
utils/lineedit.cpp utils/lineedit.cpp
@ -25,7 +24,6 @@ set(LAUNCHER_HEADER
settingspage.hpp settingspage.hpp
advancedpage.hpp advancedpage.hpp
utils/cellnameloader.hpp
utils/profilescombobox.hpp utils/profilescombobox.hpp
utils/textinputdialog.hpp utils/textinputdialog.hpp
utils/lineedit.hpp utils/lineedit.hpp
@ -41,7 +39,6 @@ set(LAUNCHER_HEADER_MOC
settingspage.hpp settingspage.hpp
advancedpage.hpp advancedpage.hpp
utils/cellnameloader.hpp
utils/textinputdialog.hpp utils/textinputdialog.hpp
utils/profilescombobox.hpp utils/profilescombobox.hpp
utils/lineedit.hpp utils/lineedit.hpp

View file

@ -1,18 +1,10 @@
#include "advancedpage.hpp" #include "advancedpage.hpp"
#include <components/config/gamesettings.hpp> #include <components/files/configurationmanager.hpp>
#include <components/config/launchersettings.hpp>
#include <QFileDialog>
#include <QCompleter>
#include <components/contentselector/view/contentselector.hpp>
#include <components/contentselector/model/esmfile.hpp>
Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent)
Config::GameSettings &gameSettings,
Settings::Manager &engineSettings, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, mCfgMgr(cfg) , mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mEngineSettings(engineSettings) , mEngineSettings(engineSettings)
{ {
setObjectName ("AdvancedPage"); setObjectName ("AdvancedPage");
@ -21,61 +13,23 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
loadSettings(); loadSettings();
} }
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
// Set up an auto-completer for the "Start default character at" field
auto *completer = new QCompleter(cellNames);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
startDefaultCharacterAtField->setCompleter(completer);
}
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);
startDefaultCharacterAtField->setEnabled(state == Qt::Checked);
}
void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
{
QString scriptFile = QFileDialog::getOpenFileName(
this,
QObject::tr("Select script file"),
QDir::currentPath(),
QString(tr("Text file (*.txt)")));
if (scriptFile.isEmpty())
return;
QFileInfo info(scriptFile);
if (!info.exists() || !info.isReadable())
return;
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
runScriptAfterStartupField->setText(path);
}
bool Launcher::AdvancedPage::loadSettings() bool Launcher::AdvancedPage::loadSettings()
{ {
// Testing
bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1;
if (skipMenu) {
skipMenuCheckBox->setCheckState(Qt::Checked);
}
startDefaultCharacterAtLabel->setEnabled(skipMenu);
startDefaultCharacterAtField->setEnabled(skipMenu);
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
// Game Settings // Game Settings
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); // Expected values are (0, 1, 2, 3)
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option. Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Input Settings // Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
@ -86,16 +40,6 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves")); maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
// User Interface Settings
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Other Settings // Other Settings
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper(); QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
@ -110,27 +54,19 @@ void Launcher::AdvancedPage::saveSettings()
// Ensure we only set the new settings if they changed. This is to avoid cluttering the // Ensure we only set the new settings if they changed. This is to avoid cluttering the
// user settings file (which by definition should only contain settings the user has touched) // user settings file (which by definition should only contain settings the user has touched)
// Testing
int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;
if (skipMenu != mGameSettings.value("skip-menu").toInt())
mGameSettings.setValue("skip-menu", QString::number(skipMenu));
QString startCell = startDefaultCharacterAtField->text();
if (startCell != mGameSettings.value("start")) {
mGameSettings.setValue("start", startCell);
}
QString scriptRun = runScriptAfterStartupField->text();
if (scriptRun != mGameSettings.value("script-run"))
mGameSettings.setValue("script-run", scriptRun);
// Game Settings // Game Settings
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Input Settings // Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
@ -144,15 +80,6 @@ void Launcher::AdvancedPage::saveSettings()
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves); mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
} }
// User Interface Settings
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Other Settings // Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General")) if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
@ -169,8 +96,3 @@ void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::str
if (cValue != mEngineSettings.getBool(setting, group)) if (cValue != mEngineSettings.getBool(setting, group))
mEngineSettings.setBool(setting, group, cValue); mEngineSettings.setBool(setting, group, cValue);
} }
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
{
loadCellsForAutocomplete(cellNames);
}

View file

@ -8,7 +8,6 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
namespace Files { struct ConfigurationManager; } namespace Files { struct ConfigurationManager; }
namespace Config { class GameSettings; }
namespace Launcher namespace Launcher
{ {
@ -17,29 +16,15 @@ namespace Launcher
Q_OBJECT Q_OBJECT
public: public:
AdvancedPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, AdvancedPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0);
Settings::Manager &engineSettings, QWidget *parent = 0);
bool loadSettings(); bool loadSettings();
void saveSettings(); void saveSettings();
public slots:
void slotLoadedCellsChanged(QStringList cellNames);
private slots:
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
private: private:
Files::ConfigurationManager &mCfgMgr; Files::ConfigurationManager &mCfgMgr;
Config::GameSettings &mGameSettings;
Settings::Manager &mEngineSettings; Settings::Manager &mEngineSettings;
/**
* Load the cells associated with the given content files for use in autocomplete
* @param filePaths the file paths of the content files to be examined
*/
void loadCellsForAutocomplete(QStringList filePaths);
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group); void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group); void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
}; };

View file

@ -7,10 +7,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <QMenu> #include <QMenu>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <thread>
#include <mutex>
#include <apps/launcher/utils/cellnameloader.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/contentselector/model/esmfile.hpp> #include <components/contentselector/model/esmfile.hpp>
@ -19,7 +16,6 @@
#include <components/config/gamesettings.hpp> #include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp> #include <components/config/launchersettings.hpp>
#include <iostream>
#include "utils/textinputdialog.hpp" #include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp" #include "utils/profilescombobox.hpp"
@ -44,13 +40,6 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
buildView(); buildView();
loadSettings(); loadSettings();
// Connect signal and slot after the settings have been loaded. We only care about the user changing
// the addons and don't want to get signals of the system doing it during startup.
connect(mSelector, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex)),
this, SLOT(slotAddonDataChanged()));
// Call manually to indicate all changes to addon data during startup.
slotAddonDataChanged();
} }
void Launcher::DataFilesPage::buildView() void Launcher::DataFilesPage::buildView()
@ -153,17 +142,6 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
mGameSettings.setContentList(fileNames); mGameSettings.setContentList(fileNames);
} }
QStringList Launcher::DataFilesPage::selectedFilePaths()
{
//retrieve the files selected for the profile
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
QStringList filePaths;
foreach(const ContentSelectorModel::EsmFile *item, items) {
filePaths.append(item->filePath());
}
return filePaths;
}
void Launcher::DataFilesPage::removeProfile(const QString &profile) void Launcher::DataFilesPage::removeProfile(const QString &profile)
{ {
mLauncherSettings.removeContentList(profile); mLauncherSettings.removeContentList(profile);
@ -330,31 +308,3 @@ bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)
return (msgBox.clickedButton() == deleteButton); return (msgBox.clickedButton() == deleteButton);
} }
void Launcher::DataFilesPage::slotAddonDataChanged()
{
QStringList selectedFiles = selectedFilePaths();
if (previousSelectedFiles != selectedFiles) {
previousSelectedFiles = selectedFiles;
// Loading cells for core Morrowind + Expansions takes about 0.2 seconds, which is enough to cause a
// barely perceptible UI lag. Splitting into its own thread to alleviate that.
std::thread loadCellsThread(&DataFilesPage::reloadCells, this, selectedFiles);
loadCellsThread.detach();
}
}
// Mutex lock to run reloadCells synchronously.
std::mutex _reloadCellsMutex;
void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
{
// Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time
// Based on https://stackoverflow.com/a/5429695/531762
std::unique_lock<std::mutex> lock(_reloadCellsMutex);
// The following code will run only if there is not another thread currently running it
CellNameLoader cellNameLoader;
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(cellNamesList);
}

View file

@ -7,7 +7,6 @@
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QStringList>
class QSortFilterProxyModel; class QSortFilterProxyModel;
class QAbstractItemModel; class QAbstractItemModel;
@ -42,15 +41,8 @@ namespace Launcher
void saveSettings(const QString &profile = ""); void saveSettings(const QString &profile = "");
bool loadSettings(); bool loadSettings();
/**
* Returns the file paths of all selected content files
* @return the file paths of all selected content files
*/
QStringList selectedFilePaths();
signals: signals:
void signalProfileChanged (int index); void signalProfileChanged (int index);
void signalLoadedCellsChanged(QStringList selectedFiles);
public slots: public slots:
void slotProfileChanged (int index); void slotProfileChanged (int index);
@ -60,7 +52,6 @@ namespace Launcher
void slotProfileChangedByUser(const QString &previous, const QString &current); void slotProfileChangedByUser(const QString &previous, const QString &current);
void slotProfileRenamed(const QString &previous, const QString &current); void slotProfileRenamed(const QString &previous, const QString &current);
void slotProfileDeleted(const QString &item); void slotProfileDeleted(const QString &item);
void slotAddonDataChanged ();
void updateOkButton(const QString &text); void updateOkButton(const QString &text);
@ -81,7 +72,7 @@ namespace Launcher
Config::LauncherSettings &mLauncherSettings; Config::LauncherSettings &mLauncherSettings;
QString mPreviousProfile; QString mPreviousProfile;
QStringList previousSelectedFiles;
QString mDataLocal; QString mDataLocal;
void setPluginsCheckstates(Qt::CheckState state); void setPluginsCheckstates(Qt::CheckState state);
@ -96,7 +87,6 @@ namespace Launcher
void addProfile (const QString &profile, bool setAsCurrent); void addProfile (const QString &profile, bool setAsCurrent);
void checkForDefaultProfile(); void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName); void populateFileViews(const QString& contentModelName);
void reloadCells(QStringList selectedFiles);
class PathIterator class PathIterator
{ {

View file

@ -1,7 +1,6 @@
#include "graphicspage.hpp" #include "graphicspage.hpp"
#include <boost/math/common_factor.hpp> #include <boost/math/common_factor.hpp>
#include <csignal>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QMessageBox> #include <QMessageBox>
#include <QDir> #include <QDir>
@ -12,7 +11,6 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include <SDL_video.h> #include <SDL_video.h>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -48,28 +46,8 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
} }
bool Launcher::GraphicsPage::connectToSdl() {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
// Required for determining screen resolution and such on the Graphics tab
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
return false;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
return true;
}
bool Launcher::GraphicsPage::setupSDL() bool Launcher::GraphicsPage::setupSDL()
{ {
bool sdlConnectSuccessful = connectToSdl();
if (!sdlConnectSuccessful)
{
return false;
}
int displays = SDL_GetNumVideoDisplays(); int displays = SDL_GetNumVideoDisplays();
if (displays < 0) if (displays < 0)
@ -89,9 +67,6 @@ bool Launcher::GraphicsPage::setupSDL()
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
} }
// Disconnect from SDL processes
SDL_Quit();
return true; return true;
} }

View file

@ -37,11 +37,6 @@ namespace Launcher
QStringList getAvailableResolutions(int screen); QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution(); QRect getMaximumResolution();
/**
* Connect to the SDL so that we can use it to determine graphics
* @return whether or not connecting to SDL is successful
*/
bool connectToSdl();
bool setupSDL(); bool setupSDL();
}; };
} }

View file

@ -1,4 +1,5 @@
#include <iostream> #include <iostream>
#include <csignal>
#include <QApplication> #include <QApplication>
#include <QTextCodec> #include <QTextCodec>
@ -11,12 +12,24 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include "maindialog.hpp" #include "maindialog.hpp"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
try try
{ {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError());
return 0;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
QApplication app(argc, argv); QApplication app(argc, argv);
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
@ -33,7 +46,9 @@ int main(int argc, char *argv[])
if (result == Launcher::FirstRunDialogResultContinue) if (result == Launcher::FirstRunDialogResultContinue)
mainWin.show(); mainWin.show();
return app.exec(); int returnValue = app.exec();
SDL_Quit();
return returnValue;
} }
catch (std::exception& e) catch (std::exception& e)
{ {

View file

@ -90,19 +90,19 @@ void Launcher::MainDialog::createIcons()
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget); QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon(":/images/preferences-video.png")); graphicsButton->setIcon(QIcon::fromTheme("video-display"));
graphicsButton->setText(tr("Graphics")); graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute); graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget); QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
settingsButton->setIcon(QIcon(":/images/preferences.png")); settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
settingsButton->setText(tr("Settings")); settingsButton->setText(tr("Settings"));
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget); QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png")); advancedButton->setIcon(QIcon::fromTheme("emblem-system"));
advancedButton->setText(tr("Advanced")); advancedButton->setText(tr("Advanced"));
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
@ -119,7 +119,7 @@ void Launcher::MainDialog::createPages()
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this); mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this); mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mAdvancedPage = new AdvancedPage(mCfgMgr, mGameSettings, mEngineSettings, this); mAdvancedPage = new AdvancedPage(mCfgMgr, mEngineSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage // Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
@ -139,8 +139,6 @@ void Launcher::MainDialog::createPages()
connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int))); connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int))); connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
} }

View file

@ -1,48 +0,0 @@
#include "cellnameloader.hpp"
#include <components/esm/loadcell.hpp>
#include <components/contentselector/view/contentselector.hpp>
QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
{
QSet<QString> cellNames;
ESM::ESMReader esmReader;
// Loop through all content files
for (auto &contentPath : contentPaths) {
esmReader.open(contentPath.toStdString());
// Loop through all records
while(esmReader.hasMoreRecs())
{
ESM::NAME recordName = esmReader.getRecName();
esmReader.getRecHeader();
if (isCellRecord(recordName)) {
QString cellName = getCellName(esmReader);
if (!cellName.isEmpty()) {
cellNames.insert(cellName);
}
}
// Stop loading content for this record and continue to the next
esmReader.skipRecord();
}
}
return cellNames;
}
bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
{
return recordName.intval == ESM::REC_CELL;
}
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
{
ESM::Cell cell;
bool isDeleted = false;
cell.loadNameAndData(esmReader, isDeleted);
return QString::fromStdString(cell.mName);
}

View file

@ -1,41 +0,0 @@
#ifndef OPENMW_CELLNAMELOADER_H
#define OPENMW_CELLNAMELOADER_H
#include <QComboBox>
#include <QSet>
#include <QString>
#include <components/esm/esmreader.hpp>
namespace ESM {class ESMReader; struct Cell;}
namespace ContentSelectorView {class ContentSelector;}
class CellNameLoader {
public:
/**
* Returns the names of all cells contained within the given content files
* @param contentPaths the file paths of each content file to be examined
* @return the names of all cells
*/
QSet<QString> getCellNames(QStringList &contentPaths);
private:
/**
* Returns whether or not the given record is of type "Cell"
* @param name The name associated with the record
* @return whether or not the given record is of type "Cell"
*/
bool isCellRecord(ESM::NAME &name);
/**
* Returns the name of the cell
* @param esmReader the reader currently pointed to a loaded cell
* @return the name of the cell
*/
QString getCellName(ESM::ESMReader &esmReader);
};
#endif //OPENMW_CELLNAMELOADER_H

69
apps/master/AdminRest.cpp Normal file
View file

@ -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();
}

25
apps/master/AdminRest.hpp Normal file
View file

@ -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;
};

View file

@ -1,14 +1,13 @@
project(masterserver) project(masterserver)
#set(CMAKE_CXX_STANDARD 14)
add_definitions(-std=gnu++14)
include_directories("./") 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}) 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) option(BUILD_MASTER_TEST "build master server test program" OFF)

View file

@ -12,19 +12,54 @@
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp> #include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp> #include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include <components/openmw-mp/Version.hpp> #include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Utils.hpp>
#include <boost/filesystem.hpp>
using namespace RakNet; using namespace RakNet;
using namespace std; using namespace std;
using namespace mwmp; using namespace mwmp;
using namespace chrono; using namespace chrono;
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port) MasterServer::MasterServer(const std::string &luaScript)
{ {
peer = RakPeerInterface::GetInstance(); state.open_libraries();
sockdescr = SocketDescriptor(port, 0);
peer->Startup(maxConnections, &sockdescr, 1, 1000);
peer->SetMaximumIncomingConnections(maxConnections); 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.as<unsigned short>(), nullptr);
peer->Startup(maxConnections.as<unsigned short>(), &sockdescr, 1, 1000);
peer->SetLimitIPConnectionFrequency(true);
peer->SetMaximumIncomingConnections(maxConnections.as<unsigned short>());
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW)); peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
run = false; run = false;
} }
@ -52,6 +87,13 @@ void MasterServer::Thread()
PacketMasterAnnounce pma(peer); PacketMasterAnnounce pma(peer);
pma.SetSendStream(&send); 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) while (run)
{ {
Packet *packet = peer->Receive(); Packet *packet = peer->Receive();
@ -67,9 +109,9 @@ void MasterServer::Thread()
servers.erase(it++); servers.erase(it++);
else ++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; cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
peer->CloseConnection(id->first, true); peer->CloseConnection(id->first, true);
@ -113,7 +155,7 @@ void MasterServer::Thread()
SystemAddress addr; SystemAddress addr;
data.Read(addr); // update 1 server data.Read(addr); // update 1 server
ServerIter it = servers.find(addr); auto it = servers.find(addr);
if (it != servers.end()) if (it != servers.end())
{ {
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second)); pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
@ -127,7 +169,7 @@ void MasterServer::Thread()
} }
case ID_MASTER_ANNOUNCE: case ID_MASTER_ANNOUNCE:
{ {
ServerIter iter = servers.find(packet->systemAddress); auto iter = servers.find(packet->systemAddress);
pma.SetReadStream(&data); pma.SetReadStream(&data);
SServer server; SServer server;
@ -141,6 +183,26 @@ void MasterServer::Thread()
pendingACKs[packet->guid] = steady_clock::now(); 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 (iter != servers.end())
{ {
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE) if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
@ -152,9 +214,19 @@ void MasterServer::Thread()
} }
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE) else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{ {
cout << "Updated";
iter->second = server; if (isServerValid(server))
keepAliveFunc(); {
cout << "Updated";
iter->second = server;
keepAliveFunc();
}
else
{
cout << "Update rejected";
servers.erase(iter);
pendingACKs.erase(packet->guid);
}
} }
else else
{ {
@ -164,9 +236,14 @@ void MasterServer::Thread()
} }
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE) else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{ {
cout << "Added"; if (isServerValid(server))
iter = servers.insert({packet->systemAddress, server}).first; {
keepAliveFunc(); cout << "Added";
iter = servers.insert({packet->systemAddress, server}).first;
keepAliveFunc();
}
else
cout << "Adding rejected";
} }
else else
{ {
@ -186,7 +263,8 @@ void MasterServer::Thread()
peer->CloseConnection(packet->systemAddress, true); peer->CloseConnection(packet->systemAddress, true);
break; break;
default: 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); peer->CloseConnection(packet->systemAddress, true);
} }
} }
@ -234,3 +312,22 @@ MasterServer::ServerMap *MasterServer::GetServers()
{ {
return &servers; 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());
}

View file

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

View file

@ -7,22 +7,12 @@
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include "RestUtils.hpp"
using namespace std; using namespace std;
using namespace chrono; using namespace chrono;
using namespace boost::property_tree; 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) inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
{ {
server.SetName(pt.get<string>("hostname").c_str()); server.SetName(pt.get<string>("hostname").c_str());
@ -70,7 +60,7 @@ void RestServer::start()
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1])); auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port))); queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
ss << "}"; ss << "}";
ResponseStr(*response, ss.str(), "application/json"); ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
} }
catch(out_of_range e) catch(out_of_range e)
{ {
@ -93,7 +83,7 @@ void RestServer::start()
ss << ", "; ss << ", ";
} }
ss << "}}"; ss << "}}";
ResponseStr(*response, ss.str(), "application/json"); ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
updatedCache = false; updatedCache = false;
} }
*response << str; *response << str;
@ -110,7 +100,7 @@ void RestServer::start()
MasterServer::SServer server; MasterServer::SServer server;
ptreeToServer(pt, server); ptreeToServer(pt, server);
unsigned short port = pt.get<unsigned short>("port"); auto port = pt.get<unsigned short>("port");
server.lastUpdate = steady_clock::now(); server.lastUpdate = steady_clock::now();
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server}); serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
updatedCache = true; updatedCache = true;
@ -137,7 +127,6 @@ void RestServer::start()
*response << response400; *response << response400;
return; return;
} }
if (request->content.size() != 0) if (request->content.size() != 0)
{ {
try try
@ -171,14 +160,14 @@ void RestServer::start()
ss << ", \"players\": " << players; ss << ", \"players\": " << players;
ss << "}"; ss << "}";
ResponseStr(*response, ss.str(), "application/json"); ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
}; };
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) { httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
*response << response400; *response << response400;
}; };
httpServer.start(); thr = thread([this](){httpServer.start();});
} }
void RestServer::cacheUpdated() void RestServer::cacheUpdated()
@ -189,4 +178,6 @@ void RestServer::cacheUpdated()
void RestServer::stop() void RestServer::stop()
{ {
httpServer.stop(); httpServer.stop();
if(thr.joinable())
thr.join();
} }

View file

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

25
apps/master/RestUtils.hpp Normal file
View file

@ -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;
}

View file

@ -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.

View file

@ -1,72 +1,137 @@
#ifndef BASE_SERVER_HPP #pragma once
#define BASE_SERVER_HPP
#include <boost/asio.hpp> #include "utility.hpp"
#include <boost/algorithm/string/predicate.hpp> #include <condition_variable>
#include <boost/functional/hash.hpp>
#include <map>
#include <unordered_map>
#include <thread>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <map>
#include <sstream> #include <sstream>
#include <thread>
#include <unordered_set>
#include <regex> #include <regex>
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH #ifdef USE_STANDALONE_ASIO
#define CASE_INSENSITIVE_EQUALS_AND_HASH #include <asio.hpp>
#include <asio/steady_timer.hpp>
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html namespace SimpleWeb {
struct case_insensitive_equals using error_code = std::error_code;
{ using errc = std::errc;
bool operator()(const std::string &key1, const std::string &key2) const namespace make_error_code = std;
{ } // namespace SimpleWeb
return boost::algorithm::iequals(key1, key2); #else
} #include <boost/asio.hpp>
}; #include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
struct case_insensitive_hash namespace asio = boost::asio;
{ using error_code = boost::system::error_code;
size_t operator()(const std::string &key) const namespace errc = boost::system::errc;
{ namespace make_error_code = boost::system::errc;
std::size_t seed = 0; } // namespace SimpleWeb
for (auto &c: key)
boost::hash_combine(seed, std::tolower(c));
return seed;
}
};
#endif #endif
namespace SimpleWeb namespace SimpleWeb {
{ template <class socket_type>
template<class socket_type>
class Server; class Server;
template<class socket_type> template <class socket_type>
class ServerBase class ServerBase {
{ protected:
class Session;
public: public:
virtual ~ServerBase() class Response : public std::enable_shared_from_this<Response>, public std::ostream {
{}
class Response : public std::ostream
{
friend class ServerBase<socket_type>; friend class ServerBase<socket_type>;
friend class Server<socket_type>;
boost::asio::streambuf streambuf; asio::streambuf streambuf;
std::shared_ptr<socket_type> socket; std::shared_ptr<Session> session;
long timeout_content;
Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket) Response(std::shared_ptr<Session> session, long timeout_content) noexcept : std::ostream(&streambuf), session(std::move(session)), timeout_content(timeout_content) {}
{}
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: public:
size_t size() size_t size() noexcept {
{
return streambuf.size(); 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. /// 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 /// This is useful when implementing a HTTP/1.0-server sending content
@ -74,438 +139,399 @@ namespace SimpleWeb
bool close_connection_after_response = false; bool close_connection_after_response = false;
}; };
class Content : public std::istream class Content : public std::istream {
{
friend class ServerBase<socket_type>; friend class ServerBase<socket_type>;
public: public:
size_t size() size_t size() noexcept {
{
return streambuf.size(); return streambuf.size();
} }
/// Convenience function to return std::string. The stream buffer is consumed.
std::string string() std::string string() noexcept {
{ try {
std::stringstream ss; std::stringstream ss;
ss << rdbuf(); ss << rdbuf();
return ss.str(); return ss.str();
}
catch(...) {
return std::string();
}
} }
private: private:
boost::asio::streambuf &streambuf; asio::streambuf &streambuf;
Content(asio::streambuf &streambuf) noexcept : std::istream(&streambuf), streambuf(streambuf) {}
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
{}
}; };
class Request class Request {
{
friend class ServerBase<socket_type>; friend class ServerBase<socket_type>;
friend class Server<socket_type>; friend class Server<socket_type>;
friend class Session;
public: public:
std::string method, path, http_version; std::string method, path, query_string, http_version;
Content content; Content content;
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header; CaseInsensitiveMultimap header;
std::smatch path_match; std::smatch path_match;
std::string remote_endpoint_address; std::string remote_endpoint_address;
unsigned short remote_endpoint_port; unsigned short remote_endpoint_port;
private: /// Returns query keys with percent-decoded values.
Request(const socket_type &socket) : content(streambuf) CaseInsensitiveMultimap parse_query_string() noexcept {
{ return SimpleWeb::QueryString::parse(query_string);
try
{
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
}
catch (...)
{}
} }
boost::asio::streambuf streambuf; private:
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) {}
}; };
class Config 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());
}
}
std::shared_ptr<Connection> connection;
std::shared_ptr<Request> request;
};
public:
class Config {
friend class ServerBase<socket_type>; friend class ServerBase<socket_type>;
Config(unsigned short port) : port(port) Config(unsigned short port) noexcept : port(port) {}
{}
public: public:
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS. /// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
unsigned short port; 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; size_t thread_pool_size = 1;
/// Timeout on request handling. Defaults to 5 seconds. /// 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. /// 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. /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
/// If empty, the address will be any address. /// If empty, the address will be any address.
std::string address; std::string address;
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true. /// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
bool reuse_address = true; bool reuse_address = true;
}; };
/// Set before calling start().
///Set before calling start().
Config config; Config config;
private: private:
class regex_orderable : public std::regex class regex_orderable : public std::regex {
{
std::string str; std::string str;
public: public:
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr) 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 {
regex_orderable(const std::string &regex_str) : std::regex(regex_str), str(regex_str)
{}
bool operator<(const regex_orderable &rhs) const
{
return str < rhs.str; return str < rhs.str;
} }
}; };
public: public:
/// Warning: do not add or remove resources after start() is called /// Warning: do not add or remove resources after start() is called
std::map<regex_orderable, std::map<std::string, 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::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
resource;
std::map<std::string, 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>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
std::function< std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
const boost::system::error_code &)>
on_error;
std::function<void(std::shared_ptr<socket_type> socket, std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
virtual void start() /// If you have your own asio::io_service, store its pointer here before running start().
{ std::shared_ptr<asio::io_service> io_service;
if (!io_service)
io_service = std::make_shared<boost::asio::io_service>();
if (io_service->stopped()) virtual void start() {
if(!io_service) {
io_service = std::make_shared<asio::io_service>();
internal_io_service = true;
}
if(io_service->stopped())
io_service->reset(); io_service->reset();
boost::asio::ip::tcp::endpoint endpoint; asio::ip::tcp::endpoint endpoint;
if (config.address.size() > 0) if(config.address.size() > 0)
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
config.port);
else 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) if(!acceptor)
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>( acceptor = std::unique_ptr<asio::ip::tcp::acceptor>(new asio::ip::tcp::acceptor(*io_service));
new boost::asio::ip::tcp::acceptor(*io_service));
acceptor->open(endpoint.protocol()); 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->bind(endpoint);
acceptor->listen(); acceptor->listen();
accept(); accept();
//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling if(internal_io_service) {
threads.clear(); // If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
for (size_t c = 1; c < config.thread_pool_size; c++) threads.clear();
{ for(size_t c = 1; c < config.thread_pool_size; c++) {
threads.emplace_back([this]() threads.emplace_back([this]() {
{ this->io_service->run();
io_service->run(); });
}); }
}
//Main thread // Main thread
if (config.thread_pool_size > 0) if(config.thread_pool_size > 0)
io_service->run(); io_service->run();
//Wait for the rest of the threads, if any, to finish as well // Wait for the rest of the threads, if any, to finish as well
for (auto &t: threads) for(auto &t : threads)
{ t.join();
t.join();
} }
} }
void stop() /// Stop accepting new requests, and close current connections.
{ void stop() noexcept {
acceptor->close(); if(acceptor) {
if (config.thread_pool_size > 0) error_code ec;
io_service->stop(); 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 virtual ~ServerBase() noexcept {
void send(const std::shared_ptr<Response> &response, handler_runner->stop();
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const stop();
{
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);
});
} }
/// 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: 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; std::vector<std::thread> threads;
ServerBase(unsigned short port) : config(port) std::shared_ptr<std::unordered_set<Connection *>> connections;
{} std::shared_ptr<std::mutex> connections_mutex;
virtual void accept()=0; std::shared_ptr<ScopeRunner> handler_runner;
std::shared_ptr<boost::asio::deadline_timer> ServerBase(unsigned short port) noexcept : config(port), connections(new std::unordered_set<Connection *>()), connections_mutex(new std::mutex()), handler_runner(new ScopeRunner()) {}
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); virtual void accept() = 0;
timer->expires_from_now(boost::posix_time::seconds(seconds));
timer->async_wait([socket](const boost::system::error_code &ec) template <typename... Args>
{ std::shared_ptr<Connection> create_connection(Args &&... args) noexcept {
if (!ec) auto connections = this->connections;
{ auto connections_mutex = this->connections_mutex;
boost::system::error_code ec; auto connection = std::shared_ptr<Connection>(new Connection(handler_runner, std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); {
socket->lowest_layer().close(); std::unique_lock<std::mutex> lock(*connections_mutex);
} auto it = connections->find(connection);
}); if(it != connections->end())
return timer; 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<socket_type> &socket) void read_request_and_content(const std::shared_ptr<Session> &session) {
{ session->connection->set_timeout(config.timeout_request);
//Create new streambuf (Request::streambuf) for async_read_until() asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
//shared_ptr is used to pass temporary objects to the asynchronous functions session->connection->cancel_timeout();
std::shared_ptr<Request> request(new Request(*socket)); 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;
//Set timeout on the following boost::asio::async-read or write function if(!RequestMessage::parse(session->request->content, session->request->method, session->request->path,
auto timer = this->get_timeout_timer(socket, config.timeout_request); session->request->query_string, session->request->http_version, session->request->header)) {
if(this->on_error)
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", [this, socket, request, timer] this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
(const boost::system::error_code &ec,
size_t bytes_transferred)
{
if (timer)
timer->cancel();
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 =
request->streambuf.size() - bytes_transferred;
if (!this->parse_request(request))
return; return;
}
//If content, read that as well // If content, read that as well
auto it = request->header.find("Content-Length"); auto it = session->request->header.find("Content-Length");
if (it != request->header.end()) if(it != session->request->header.end()) {
{ unsigned long long content_length = 0;
unsigned long long content_length; try {
try
{
content_length = stoull(it->second); content_length = stoull(it->second);
} }
catch (const std::exception &e) catch(const std::exception &e) {
{ if(this->on_error)
if (on_error) this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
on_error(request, boost::system::error_code(
boost::system::errc::protocol_error,
boost::system::generic_category()));
return; return;
} }
if (content_length > num_additional_bytes) if(content_length > num_additional_bytes) {
{ session->connection->set_timeout(config.timeout_content);
//Set timeout on the following boost::asio::async-read or write function 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*/) {
auto timer = this->get_timeout_timer(socket, session->connection->cancel_timeout();
config.timeout_content); auto lock = session->connection->handler_runner->continue_lock();
boost::asio::async_read(*socket, request->streambuf, if(!lock)
boost::asio::transfer_exactly( return;
content_length - if(!ec)
num_additional_bytes), this->find_resource(session);
[this, socket, request, timer] else if(this->on_error)
(const boost::system::error_code &ec, this->on_error(session->request, ec);
size_t /*bytes_transferred*/) });
{
if (timer)
timer->cancel();
if (!ec)
this->find_resource(socket,
request);
else if (on_error)
on_error(request, ec);
});
} }
else else
this->find_resource(socket, request); this->find_resource(session);
} }
else else
this->find_resource(socket, request); this->find_resource(session);
} }
else if (on_error) else if(this->on_error)
on_error(request, ec); this->on_error(session->request, ec);
}); });
} }
bool parse_request(const std::shared_ptr<Request> &request) const void find_resource(const std::shared_ptr<Session> &session) {
{ // Upgrade connection
std::string line; if(on_upgrade) {
getline(request->content, line); auto it = session->request->header.find("Upgrade");
size_t method_end; if(it != session->request->header.end()) {
if ((method_end = line.find(' ')) != std::string::npos) // remove connection from connections
{
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)
{ {
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0) std::unique_lock<std::mutex> lock(*connections_mutex);
return false; auto it = connections->find(session->connection.get());
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2); if(it != connections->end())
connections->erase(it);
} }
else
return false;
getline(request->content, line); on_upgrade(session->connection->socket, session->request);
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);
return; return;
} }
} }
//Find path- and method-match, and call write_response // Find path- and method-match, and call write_response
for (auto &regex_method: resource) for(auto &regex_method : resource) {
{ auto it = regex_method.second.find(session->request->method);
auto it = regex_method.second.find(request->method); if(it != regex_method.second.end()) {
if (it != regex_method.second.end())
{
std::smatch sm_res; std::smatch sm_res;
if (std::regex_match(request->path, sm_res, regex_method.first)) if(std::regex_match(session->request->path, sm_res, regex_method.first)) {
{ session->request->path_match = std::move(sm_res);
request->path_match = std::move(sm_res); write_response(session, it->second);
write_response(socket, request, it->second);
return; return;
} }
} }
} }
auto it = default_resource.find(request->method); auto it = default_resource.find(session->request->method);
if (it != default_resource.end()) if(it != default_resource.end())
{ write_response(session, it->second);
write_response(socket, request, it->second);
}
} }
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request, void write_response(const std::shared_ptr<Session> &session,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
std::shared_ptr< session->connection->set_timeout(config.timeout_content);
typename ServerBase<socket_type>::Request>)> &resource_function) auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
{
//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)
{
auto response = std::shared_ptr<Response>(response_ptr); auto response = std::shared_ptr<Response>(response_ptr);
this->send(response, [this, response, request, timer]( response->send([this, response](const error_code &ec) {
const boost::system::error_code &ec) if(!ec) {
{ if(response->close_connection_after_response)
if (timer)
timer->cancel();
if (!ec)
{
if (response->close_connection_after_response)
return; return;
auto range = request->header.equal_range( auto range = response->session->request->header.equal_range("Connection");
"Connection"); for(auto it = range.first; it != range.second; it++) {
for (auto it = range.first; it != range.second; it++) if(case_insensitive_equal(it->second, "close"))
{
if (boost::iequals(it->second, "close"))
{
return; return;
} else if(case_insensitive_equal(it->second, "keep-alive")) {
else if (boost::iequals(it->second, "keep-alive")) auto new_session = std::make_shared<Session>(response->session->connection);
{ this->read_request_and_content(new_session);
this->read_request_and_content(
response->socket);
return; return;
} }
} }
if (request->http_version >= "1.1") if(response->session->request->http_version >= "1.1") {
this->read_request_and_content(response->socket); auto new_session = std::make_shared<Session>(response->session->connection);
this->read_request_and_content(new_session);
return;
}
} }
else if (on_error) else if(this->on_error)
on_error(request, ec); this->on_error(response->session->request, ec);
}); });
}); });
try try {
{ resource_function(response, session->request);
resource_function(response, request);
} }
catch (const std::exception &e) catch(const std::exception &e) {
{ if(on_error)
if (on_error) on_error(session->request, make_error_code::make_error_code(errc::operation_canceled));
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
boost::system::generic_category()));
return; return;
} }
} }
}; };
} }
#endif //BASE_SERVER_HPP

View file

@ -1,55 +1,42 @@
/* #pragma once
* 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
#include "base_server.hpp" #include "base_server.hpp"
namespace SimpleWeb namespace SimpleWeb {
{
template<class socket_type> template <class socket_type>
class Server : public ServerBase<socket_type> {}; class Server : public ServerBase<socket_type> {};
typedef boost::asio::ip::tcp::socket HTTP; using HTTP = asio::ip::tcp::socket;
template<> template <>
class Server<HTTP> : public ServerBase<HTTP> class Server<HTTP> : public ServerBase<HTTP> {
{
public: public:
Server() : ServerBase<HTTP>::ServerBase(80) Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
{}
protected: protected:
virtual void accept() void accept() override {
{ auto session = std::make_shared<Session>(create_connection(*io_service));
//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) acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
{ auto lock = session->connection->handler_runner->continue_lock();
//Immediately start accepting a new connection (if io_service hasn't been stopped) if(!lock)
if (ec != boost::asio::error::operation_aborted) return;
accept();
if (!ec) // Immediately start accepting a new connection (unless io_service has been stopped)
{ if(ec != asio::error::operation_aborted)
boost::asio::ip::tcp::no_delay option(true); this->accept();
socket->set_option(option);
this->read_request_and_content(socket); 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) else if(this->on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec); this->on_error(session->request, ec);
}); });
} }
}; };
} } // namespace SimpleWeb
#endif //SERVER_HTTP_HPP

View file

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

View file

@ -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

View file

@ -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

View file

@ -1,36 +1,74 @@
#include <iostream> #include <iostream>
#include <Kbhit.h> #include <Kbhit.h>
#include <RakSleep.h> #include <RakSleep.h>
#include <sol.hpp>
#include "MasterServer.hpp" #include "MasterServer.hpp"
#include "RestServer.hpp" #include "RestServer.hpp"
#include "AdminRest.hpp"
using namespace RakNet; using namespace RakNet;
using namespace std; using namespace std;
unique_ptr<RestServer> restServer; unique_ptr<RestServer> restServer;
unique_ptr<MasterServer> masterServer; shared_ptr<MasterServer> masterServer;
bool run = true; unique_ptr<AdminRest> restAdminServer;
int main() int main(int argc, char* argv[])
{ {
masterServer.reset(new MasterServer(2000, 25560)); if (argc != 2)
restServer.reset(new RestServer(8080, masterServer->GetServers())); 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*/){ auto onExit = [](int /*sig*/){
restServer->stop(); restServer->stop();
restAdminServer->stop();
masterServer->luaStuff([](sol::state &state) {
sol::protected_function func = state["OnExit"];
if (func.valid())
func.call();
});
masterServer->Stop(false); masterServer->Stop(false);
masterServer->Wait(); masterServer->Wait();
run = false;
}; };
signal(SIGINT, onExit); signal(SIGINT, onExit);
signal(SIGTERM, onExit); signal(SIGTERM, onExit);
masterServer->Start(); masterServer->Start();
restServer->start();
thread server_thread([]() { restServer->start(); }); restAdminServer->start();
server_thread.join();
masterServer->Wait(); masterServer->Wait();
return 0; return 0;

View file

@ -874,6 +874,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
{ {
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles; std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string gameFile("");
std::time_t defaultTime = 0; std::time_t defaultTime = 0;
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
@ -889,7 +890,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
multistrmap::const_iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for (int i=0; it != ini.end(); i++) for (int i=0; it != ini.end(); i++)
{ {
std::string gameFile = baseGameFile; gameFile = baseGameFile;
gameFile.append(std::to_string(i)); gameFile.append(std::to_string(i));
it = ini.find(gameFile); it = ini.find(gameFile);

View file

@ -5,9 +5,6 @@
#include <QLocalSocket> #include <QLocalSocket>
#include <QMessageBox> #include <QMessageBox>
#include <components/crashcatcher/crashcatcher.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
@ -21,16 +18,12 @@
using namespace Fallback; using namespace Fallback;
CS::Editor::Editor (int argc, char **argv) CS::Editor::Editor ()
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""), mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager), mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{ {
// install the crash handler as soon as possible. note that the log path
// does not depend on config being read.
crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string());
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(); std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
setupDataFiles (config.first); setupDataFiles (config.first);

View file

@ -66,7 +66,7 @@ namespace CS
public: public:
Editor (int argc, char **argv); Editor ();
~Editor (); ~Editor ();
bool makeIPCServer(); bool makeIPCServer();

View file

@ -8,9 +8,8 @@
#include <QIcon> #include <QIcon>
#include <QMetaType> #include <QMetaType>
#include <components/misc/debugging.hpp>
#include "model/doc/messages.hpp" #include "model/doc/messages.hpp"
#include "model/world/universalid.hpp" #include "model/world/universalid.hpp"
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -42,43 +41,45 @@ class Application : public QApplication
Application (int& argc, char *argv[]) : QApplication (argc, argv) {} Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
}; };
int runApplication(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif #endif
// To allow background thread drawing in OSG try
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor(argc, argv);
if(!editor.makeIPCServer())
{ {
editor.connectToIPCServer(); // To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath());
#endif
application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0; return 0;
} }
return editor.run();
}
int main(int argc, char *argv[])
{
return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log");
} }

View file

@ -320,13 +320,12 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int, bool)), this, SIGNAL (operationDone (int, bool))); connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)), connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*))); this, SIGNAL (mergeDone (CSMDoc::Document*)));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool))); connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect ( connect (
&mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)), &mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
@ -438,7 +437,7 @@ void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
std::cout << message.mMessage << std::endl; std::cout << message.mMessage << std::endl;
} }
void CSMDoc::Document::operationDone2 (int type, bool failed) void CSMDoc::Document::operationDone (int type, bool failed)
{ {
if (type==CSMDoc::State_Saving && !failed) if (type==CSMDoc::State_Saving && !failed)
mDirty = false; mDirty = false;

View file

@ -168,15 +168,13 @@ namespace CSMDoc
/// document. This signal must be handled to avoid a leak. /// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document); void mergeDone (CSMDoc::Document *document);
void operationDone (int type, bool failed);
private slots: private slots:
void modificationStateChanged (bool clean); void modificationStateChanged (bool clean);
void reportMessage (const CSMDoc::Message& message, int type); void reportMessage (const CSMDoc::Message& message, int type);
void operationDone2 (int type, bool failed); void operationDone (int type, bool failed);
void runStateChanged(); void runStateChanged();

View file

@ -123,7 +123,6 @@ void CSMPrefs::State::declare()
declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues); declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues);
declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues); declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues);
declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues); declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues);
declareBool("ignore-base-records", "Ignore base records in verifier", false);
declareCategory ("Search & Replace"); declareCategory ("Search & Replace");
declareInt ("char-before", "Characters before search string", 10). declareInt ("char-before", "Characters before search string", 10).
@ -201,9 +200,6 @@ void CSMPrefs::State::declare()
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1); declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Rendering"); declareCategory ("Rendering");
declareInt ("framerate-limit", "FPS limit", 60).
setTooltip("Framerate limit in 3D preview windows. Zero value means \"unlimited\".").
setRange(0, 10000);
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170); declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool ("camera-ortho", "Orthographic projection for camera", false); declareBool ("camera-ortho", "Orthographic projection for camera", false);
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100). declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).

View file

@ -5,20 +5,14 @@
#include <components/esm/loadbsgn.hpp> #include <components/esm/loadbsgn.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns) CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
: mBirthsigns (birthsigns) : mBirthsigns (birthsigns)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::BirthsignCheckStage::setup() int CSMTools::BirthsignCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mBirthsigns.getSize(); return mBirthsigns.getSize();
} }
@ -26,8 +20,7 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag
{ {
const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage); const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::BirthSign& birthsign = record.get(); const ESM::BirthSign& birthsign = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class BirthsignCheckStage : public CSMDoc::Stage class BirthsignCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns; const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
bool mIgnoreBaseRecords;
public: public:

View file

@ -1,7 +1,5 @@
#include "bodypartcheck.hpp" #include "bodypartcheck.hpp"
#include "../prefs/state.hpp"
CSMTools::BodyPartCheckStage::BodyPartCheckStage( CSMTools::BodyPartCheckStage::BodyPartCheckStage(
const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts, const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,
const CSMWorld::Resources &meshes, const CSMWorld::Resources &meshes,
@ -9,14 +7,10 @@ CSMTools::BodyPartCheckStage::BodyPartCheckStage(
mBodyParts(bodyParts), mBodyParts(bodyParts),
mMeshes(meshes), mMeshes(meshes),
mRaces(races) mRaces(races)
{ { }
mIgnoreBaseRecords = false;
}
int CSMTools::BodyPartCheckStage::setup() int CSMTools::BodyPartCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mBodyParts.getSize(); return mBodyParts.getSize();
} }
@ -24,8 +18,7 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
{ {
const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage); const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if ( record.isDeleted() )
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::BodyPart &bodyPart = record.get(); const ESM::BodyPart &bodyPart = record.get();

View file

@ -17,7 +17,6 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::BodyPart> &mBodyParts; const CSMWorld::IdCollection<ESM::BodyPart> &mBodyParts;
const CSMWorld::Resources &mMeshes; const CSMWorld::Resources &mMeshes;
const CSMWorld::IdCollection<ESM::Race> &mRaces; const CSMWorld::IdCollection<ESM::Race> &mRaces;
bool mIgnoreBaseRecords;
public: public:
BodyPartCheckStage( BodyPartCheckStage(

View file

@ -6,20 +6,14 @@
#include <components/esm/loadclas.hpp> #include <components/esm/loadclas.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes) CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)
: mClasses (classes) : mClasses (classes)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::ClassCheckStage::setup() int CSMTools::ClassCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mClasses.getSize(); return mClasses.getSize();
} }
@ -27,8 +21,7 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage); const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Class& class_ = record.get(); const ESM::Class& class_ = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class ClassCheckStage : public CSMDoc::Stage class ClassCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;
bool mIgnoreBaseRecords;
public: public:

View file

@ -6,20 +6,14 @@
#include <components/esm/loadfact.hpp> #include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions) CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)
: mFactions (factions) : mFactions (factions)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::FactionCheckStage::setup() int CSMTools::FactionCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mFactions.getSize(); return mFactions.getSize();
} }
@ -27,8 +21,7 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
{ {
const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage); const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Faction& faction = record.get(); const ESM::Faction& faction = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class FactionCheckStage : public CSMDoc::Stage class FactionCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mIgnoreBaseRecords;
public: public:

View file

@ -2,20 +2,14 @@
#include <sstream> #include <sstream>
#include "../prefs/state.hpp"
#include "../world/defaultgmsts.hpp" #include "../world/defaultgmsts.hpp"
CSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings) CSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings)
: mGameSettings(gameSettings) : mGameSettings(gameSettings)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::GmstCheckStage::setup() int CSMTools::GmstCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mGameSettings.getSize(); return mGameSettings.getSize();
} }
@ -23,8 +17,7 @@ void CSMTools::GmstCheckStage::perform(int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage); const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::GameSetting& gmst = record.get(); const ESM::GameSetting& gmst = record.get();

View file

@ -25,7 +25,6 @@ namespace CSMTools
private: private:
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings; const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
bool mIgnoreBaseRecords;
std::string varTypeToString(ESM::VarType); std::string varTypeToString(ESM::VarType);

View file

@ -3,19 +3,13 @@
#include <set> #include <set>
#include <sstream> #include <sstream>
#include "../prefs/state.hpp"
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals, CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
const CSMWorld::InfoCollection& journalInfos) const CSMWorld::InfoCollection& journalInfos)
: mJournals(journals), mJournalInfos(journalInfos) : mJournals(journals), mJournalInfos(journalInfos)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::JournalCheckStage::setup() int CSMTools::JournalCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mJournals.getSize(); return mJournals.getSize();
} }
@ -23,8 +17,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage); const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (journalRecord.isDeleted())
if ((mIgnoreBaseRecords && journalRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || journalRecord.isDeleted())
return; return;
const ESM::Dialogue &journal = journalRecord.get(); const ESM::Dialogue &journal = journalRecord.get();
@ -50,10 +43,6 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
statusNamedCount += 1; statusNamedCount += 1;
} }
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly)
continue;
if (journalInfo.mResponse.empty()) if (journalInfo.mResponse.empty())
{ {
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);

View file

@ -28,7 +28,6 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals; const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::InfoCollection& mJournalInfos; const CSMWorld::InfoCollection& mJournalInfos;
bool mIgnoreBaseRecords;
}; };
} }

View file

@ -2,8 +2,6 @@
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp"
#include "../world/resources.hpp" #include "../world/resources.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
@ -79,26 +77,16 @@ CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollect
mReferenceables(referenceables), mReferenceables(referenceables),
mIcons(icons), mIcons(icons),
mTextures(textures) mTextures(textures)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::MagicEffectCheckStage::setup() int CSMTools::MagicEffectCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mMagicEffects.getSize(); return mMagicEffects.getSize();
} }
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages) void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)
{ {
const CSMWorld::Record<ESM::MagicEffect> &record = mMagicEffects.getRecord(stage); ESM::MagicEffect effect = mMagicEffects.getRecord(stage).get();
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mData.mBaseCost < 0.0f) if (effect.mData.mBaseCost < 0.0f)

View file

@ -24,7 +24,6 @@ namespace CSMTools
const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::RefIdCollection &mReferenceables;
const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures; const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords;
private: private:
bool isTextureExists(const std::string &texture, bool isIcon) const; bool isTextureExists(const std::string &texture, bool isIcon) const;

View file

@ -3,8 +3,6 @@
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "../world/subcellcollection.hpp" #include "../world/subcellcollection.hpp"
@ -12,14 +10,10 @@
CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids) CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)
: mPathgrids (pathgrids) : mPathgrids (pathgrids)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::PathgridCheckStage::setup() int CSMTools::PathgridCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mPathgrids.getSize(); return mPathgrids.getSize();
} }
@ -27,8 +21,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
{ {
const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage); const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const CSMWorld::Pathgrid& pathgrid = record.get(); const CSMWorld::Pathgrid& pathgrid = record.get();

View file

@ -25,7 +25,6 @@ namespace CSMTools
{ {
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid, const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,
CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids; CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids;
bool mIgnoreBaseRecords;
public: public:

View file

@ -4,8 +4,6 @@
#include <components/esm/loadrace.hpp> #include <components/esm/loadrace.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages) void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages)
@ -17,14 +15,6 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
const ESM::Race& race = record.get(); const ESM::Race& race = record.get();
// Consider mPlayable flag even when "Base" records are ignored
if (race.mData.mFlags & 0x1)
mPlayable = true;
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly)
return;
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);
// test for empty name and description // test for empty name and description
@ -48,6 +38,10 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
if (race.mData.mWeight.mFemale<0) if (race.mData.mWeight.mFemale<0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight"));
// remember playable flag
if (race.mData.mFlags & 0x1)
mPlayable = true;
/// \todo check data members that can't be edited in the table view /// \todo check data members that can't be edited in the table view
} }
@ -61,15 +55,11 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races) CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
: mRaces (races), mPlayable (false) : mRaces (races), mPlayable (false)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::RaceCheckStage::setup() int CSMTools::RaceCheckStage::setup()
{ {
mPlayable = false; mPlayable = false;
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mRaces.getSize()+1; return mRaces.getSize()+1;
} }

View file

@ -14,7 +14,6 @@ namespace CSMTools
{ {
const CSMWorld::IdCollection<ESM::Race>& mRaces; const CSMWorld::IdCollection<ESM::Race>& mRaces;
bool mPlayable; bool mPlayable;
bool mIgnoreBaseRecords;
void performPerRecord (int stage, CSMDoc::Messages& messages); void performPerRecord (int stage, CSMDoc::Messages& messages);

View file

@ -2,8 +2,6 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "../prefs/state.hpp"
#include "../world/record.hpp" #include "../world/record.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -20,7 +18,6 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
mScripts(scripts), mScripts(scripts),
mPlayerPresent(false) mPlayerPresent(false)
{ {
mIgnoreBaseRecords = false;
} }
void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages)
@ -231,8 +228,6 @@ void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& me
int CSMTools::ReferenceableCheckStage::setup() int CSMTools::ReferenceableCheckStage::setup()
{ {
mPlayerPresent = false; mPlayerPresent = false;
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mReferencables.getSize() + 1; return mReferencables.getSize() + 1;
} }
@ -243,8 +238,7 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Book& book = (dynamic_cast<const CSMWorld::Record<ESM::Book>& >(baseRecord)).get(); const ESM::Book& book = (dynamic_cast<const CSMWorld::Record<ESM::Book>& >(baseRecord)).get();
@ -263,8 +257,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get(); const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
@ -285,8 +278,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Potion& potion = (dynamic_cast<const CSMWorld::Record<ESM::Potion>& >(baseRecord)).get(); const ESM::Potion& potion = (dynamic_cast<const CSMWorld::Record<ESM::Potion>& >(baseRecord)).get();
@ -307,8 +299,7 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Apparatus& apparatus = (dynamic_cast<const CSMWorld::Record<ESM::Apparatus>& >(baseRecord)).get(); const ESM::Apparatus& apparatus = (dynamic_cast<const CSMWorld::Record<ESM::Apparatus>& >(baseRecord)).get();
@ -329,8 +320,7 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Armor& armor = (dynamic_cast<const CSMWorld::Record<ESM::Armor>& >(baseRecord)).get(); const ESM::Armor& armor = (dynamic_cast<const CSMWorld::Record<ESM::Armor>& >(baseRecord)).get();
@ -357,8 +347,7 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get(); const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
@ -376,8 +365,7 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get(); const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
@ -409,8 +397,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get(); const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
@ -486,8 +473,7 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get(); const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
@ -511,8 +497,7 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get(); const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
@ -531,9 +516,10 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted()) {
return; return;
}
const ESM::CreatureLevList& CreatureLevList = (dynamic_cast<const CSMWorld::Record<ESM::CreatureLevList>& >(baseRecord)).get(); const ESM::CreatureLevList& CreatureLevList = (dynamic_cast<const CSMWorld::Record<ESM::CreatureLevList>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/
@ -548,9 +534,10 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted()) {
return; return;
}
const ESM::ItemLevList& ItemLevList = (dynamic_cast<const CSMWorld::Record<ESM::ItemLevList>& >(baseRecord)).get(); const ESM::ItemLevList& ItemLevList = (dynamic_cast<const CSMWorld::Record<ESM::ItemLevList>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId);
@ -564,8 +551,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Light& light = (dynamic_cast<const CSMWorld::Record<ESM::Light>& >(baseRecord)).get(); const ESM::Light& light = (dynamic_cast<const CSMWorld::Record<ESM::Light>& >(baseRecord)).get();
@ -588,8 +574,7 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Lockpick& lockpick = (dynamic_cast<const CSMWorld::Record<ESM::Lockpick>& >(baseRecord)).get(); const ESM::Lockpick& lockpick = (dynamic_cast<const CSMWorld::Record<ESM::Lockpick>& >(baseRecord)).get();
@ -610,8 +595,7 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Miscellaneous& miscellaneous = (dynamic_cast<const CSMWorld::Record<ESM::Miscellaneous>& >(baseRecord)).get(); const ESM::Miscellaneous& miscellaneous = (dynamic_cast<const CSMWorld::Record<ESM::Miscellaneous>& >(baseRecord)).get();
@ -635,14 +619,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
const ESM::NPC& npc = (dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(baseRecord)).get(); const ESM::NPC& npc = (dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(baseRecord)).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId);
//Detect if player is present
if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl?
mPlayerPresent = true;
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly)
return;
short level(npc.mNpdt.mLevel); short level(npc.mNpdt.mLevel);
char disposition(npc.mNpdt.mDisposition); char disposition(npc.mNpdt.mDisposition);
char reputation(npc.mNpdt.mReputation); char reputation(npc.mNpdt.mReputation);
@ -650,6 +626,10 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
//Don't know what unknown is for //Don't know what unknown is for
int gold(npc.mNpdt.mGold); int gold(npc.mNpdt.mGold);
//Detect if player is present
if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl?
mPlayerPresent = true;
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{ {
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
@ -748,8 +728,7 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Weapon& weapon = (dynamic_cast<const CSMWorld::Record<ESM::Weapon>& >(baseRecord)).get(); const ESM::Weapon& weapon = (dynamic_cast<const CSMWorld::Record<ESM::Weapon>& >(baseRecord)).get();
@ -829,8 +808,7 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Probe& probe = (dynamic_cast<const CSMWorld::Record<ESM::Probe>& >(baseRecord)).get(); const ESM::Probe& probe = (dynamic_cast<const CSMWorld::Record<ESM::Probe>& >(baseRecord)).get();
@ -849,8 +827,7 @@ void CSMTools::ReferenceableCheckStage::repairCheck (
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Repair& repair = (dynamic_cast<const CSMWorld::Record<ESM::Repair>& >(baseRecord)).get(); const ESM::Repair& repair = (dynamic_cast<const CSMWorld::Record<ESM::Repair>& >(baseRecord)).get();
@ -869,8 +846,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
{ {
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage); const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (baseRecord.isDeleted())
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return; return;
const ESM::Static& staticElement = (dynamic_cast<const CSMWorld::Record<ESM::Static>& >(baseRecord)).get(); const ESM::Static& staticElement = (dynamic_cast<const CSMWorld::Record<ESM::Static>& >(baseRecord)).get();

View file

@ -82,7 +82,6 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts; const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mPlayerPresent; bool mPlayerPresent;
bool mIgnoreBaseRecords;
}; };
} }
#endif // REFERENCEABLECHECKSTAGE_H #endif // REFERENCEABLECHECKSTAGE_H

View file

@ -1,7 +1,5 @@
#include "referencecheck.hpp" #include "referencecheck.hpp"
#include "../prefs/state.hpp"
CSMTools::ReferenceCheckStage::ReferenceCheckStage( CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::RefCollection& references, const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables, const CSMWorld::RefIdCollection& referencables,
@ -14,15 +12,13 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
mCells(cells), mCells(cells),
mFactions(factions) mFactions(factions)
{ {
mIgnoreBaseRecords = false;
} }
void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages) void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages)
{ {
const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage); const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::CellRef& cellRef = record.get();
@ -104,7 +100,5 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
int CSMTools::ReferenceCheckStage::setup() int CSMTools::ReferenceCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mReferences.getSize(); return mReferences.getSize();
} }

View file

@ -23,7 +23,6 @@ namespace CSMTools
const CSMWorld::RefIdData& mDataSet; const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells; const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mIgnoreBaseRecords;
}; };
} }

View file

@ -5,20 +5,14 @@
#include <components/esm/loadregn.hpp> #include <components/esm/loadregn.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions) CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)
: mRegions (regions) : mRegions (regions)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::RegionCheckStage::setup() int CSMTools::RegionCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mRegions.getSize(); return mRegions.getSize();
} }
@ -26,8 +20,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage); const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Region& region = record.get(); const ESM::Region& region = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class RegionCheckStage : public CSMDoc::Stage class RegionCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Region>& mRegions; const CSMWorld::IdCollection<ESM::Region>& mRegions;
bool mIgnoreBaseRecords;
public: public:

View file

@ -60,8 +60,6 @@ CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)
Compiler::registerExtensions (mExtensions); Compiler::registerExtensions (mExtensions);
mContext.setExtensions (&mExtensions); mContext.setExtensions (&mExtensions);
mIgnoreBaseRecords = false;
} }
int CSMTools::ScriptCheckStage::setup() int CSMTools::ScriptCheckStage::setup()
@ -80,25 +78,17 @@ int CSMTools::ScriptCheckStage::setup()
mId.clear(); mId.clear();
Compiler::ErrorHandler::reset(); Compiler::ErrorHandler::reset();
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mDocument.getData().getScripts().getSize(); return mDocument.getData().getScripts().getSize();
} }
void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Script> &record = mDocument.getData().getScripts().getRecord(stage);
mId = mDocument.getData().getScripts().getId (stage); mId = mDocument.getData().getScripts().getId (stage);
if (mDocument.isBlacklisted ( if (mDocument.isBlacklisted (
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId))) CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId)))
return; return;
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
mMessages = &messages; mMessages = &messages;
switch (mWarningMode) switch (mWarningMode)
@ -110,8 +100,10 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
try try
{ {
mFile = record.get().mId; const CSMWorld::Data& data = mDocument.getData();
std::istringstream input (record.get().mScriptText);
mFile = data.getScripts().getRecord (stage).get().mId;
std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText);
Compiler::Scanner scanner (*this, input, mContext.getExtensions()); Compiler::Scanner scanner (*this, input, mContext.getExtensions());

View file

@ -32,7 +32,6 @@ namespace CSMTools
std::string mFile; std::string mFile;
CSMDoc::Messages *mMessages; CSMDoc::Messages *mMessages;
WarningMode mWarningMode; WarningMode mWarningMode;
bool mIgnoreBaseRecords;
CSMDoc::Message::Severity getSeverity (Type type); CSMDoc::Message::Severity getSeverity (Type type);

View file

@ -22,8 +22,7 @@ void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
int pos = 0; int pos = 0;
Qt::CaseSensitivity caseSensitivity = mCase ? Qt::CaseSensitive : Qt::CaseInsensitive; while ((pos = text.indexOf (search, pos, Qt::CaseInsensitive))!=-1)
while ((pos = text.indexOf (search, pos, caseSensitivity))!=-1)
{ {
std::ostringstream hint; std::ostringstream hint;
hint hint
@ -121,26 +120,25 @@ QString CSMTools::Search::flatten (const QString& text) const
return flat; return flat;
} }
CSMTools::Search::Search() : mType (Type_None), mValue (0), mCase (false), mIdColumn (0), mTypeColumn (0), CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0),
mPaddingBefore (10), mPaddingAfter (10) {} mPaddingBefore (10), mPaddingAfter (10) {}
CSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value) CSMTools::Search::Search (Type type, const std::string& value)
: mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_Text && type!=Type_Id) if (type!=Type_Text && type!=Type_Id)
throw std::logic_error ("Invalid search parameter (string)"); throw std::logic_error ("Invalid search parameter (string)");
} }
CSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value) CSMTools::Search::Search (Type type, const QRegExp& value)
: mType (type), mRegExp (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
mRegExp.setCaseSensitivity(mCase ? Qt::CaseSensitive : Qt::CaseInsensitive);
if (type!=Type_TextRegEx && type!=Type_IdRegEx) if (type!=Type_TextRegEx && type!=Type_IdRegEx)
throw std::logic_error ("Invalid search parameter (RegExp)"); throw std::logic_error ("Invalid search parameter (RegExp)");
} }
CSMTools::Search::Search (Type type, bool caseSensitive, int value) CSMTools::Search::Search (Type type, int value)
: mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10) : mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
{ {
if (type!=Type_RecordState) if (type!=Type_RecordState)
throw std::logic_error ("invalid search parameter (int)"); throw std::logic_error ("invalid search parameter (int)");

View file

@ -43,7 +43,6 @@ namespace CSMTools
std::string mText; std::string mText;
QRegExp mRegExp; QRegExp mRegExp;
int mValue; int mValue;
bool mCase;
std::set<int> mColumns; std::set<int> mColumns;
int mIdColumn; int mIdColumn;
int mTypeColumn; int mTypeColumn;
@ -68,11 +67,11 @@ namespace CSMTools
Search(); Search();
Search (Type type, bool caseSensitive, const std::string& value); Search (Type type, const std::string& value);
Search (Type type, bool caseSensitive, const QRegExp& value); Search (Type type, const QRegExp& value);
Search (Type type, bool caseSensitive, int value); Search (Type type, int value);
// Configure search for the specified model. // Configure search for the specified model.
void configure (const CSMWorld::IdTableBase *model); void configure (const CSMWorld::IdTableBase *model);

View file

@ -4,20 +4,14 @@
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills) CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
: mSkills (skills) : mSkills (skills)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::SkillCheckStage::setup() int CSMTools::SkillCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSkills.getSize(); return mSkills.getSize();
} }
@ -25,8 +19,7 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Skill>& record = mSkills.getRecord (stage); const CSMWorld::Record<ESM::Skill>& record = mSkills.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Skill& skill = record.get(); const ESM::Skill& skill = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class SkillCheckStage : public CSMDoc::Stage class SkillCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Skill>& mSkills; const CSMWorld::IdCollection<ESM::Skill>& mSkills;
bool mIgnoreBaseRecords;
public: public:

View file

@ -4,20 +4,14 @@
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds) CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds) : mSounds (sounds)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::SoundCheckStage::setup() int CSMTools::SoundCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSounds.getSize(); return mSounds.getSize();
} }
@ -25,8 +19,7 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage); const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Sound& sound = record.get(); const ESM::Sound& sound = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class SoundCheckStage : public CSMDoc::Stage class SoundCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Sound>& mSounds; const CSMWorld::IdCollection<ESM::Sound>& mSounds;
bool mIgnoreBaseRecords;
public: public:

View file

@ -2,8 +2,6 @@
#include <sstream> #include <sstream>
#include "../prefs/state.hpp"
#include "../world/refiddata.hpp" #include "../world/refiddata.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -13,24 +11,20 @@ CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ES
: mSoundGens(soundGens), : mSoundGens(soundGens),
mSounds(sounds), mSounds(sounds),
mReferenceables(referenceables) mReferenceables(referenceables)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::SoundGenCheckStage::setup() int CSMTools::SoundGenCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSoundGens.getSize(); return mSoundGens.getSize();
} }
void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages) void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages)
{ {
const CSMWorld::Record<ESM::SoundGenerator> &record = mSoundGens.getRecord(stage); const CSMWorld::Record<ESM::SoundGenerator> &record = mSoundGens.getRecord(stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records {
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
}
const ESM::SoundGenerator& soundGen = record.get(); const ESM::SoundGenerator& soundGen = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);

View file

@ -13,7 +13,6 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens; const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
const CSMWorld::IdCollection<ESM::Sound> &mSounds; const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables; const CSMWorld::RefIdCollection &mReferenceables;
bool mIgnoreBaseRecords;
public: public:
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens, SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,

View file

@ -5,20 +5,14 @@
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells) CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)
: mSpells (spells) : mSpells (spells)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::SpellCheckStage::setup() int CSMTools::SpellCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSpells.getSize(); return mSpells.getSize();
} }
@ -26,8 +20,7 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage); const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
const ESM::Spell& spell = record.get(); const ESM::Spell& spell = record.get();

View file

@ -13,7 +13,6 @@ namespace CSMTools
class SpellCheckStage : public CSMDoc::Stage class SpellCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Spell>& mSpells; const CSMWorld::IdCollection<ESM::Spell>& mSpells;
bool mIgnoreBaseRecords;
public: public:

View file

@ -1,23 +1,18 @@
#include "startscriptcheck.hpp" #include "startscriptcheck.hpp"
#include "../prefs/state.hpp"
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
CSMTools::StartScriptCheckStage::StartScriptCheckStage ( CSMTools::StartScriptCheckStage::StartScriptCheckStage (
const CSMWorld::IdCollection<ESM::StartScript>& startScripts, const CSMWorld::IdCollection<ESM::StartScript>& startScripts,
const CSMWorld::IdCollection<ESM::Script>& scripts) const CSMWorld::IdCollection<ESM::Script>& scripts)
: mStartScripts (startScripts), mScripts (scripts) : mStartScripts (startScripts), mScripts (scripts)
{ {}
mIgnoreBaseRecords = false;
}
void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messages) void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messages)
{ {
const CSMWorld::Record<ESM::StartScript>& record = mStartScripts.getRecord (stage); const CSMWorld::Record<ESM::StartScript>& record = mStartScripts.getRecord (stage);
// Skip "Base" records (setting!) and "Deleted" records if (record.isDeleted())
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return; return;
std::string scriptId = record.get().mId; std::string scriptId = record.get().mId;
@ -31,7 +26,5 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa
int CSMTools::StartScriptCheckStage::setup() int CSMTools::StartScriptCheckStage::setup()
{ {
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mStartScripts.getSize(); return mStartScripts.getSize();
} }

View file

@ -14,7 +14,6 @@ namespace CSMTools
{ {
const CSMWorld::IdCollection<ESM::StartScript>& mStartScripts; const CSMWorld::IdCollection<ESM::StartScript>& mStartScripts;
const CSMWorld::IdCollection<ESM::Script>& mScripts; const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mIgnoreBaseRecords;
public: public:

View file

@ -2,8 +2,6 @@
#include <sstream> #include <sstream>
#include "../prefs/state.hpp"
#include "../world/infoselectwrapper.hpp" #include "../world/infoselectwrapper.hpp"
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage( CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
@ -31,9 +29,7 @@ CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
mTopics(topics), mTopics(topics),
mReferencables(referencables), mReferencables(referencables),
mSoundFiles(soundFiles) mSoundFiles(soundFiles)
{ {}
mIgnoreBaseRecords = false;
}
int CSMTools::TopicInfoCheckStage::setup() int CSMTools::TopicInfoCheckStage::setup()
{ {
@ -71,8 +67,6 @@ int CSMTools::TopicInfoCheckStage::setup()
} }
} }
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mTopicInfos.getSize(); return mTopicInfos.getSize();
} }
@ -80,8 +74,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
{ {
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage); const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records if (infoRecord.isDeleted())
if ((mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || infoRecord.isDeleted())
return; return;
const CSMWorld::Info& topicInfo = infoRecord.get(); const CSMWorld::Info& topicInfo = infoRecord.get();

View file

@ -65,8 +65,6 @@ namespace CSMTools
std::set<std::string> mCellNames; std::set<std::string> mCellNames;
bool mIgnoreBaseRecords;
// These return false when not successful and write an error // These return false when not successful and write an error
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);

View file

@ -88,7 +88,6 @@ namespace CSMWorld
Display_UnsignedInteger8, Display_UnsignedInteger8,
Display_Integer, Display_Integer,
Display_Float, Display_Float,
Display_Double,
Display_Var, Display_Var,
Display_GmstVarType, Display_GmstVarType,
Display_GlobalVarType, Display_GlobalVarType,

View file

@ -136,7 +136,7 @@ namespace CSMWorld
struct VarTypeColumn : public Column<ESXRecordT> struct VarTypeColumn : public Column<ESXRecordT>
{ {
VarTypeColumn (ColumnBase::Display display) VarTypeColumn (ColumnBase::Display display)
: Column<ESXRecordT> (Columns::ColumnId_ValueType, display, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh) : Column<ESXRecordT> (Columns::ColumnId_ValueType, display)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
@ -161,7 +161,7 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct VarValueColumn : public Column<ESXRecordT> struct VarValueColumn : public Column<ESXRecordT>
{ {
VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh) {} VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var) {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
@ -1371,7 +1371,7 @@ namespace CSMWorld
RotColumn (ESM::Position ESXRecordT::* position, int index, bool door) RotColumn (ESM::Position ESXRecordT::* position, int index, bool door)
: Column<ESXRecordT> ( : Column<ESXRecordT> (
(door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index, (door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index,
ColumnBase::Display_Double), mPosition (position), mIndex (index) {} ColumnBase::Display_Float), mPosition (position), mIndex (index) {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {

View file

@ -84,28 +84,15 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole) if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
{ {
mIdCollection->setData (index.row(), index.column(), value); mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged(index, index);
// Modifying a value can also change the Modified status of a record.
int stateColumn = searchColumnIndex(Columns::ColumnId_Modification); int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);
if (stateColumn != -1) if (stateColumn != -1)
{ {
if (index.column() == stateColumn) QModelIndex stateIndex = this->index(index.row(), stateColumn);
{ emit dataChanged(stateIndex, stateIndex);
// modifying the state column can modify other values. we need to tell }
// views that the whole row has changed.
emit dataChanged(this->index(index.row(), 0),
this->index(index.row(), columnCount(index.parent())));
} else
{
emit dataChanged(index, index);
// Modifying a value can also change the Modified status of a record.
QModelIndex stateIndex = this->index(index.row(), stateColumn);
emit dataChanged(stateIndex, stateIndex);
}
} else
emit dataChanged(index, index);
return true; return true;
} }

View file

@ -184,11 +184,11 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float)); new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Double)); new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Float));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Double)); new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Float));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Double)); new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Float));
// Nested table // Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList, mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList,

View file

@ -101,39 +101,15 @@ void CSVDoc::View::setupFileMenu()
file->addAction(exit); file->addAction(exit);
} }
namespace
{
void updateUndoRedoAction(QAction *action, const std::string &settingsKey)
{
QKeySequence seq;
CSMPrefs::State::get().getShortcutManager().getSequence(settingsKey, seq);
action->setShortcut(seq);
}
}
void CSVDoc::View::undoActionChanged()
{
updateUndoRedoAction(mUndo, "document-edit-undo");
}
void CSVDoc::View::redoActionChanged()
{
updateUndoRedoAction(mRedo, "document-edit-redo");
}
void CSVDoc::View::setupEditMenu() void CSVDoc::View::setupEditMenu()
{ {
QMenu *edit = menuBar()->addMenu (tr ("Edit")); QMenu *edit = menuBar()->addMenu (tr ("Edit"));
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
setupShortcut("document-edit-undo", mUndo); setupShortcut("document-edit-undo", mUndo);
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
edit->addAction (mUndo); edit->addAction (mUndo);
mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); mRedo= mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
setupShortcut("document-edit-redo", mRedo); setupShortcut("document-edit-redo", mRedo);
edit->addAction (mRedo); edit->addAction (mRedo);
@ -646,7 +622,6 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
} }
assert(view); assert(view);
view->setParent(this); view->setParent(this);
view->setEditLock (mDocument->getState() & CSMDoc::State_Locked);
mSubViews.append(view); // only after assert mSubViews.append(view); // only after assert
int minWidth = windows["minimum-width"].toInt(); int minWidth = windows["minimum-width"].toInt();

View file

@ -152,10 +152,6 @@ namespace CSVDoc
void settingChanged (const CSMPrefs::Setting *setting); void settingChanged (const CSMPrefs::Setting *setting);
void undoActionChanged();
void redoActionChanged();
void newView(); void newView();
void save(); void save();

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