From ea953286a5643bb87e534ab7f9d16ae4dc0f84f0 Mon Sep 17 00:00:00 2001 From: Jon Baker Date: Wed, 11 Apr 2012 03:11:49 +0100 Subject: [PATCH] Added fCraft Revision 1555; let's start again. Tomorrow. Bring coffee. --- APIReference.shfbproj | 63 + CHANGELOG.txt | 1653 ++++++++ ConfigGUI/AddWorldPopup.Designer.cs | 2580 +++++++++++ ConfigGUI/AddWorldPopup.cs | 968 +++++ ConfigGUI/AddWorldPopup.resx | 132 + ConfigGUI/ChatBackground.png | Bin 0 -> 4199 bytes ConfigGUI/ChatPreview.Designer.cs | 40 + ConfigGUI/ChatPreview.cs | 126 + ConfigGUI/ChatPreview.resx | 120 + ConfigGUI/ColorPicker.Designer.cs | 305 ++ ConfigGUI/ColorPicker.cs | 65 + ConfigGUI/ColorPicker.resx | 1155 +++++ ConfigGUI/ConfigGUI.csproj | 183 + ConfigGUI/CustomPictureBox.cs | 20 + ConfigGUI/DeleteRankPopup.Designer.cs | 115 + ConfigGUI/DeleteRankPopup.cs | 31 + ConfigGUI/DeleteRankPopup.resx | 125 + ConfigGUI/KeywordPicker.Designer.cs | 71 + ConfigGUI/KeywordPicker.cs | 53 + ConfigGUI/KeywordPicker.resx | 120 + ConfigGUI/MainForm.Adapter.cs | 665 +++ ConfigGUI/MainForm.Designer.cs | 3774 +++++++++++++++++ ConfigGUI/MainForm.ToolTips.cs | 698 +++ ConfigGUI/MainForm.cs | 1551 +++++++ ConfigGUI/MainForm.resx | 1187 ++++++ ConfigGUI/MinecraftFont/LICENSE.txt | 9 + ConfigGUI/MinecraftFont/README.txt | 28 + ConfigGUI/MinecraftFont/minecraft.ttf | Bin 0 -> 17676 bytes ConfigGUI/PermissionLimitBox.Designer.cs | 68 + ConfigGUI/PermissionLimitBox.cs | 69 + ConfigGUI/PermissionLimitBox.resx | 120 + ConfigGUI/Program.cs | 46 + ConfigGUI/Properties/AssemblyInfo.cs | 40 + ConfigGUI/Properties/Resources.Designer.cs | 77 + ConfigGUI/Properties/Resources.resx | 127 + ConfigGUI/SortableBindingList.cs | 192 + ConfigGUI/TextEditorPopup.Designer.cs | 143 + ConfigGUI/TextEditorPopup.cs | 70 + ConfigGUI/TextEditorPopup.resx | 120 + ConfigGUI/UpdaterSettingsPopup.Designer.cs | 235 + ConfigGUI/UpdaterSettingsPopup.cs | 97 + ConfigGUI/UpdaterSettingsPopup.resx | 120 + ConfigGUI/WorldListEntry.cs | 432 ++ ConfigGUI/app.config | 3 + ConfigGUI/fcraft_config.ico | Bin 0 -> 61798 bytes README.txt | 250 ++ ServerCLI/Program.cs | 230 + ServerCLI/Properties/AssemblyInfo.cs | 40 + ServerCLI/ServerCLI.csproj | 67 + ServerCLI/app.config | 3 + ServerCLI/fcraft_console.ico | Bin 0 -> 61798 bytes ServerGUI/ConsoleBox.cs | 64 + ServerGUI/MainForm.Designer.cs | 150 + ServerGUI/MainForm.cs | 255 ++ ServerGUI/MainForm.resx | 1155 +++++ ServerGUI/Program.cs | 44 + ServerGUI/Properties/AssemblyInfo.cs | 40 + ServerGUI/ServerGUI.csproj | 94 + ServerGUI/UpdateWindow.Designer.cs | 178 + ServerGUI/UpdateWindow.cs | 112 + ServerGUI/UpdateWindow.resx | 120 + ServerGUI/app.config | 3 + ServerGUI/f2k.ico | Bin 0 -> 61798 bytes UpdateInstaller/Program.cs | 253 ++ UpdateInstaller/Properties/AssemblyInfo.cs | 35 + .../Properties/Resources.Designer.cs | 70 + UpdateInstaller/Properties/Resources.resx | 124 + UpdateInstaller/UpdateInstaller.csproj | 130 + UpdateInstaller/ZipStorer.cs | 698 +++ UpdateInstaller/app.config | 3 + UpdateInstaller/fcraft_updater.ico | Bin 0 -> 61798 bytes UpdaterBuilder/Program.cs | 37 + UpdaterBuilder/Properties/AssemblyInfo.cs | 35 + UpdaterBuilder/UpdateBuilder.csproj | 109 + UpdaterBuilder/ZipStorer.cs | 700 +++ UpdaterBuilder/app.config | 3 + f2k.ico | Bin 0 -> 61798 bytes fCraft.sln | 110 + fCraft/AutoRank/AutoRankManager.cs | 170 + fCraft/AutoRank/Conditions.cs | 355 ++ fCraft/AutoRank/Criterion.cs | 68 + fCraft/Commands/BuildingCommands.cs | 1780 ++++++++ fCraft/Commands/ChatCommands.cs | 407 ++ fCraft/Commands/Command.cs | 236 ++ fCraft/Commands/CommandCategory.cs | 36 + fCraft/Commands/CommandDescriptor.cs | 151 + fCraft/Commands/CommandManager.cs | 323 ++ fCraft/Commands/InfoCommands.cs | 1294 ++++++ fCraft/Commands/MaintenanceCommands.cs | 1400 ++++++ fCraft/Commands/ModerationCommands.cs | 1347 ++++++ fCraft/Commands/WorldCommands.cs | 2592 +++++++++++ fCraft/Commands/ZoneCommands.cs | 606 +++ fCraft/Drawing/Axis.cs | 7 + fCraft/Drawing/BrushManager.cs | 84 + .../Brushes/AbstractPerlinNoiseBrush.cs | 146 + fCraft/Drawing/Brushes/CheckeredBrush.cs | 138 + fCraft/Drawing/Brushes/CloudyBrush.cs | 172 + fCraft/Drawing/Brushes/MarbledBrush.cs | 182 + fCraft/Drawing/Brushes/NormalBrush.cs | 131 + fCraft/Drawing/Brushes/RainbowBrush.cs | 81 + fCraft/Drawing/Brushes/RandomBrush.cs | 214 + fCraft/Drawing/Brushes/ReplaceBrush.cs | 176 + fCraft/Drawing/Brushes/ReplaceBrushBrush.cs | 175 + fCraft/Drawing/Brushes/ReplaceNotBrush.cs | 177 + fCraft/Drawing/CopyState.cs | 52 + fCraft/Drawing/DrawOpWithBrush.cs | 91 + fCraft/Drawing/DrawOperation.cs | 407 ++ fCraft/Drawing/DrawOps/CuboidDrawOperation.cs | 46 + .../DrawOps/CuboidHollowDrawOperation.cs | 91 + .../DrawOps/CuboidWireframeDrawOperation.cs | 102 + fCraft/Drawing/DrawOps/CutDrawOperation.cs | 85 + .../Drawing/DrawOps/EllipsoidDrawOperation.cs | 74 + .../DrawOps/EllipsoidHollowDrawOperation.cs | 151 + fCraft/Drawing/DrawOps/Fill2DDrawOperation.cs | 263 ++ fCraft/Drawing/DrawOps/LineDrawOperation.cs | 42 + fCraft/Drawing/DrawOps/PasteDrawOperation.cs | 158 + .../DrawOps/QuickPasteDrawOperation.cs | 19 + fCraft/Drawing/DrawOps/SphereDrawOperation.cs | 30 + .../DrawOps/SphereHollowDrawOperation.cs | 30 + fCraft/Drawing/DrawOps/TorusDrawOperation.cs | 88 + .../Drawing/DrawOps/TriangleDrawOperation.cs | 128 + .../DrawOps/TriangleWireframeDrawOperation.cs | 86 + fCraft/Drawing/DrawOps/UndoDrawOperation.cs | 91 + fCraft/Drawing/IBrush.cs | 81 + fCraft/Drawing/UndoState.cs | 65 + fCraft/MapConversion/IMapConverter.cs | 42 + fCraft/MapConversion/INIFile.cs | 75 + fCraft/MapConversion/MapD3.cs | 211 + fCraft/MapConversion/MapDAT.cs | 251 ++ fCraft/MapConversion/MapFCMv2.cs | 146 + fCraft/MapConversion/MapFCMv3.cs | 318 ++ fCraft/MapConversion/MapFCMv4.cs | 271 ++ fCraft/MapConversion/MapFormat.cs | 49 + fCraft/MapConversion/MapFormatException.cs | 11 + fCraft/MapConversion/MapJTE.cs | 165 + fCraft/MapConversion/MapMCSharp.cs | 280 ++ fCraft/MapConversion/MapMinerCPP.cs | 153 + fCraft/MapConversion/MapMyne.cs | 133 + fCraft/MapConversion/MapNBT.cs | 94 + fCraft/MapConversion/MapOpticraft.cs | 318 ++ fCraft/MapConversion/MapUtility.cs | 221 + fCraft/MapConversion/NBTag.cs | 539 +++ fCraft/Network/Heartbeat.cs | 314 ++ fCraft/Network/IPBanInfo.cs | 183 + fCraft/Network/IPBanList.cs | 651 +++ fCraft/Network/IRC.cs | 1102 +++++ fCraft/Network/IRCCommands.cs | 469 ++ fCraft/Network/IRCMessage.cs | 60 + fCraft/Network/LineWrapper.cs | 356 ++ fCraft/Network/OpCode.cs | 23 + fCraft/Network/Packet.cs | 55 + fCraft/Network/PacketWriter.cs | 234 + fCraft/Network/Player.Networking.cs | 1531 +++++++ fCraft/Player/Chat.cs | 368 ++ fCraft/Player/ChatTimer.cs | 184 + fCraft/Player/Permission.cs | 161 + fCraft/Player/Player.Events.cs | 470 ++ fCraft/Player/Player.cs | 1577 +++++++ fCraft/Player/PlayerConstants.cs | 232 + fCraft/Player/PlayerDB.cs | 813 ++++ fCraft/Player/PlayerEnumerable.cs | 688 +++ fCraft/Player/PlayerInfo.Actions.cs | 1057 +++++ fCraft/Player/PlayerInfo.Events.cs | 301 ++ fCraft/Player/PlayerInfo.cs | 1349 ++++++ fCraft/Player/PlayerOpException.cs | 300 ++ fCraft/Player/Position.cs | 105 + fCraft/Player/Rank.cs | 598 +++ fCraft/Player/RankManager.cs | 260 ++ fCraft/Player/SecurityController.cs | 399 ++ fCraft/Properties/AssemblyInfo.cs | 40 + fCraft/System/ArgKey.cs | 37 + fCraft/System/Config.cs | 1258 ++++++ fCraft/System/ConfigKey.Metadata.cs | 383 ++ fCraft/System/ConfigKey.cs | 537 +++ fCraft/System/ConfigSection.cs | 31 + fCraft/System/Logger.cs | 588 +++ fCraft/System/Scheduler.cs | 278 ++ fCraft/System/SchedulerTask.cs | 275 ++ fCraft/System/Server.Events.cs | 131 + fCraft/System/Server.cs | 1260 ++++++ fCraft/Utils/BoundingBox.cs | 191 + fCraft/Utils/Color.cs | 374 ++ fCraft/Utils/ConcurrentQueue.cs | 128 + fCraft/Utils/EventInterfaces.cs | 30 + fCraft/Utils/ExtensionMethods.cs | 606 +++ fCraft/Utils/IClassy.cs | 9 + fCraft/Utils/JetBrains.Annotations.cs | 351 ++ fCraft/Utils/LogRecorder.cs | 103 + fCraft/Utils/MetadataCollection.cs | 426 ++ fCraft/Utils/MonoCompat.cs | 122 + fCraft/Utils/Noise.cs | 553 +++ fCraft/Utils/Paths.cs | 375 ++ fCraft/Utils/PerlinNoise3D.cs | 168 + fCraft/Utils/Trie.cs | 1337 ++++++ fCraft/Utils/Updater.cs | 366 ++ fCraft/Utils/Vector3F.cs | 269 ++ fCraft/Utils/Vector3I.cs | 259 ++ fCraft/Utils/YesNoAuto.cs | 9 + fCraft/Utils/ZipStorer.cs | 730 ++++ fCraft/World/Block.cs | 62 + fCraft/World/BlockChangeContext.cs | 38 + fCraft/World/BlockDB.cs | 806 ++++ fCraft/World/BlockDBEntry.cs | 77 + fCraft/World/BlockUpdate.cs | 27 + fCraft/World/Forester.cs | 940 ++++ fCraft/World/Map.cs | 922 ++++ fCraft/World/MapGenerator.cs | 941 ++++ fCraft/World/MapGeneratorArgs.cs | 241 ++ fCraft/World/World.Events.cs | 85 + fCraft/World/World.cs | 696 +++ fCraft/World/WorldManager.cs | 789 ++++ fCraft/World/WorldOpException.cs | 167 + fCraft/World/Zone.cs | 245 ++ fCraft/World/ZoneCollection.cs | 345 ++ fCraft/app.config | 3 + fCraft/fCraft.csproj | 282 ++ fCraftGUI/AboutWindow.Designer.cs | 121 + fCraftGUI/AboutWindow.cs | 27 + fCraftGUI/AboutWindow.resx | 146 + fCraftGUI/IsoCat.cs | 377 ++ fCraftGUI/Properties/AssemblyInfo.cs | 35 + fCraftGUI/Properties/Resources.Designer.cs | 77 + fCraftGUI/Properties/Resources.resx | 127 + fCraftGUI/Tileset.png | Bin 0 -> 1187 bytes fCraftGUI/TilesetShadowed.png | Bin 0 -> 1189 bytes fCraftGUI/fCraftGUI.csproj | 90 + 226 files changed, 73543 insertions(+) create mode 100644 APIReference.shfbproj create mode 100644 CHANGELOG.txt create mode 100644 ConfigGUI/AddWorldPopup.Designer.cs create mode 100644 ConfigGUI/AddWorldPopup.cs create mode 100644 ConfigGUI/AddWorldPopup.resx create mode 100644 ConfigGUI/ChatBackground.png create mode 100644 ConfigGUI/ChatPreview.Designer.cs create mode 100644 ConfigGUI/ChatPreview.cs create mode 100644 ConfigGUI/ChatPreview.resx create mode 100644 ConfigGUI/ColorPicker.Designer.cs create mode 100644 ConfigGUI/ColorPicker.cs create mode 100644 ConfigGUI/ColorPicker.resx create mode 100644 ConfigGUI/ConfigGUI.csproj create mode 100644 ConfigGUI/CustomPictureBox.cs create mode 100644 ConfigGUI/DeleteRankPopup.Designer.cs create mode 100644 ConfigGUI/DeleteRankPopup.cs create mode 100644 ConfigGUI/DeleteRankPopup.resx create mode 100644 ConfigGUI/KeywordPicker.Designer.cs create mode 100644 ConfigGUI/KeywordPicker.cs create mode 100644 ConfigGUI/KeywordPicker.resx create mode 100644 ConfigGUI/MainForm.Adapter.cs create mode 100644 ConfigGUI/MainForm.Designer.cs create mode 100644 ConfigGUI/MainForm.ToolTips.cs create mode 100644 ConfigGUI/MainForm.cs create mode 100644 ConfigGUI/MainForm.resx create mode 100644 ConfigGUI/MinecraftFont/LICENSE.txt create mode 100644 ConfigGUI/MinecraftFont/README.txt create mode 100644 ConfigGUI/MinecraftFont/minecraft.ttf create mode 100644 ConfigGUI/PermissionLimitBox.Designer.cs create mode 100644 ConfigGUI/PermissionLimitBox.cs create mode 100644 ConfigGUI/PermissionLimitBox.resx create mode 100644 ConfigGUI/Program.cs create mode 100644 ConfigGUI/Properties/AssemblyInfo.cs create mode 100644 ConfigGUI/Properties/Resources.Designer.cs create mode 100644 ConfigGUI/Properties/Resources.resx create mode 100644 ConfigGUI/SortableBindingList.cs create mode 100644 ConfigGUI/TextEditorPopup.Designer.cs create mode 100644 ConfigGUI/TextEditorPopup.cs create mode 100644 ConfigGUI/TextEditorPopup.resx create mode 100644 ConfigGUI/UpdaterSettingsPopup.Designer.cs create mode 100644 ConfigGUI/UpdaterSettingsPopup.cs create mode 100644 ConfigGUI/UpdaterSettingsPopup.resx create mode 100644 ConfigGUI/WorldListEntry.cs create mode 100644 ConfigGUI/app.config create mode 100644 ConfigGUI/fcraft_config.ico create mode 100644 README.txt create mode 100644 ServerCLI/Program.cs create mode 100644 ServerCLI/Properties/AssemblyInfo.cs create mode 100644 ServerCLI/ServerCLI.csproj create mode 100644 ServerCLI/app.config create mode 100644 ServerCLI/fcraft_console.ico create mode 100644 ServerGUI/ConsoleBox.cs create mode 100644 ServerGUI/MainForm.Designer.cs create mode 100644 ServerGUI/MainForm.cs create mode 100644 ServerGUI/MainForm.resx create mode 100644 ServerGUI/Program.cs create mode 100644 ServerGUI/Properties/AssemblyInfo.cs create mode 100644 ServerGUI/ServerGUI.csproj create mode 100644 ServerGUI/UpdateWindow.Designer.cs create mode 100644 ServerGUI/UpdateWindow.cs create mode 100644 ServerGUI/UpdateWindow.resx create mode 100644 ServerGUI/app.config create mode 100644 ServerGUI/f2k.ico create mode 100644 UpdateInstaller/Program.cs create mode 100644 UpdateInstaller/Properties/AssemblyInfo.cs create mode 100644 UpdateInstaller/Properties/Resources.Designer.cs create mode 100644 UpdateInstaller/Properties/Resources.resx create mode 100644 UpdateInstaller/UpdateInstaller.csproj create mode 100644 UpdateInstaller/ZipStorer.cs create mode 100644 UpdateInstaller/app.config create mode 100644 UpdateInstaller/fcraft_updater.ico create mode 100644 UpdaterBuilder/Program.cs create mode 100644 UpdaterBuilder/Properties/AssemblyInfo.cs create mode 100644 UpdaterBuilder/UpdateBuilder.csproj create mode 100644 UpdaterBuilder/ZipStorer.cs create mode 100644 UpdaterBuilder/app.config create mode 100644 f2k.ico create mode 100644 fCraft.sln create mode 100644 fCraft/AutoRank/AutoRankManager.cs create mode 100644 fCraft/AutoRank/Conditions.cs create mode 100644 fCraft/AutoRank/Criterion.cs create mode 100644 fCraft/Commands/BuildingCommands.cs create mode 100644 fCraft/Commands/ChatCommands.cs create mode 100644 fCraft/Commands/Command.cs create mode 100644 fCraft/Commands/CommandCategory.cs create mode 100644 fCraft/Commands/CommandDescriptor.cs create mode 100644 fCraft/Commands/CommandManager.cs create mode 100644 fCraft/Commands/InfoCommands.cs create mode 100644 fCraft/Commands/MaintenanceCommands.cs create mode 100644 fCraft/Commands/ModerationCommands.cs create mode 100644 fCraft/Commands/WorldCommands.cs create mode 100644 fCraft/Commands/ZoneCommands.cs create mode 100644 fCraft/Drawing/Axis.cs create mode 100644 fCraft/Drawing/BrushManager.cs create mode 100644 fCraft/Drawing/Brushes/AbstractPerlinNoiseBrush.cs create mode 100644 fCraft/Drawing/Brushes/CheckeredBrush.cs create mode 100644 fCraft/Drawing/Brushes/CloudyBrush.cs create mode 100644 fCraft/Drawing/Brushes/MarbledBrush.cs create mode 100644 fCraft/Drawing/Brushes/NormalBrush.cs create mode 100644 fCraft/Drawing/Brushes/RainbowBrush.cs create mode 100644 fCraft/Drawing/Brushes/RandomBrush.cs create mode 100644 fCraft/Drawing/Brushes/ReplaceBrush.cs create mode 100644 fCraft/Drawing/Brushes/ReplaceBrushBrush.cs create mode 100644 fCraft/Drawing/Brushes/ReplaceNotBrush.cs create mode 100644 fCraft/Drawing/CopyState.cs create mode 100644 fCraft/Drawing/DrawOpWithBrush.cs create mode 100644 fCraft/Drawing/DrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/CuboidDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/CuboidHollowDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/CuboidWireframeDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/CutDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/EllipsoidDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/EllipsoidHollowDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/Fill2DDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/LineDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/PasteDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/QuickPasteDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/SphereDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/SphereHollowDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/TorusDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/TriangleDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/TriangleWireframeDrawOperation.cs create mode 100644 fCraft/Drawing/DrawOps/UndoDrawOperation.cs create mode 100644 fCraft/Drawing/IBrush.cs create mode 100644 fCraft/Drawing/UndoState.cs create mode 100644 fCraft/MapConversion/IMapConverter.cs create mode 100644 fCraft/MapConversion/INIFile.cs create mode 100644 fCraft/MapConversion/MapD3.cs create mode 100644 fCraft/MapConversion/MapDAT.cs create mode 100644 fCraft/MapConversion/MapFCMv2.cs create mode 100644 fCraft/MapConversion/MapFCMv3.cs create mode 100644 fCraft/MapConversion/MapFCMv4.cs create mode 100644 fCraft/MapConversion/MapFormat.cs create mode 100644 fCraft/MapConversion/MapFormatException.cs create mode 100644 fCraft/MapConversion/MapJTE.cs create mode 100644 fCraft/MapConversion/MapMCSharp.cs create mode 100644 fCraft/MapConversion/MapMinerCPP.cs create mode 100644 fCraft/MapConversion/MapMyne.cs create mode 100644 fCraft/MapConversion/MapNBT.cs create mode 100644 fCraft/MapConversion/MapOpticraft.cs create mode 100644 fCraft/MapConversion/MapUtility.cs create mode 100644 fCraft/MapConversion/NBTag.cs create mode 100644 fCraft/Network/Heartbeat.cs create mode 100644 fCraft/Network/IPBanInfo.cs create mode 100644 fCraft/Network/IPBanList.cs create mode 100644 fCraft/Network/IRC.cs create mode 100644 fCraft/Network/IRCCommands.cs create mode 100644 fCraft/Network/IRCMessage.cs create mode 100644 fCraft/Network/LineWrapper.cs create mode 100644 fCraft/Network/OpCode.cs create mode 100644 fCraft/Network/Packet.cs create mode 100644 fCraft/Network/PacketWriter.cs create mode 100644 fCraft/Network/Player.Networking.cs create mode 100644 fCraft/Player/Chat.cs create mode 100644 fCraft/Player/ChatTimer.cs create mode 100644 fCraft/Player/Permission.cs create mode 100644 fCraft/Player/Player.Events.cs create mode 100644 fCraft/Player/Player.cs create mode 100644 fCraft/Player/PlayerConstants.cs create mode 100644 fCraft/Player/PlayerDB.cs create mode 100644 fCraft/Player/PlayerEnumerable.cs create mode 100644 fCraft/Player/PlayerInfo.Actions.cs create mode 100644 fCraft/Player/PlayerInfo.Events.cs create mode 100644 fCraft/Player/PlayerInfo.cs create mode 100644 fCraft/Player/PlayerOpException.cs create mode 100644 fCraft/Player/Position.cs create mode 100644 fCraft/Player/Rank.cs create mode 100644 fCraft/Player/RankManager.cs create mode 100644 fCraft/Player/SecurityController.cs create mode 100644 fCraft/Properties/AssemblyInfo.cs create mode 100644 fCraft/System/ArgKey.cs create mode 100644 fCraft/System/Config.cs create mode 100644 fCraft/System/ConfigKey.Metadata.cs create mode 100644 fCraft/System/ConfigKey.cs create mode 100644 fCraft/System/ConfigSection.cs create mode 100644 fCraft/System/Logger.cs create mode 100644 fCraft/System/Scheduler.cs create mode 100644 fCraft/System/SchedulerTask.cs create mode 100644 fCraft/System/Server.Events.cs create mode 100644 fCraft/System/Server.cs create mode 100644 fCraft/Utils/BoundingBox.cs create mode 100644 fCraft/Utils/Color.cs create mode 100644 fCraft/Utils/ConcurrentQueue.cs create mode 100644 fCraft/Utils/EventInterfaces.cs create mode 100644 fCraft/Utils/ExtensionMethods.cs create mode 100644 fCraft/Utils/IClassy.cs create mode 100644 fCraft/Utils/JetBrains.Annotations.cs create mode 100644 fCraft/Utils/LogRecorder.cs create mode 100644 fCraft/Utils/MetadataCollection.cs create mode 100644 fCraft/Utils/MonoCompat.cs create mode 100644 fCraft/Utils/Noise.cs create mode 100644 fCraft/Utils/Paths.cs create mode 100644 fCraft/Utils/PerlinNoise3D.cs create mode 100644 fCraft/Utils/Trie.cs create mode 100644 fCraft/Utils/Updater.cs create mode 100644 fCraft/Utils/Vector3F.cs create mode 100644 fCraft/Utils/Vector3I.cs create mode 100644 fCraft/Utils/YesNoAuto.cs create mode 100644 fCraft/Utils/ZipStorer.cs create mode 100644 fCraft/World/Block.cs create mode 100644 fCraft/World/BlockChangeContext.cs create mode 100644 fCraft/World/BlockDB.cs create mode 100644 fCraft/World/BlockDBEntry.cs create mode 100644 fCraft/World/BlockUpdate.cs create mode 100644 fCraft/World/Forester.cs create mode 100644 fCraft/World/Map.cs create mode 100644 fCraft/World/MapGenerator.cs create mode 100644 fCraft/World/MapGeneratorArgs.cs create mode 100644 fCraft/World/World.Events.cs create mode 100644 fCraft/World/World.cs create mode 100644 fCraft/World/WorldManager.cs create mode 100644 fCraft/World/WorldOpException.cs create mode 100644 fCraft/World/Zone.cs create mode 100644 fCraft/World/ZoneCollection.cs create mode 100644 fCraft/app.config create mode 100644 fCraft/fCraft.csproj create mode 100644 fCraftGUI/AboutWindow.Designer.cs create mode 100644 fCraftGUI/AboutWindow.cs create mode 100644 fCraftGUI/AboutWindow.resx create mode 100644 fCraftGUI/IsoCat.cs create mode 100644 fCraftGUI/Properties/AssemblyInfo.cs create mode 100644 fCraftGUI/Properties/Resources.Designer.cs create mode 100644 fCraftGUI/Properties/Resources.resx create mode 100644 fCraftGUI/Tileset.png create mode 100644 fCraftGUI/TilesetShadowed.png create mode 100644 fCraftGUI/fCraftGUI.csproj diff --git a/APIReference.shfbproj b/APIReference.shfbproj new file mode 100644 index 0000000..39e8e61 --- /dev/null +++ b/APIReference.shfbproj @@ -0,0 +1,63 @@ + + + + + Debug + AnyCPU + 2.0 + {6c7b1503-2f51-473f-ba5e-e419e69288db} + 1.9.3.0 + + Documentation + Documentation + Documentation + + C:\fCraftHelp\0.61x\ + Documentation + en-US + Website + + + + fCraft 0.612 API Reference + MemberName + hana + CSharp + True + Copyright 2009, 2010, 2011 Matvei Stefarov &lt%3bme%40matvei.org&gt%3b + http://www.fcraft.net + + fCraft's core functionality. + Experimental AutoRank functionality. + Functionality related to draw commands, brushes, undo/redo, and copy/paste. + Event delegates and data. + Functionality for importing and exporting map files, and working with NBT and INI files. + ReSharper annotations. + + .NET 3.5 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..c9e5e21 --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,1653 @@ +0.616 + Fix: Fixed /Import terminating when importing bans, if a player with the given name is already banned. + Fix: Fixed a formatting error in /Bring and /WBring confirmation messages. + Fix: Fixed mixed-up messages in /WAccess and /WBuild when removing players from blacklist/whitelist (thanks ruggedbear and xN8Gx) + Fix: Fixed /Static not turning off after switching worlds (thanks boblol0909) + Fix: Fixed /Rotate printing incorrect copyslot number (thanks Mc_MrCat) + Fix: Fixed a ServerGUI crash when pressing the down arrow key after typing in the first console message (thanks Jason) + Fix: /Me messages no longer count towards stats twice (thanks recon_) + Fix: /Mute and /Unmute now usable with "-" in place of player name (thanks thelonedrummer18) + Fix: Fixed README.txt typo (thanks Wolfmade) + +0.615 + Fix: Fixed /Patrol checking Spectate permission, and /SpecPatrol not checking it. + Fix: Fixed /WRename not updating player's last-used-world-name (thanks Hellenion) + Fix: Fixed colorcodes (%-codes) not getting applied consistently (thanks BobKare) + Fix: Fixed a rare name-verification issue that only affected players whose ranks have been imported (thanks Mc_MrCat) + Fix: ConfigGUI now correctly interprets "Notify" and "Auto" updater settings. + Change: Data backups are now stored in ./databackups/ directory. + +0.614 + Fix: Fixed server crashing if PlayerDB.txt exists but is empty. + Fix: Fixed "/Join -" using player names, and "/TP -" using world names. + Fix: Solved File.Replace-related errors, which caused occasional save failures. + Fix: Fixed /SpecPatrol not checking caller's Spectate permission limit when selecting the next target. + Fix: Fixed "random" brush producing obvious repeating patterns. + Fix: Fixed auto-updater occasionally freezing when attempting to "Restart Now". + Fix: Added permission limit check to /Unfreeze (thanks Xb0x4d4m) + Fix: Improved the way /Bring and /WBring enforce access permissions (thanks Hellenion) + Change: Added more information about player connects/disconnects to log file. + +0.613 + Fix: Fixed ConfigGUI not saving changes to ShowConnectionMessages key. + Fix: Fixed a case where ConfigGUI could crash while attempting to rename a world. + Fix: All spectate permissions are now re-checked when someone gets promoted or demoted. + Fix: Fixed ConfigGUI Worlds tab crash when dealing with zoned maps. + Fix: Fixed IRC bot not retrying for desired nickname after reconnecting + Fix: Fixed a case where editing a world via ConfigGUI would clear security whitelists/blacklists. + Fix: Fixed spectating players not being treated as hidden (thanks Hellenion) + Fix: Fixed formatting in "Player ___ got auto-kicked" message (thanks BobKare) + Fix: Fixed auto-kick revealing hidden players (thanks BobKare) + Fix: Fixed RandomBrush-related /Undo artifacts in /Triangle (thanks Lunara_Noctis) + +0.612 + Fix: Fixed "/Worlds @Rank" not autocompleting rank names. + Fix: Fixed compatibility with legacy PlayerDB format versions. PlayerDB files created by fCraft 0.400+ will work again. + Fix: Fixed several syntax problems and crashes in /Gen. + Fix: Fixed a bug that reset a world's BackupInterval to default and IsLocked flag to false after changing the map. + Fix: Fixed a bug that prevented kicks/bans/promotions/demotions from being announced on IRC if no reason was given. + Change: Capped all user-entered time spans at 9999 days. + Change: Shortened and clarified some confirmation prompts (/Gen, /WLoad, /ZRemove, /Import, /MassRank). + +0.611 + Feature: Added information about periodic backups to /WInfo. + Fix: Fixed /Players not working properly if both world name and offset were given. + Fix: Fixed /WAccess, /WBuild, and /ZEdit not working with one-letter rank names. + Fix: Fixed /BanIP, /BanAll, /UnbanIP, and /UnbanAll treating fully numeric player names as IPs. + Fix: Fixed /SetInfo not resetting PlayerInfo.LastModified date. + Fix: Fixed "/Worlds populated" revealing worlds with hidden players. + Fix: Fixed /BringAll ignoring per-rank limits in some cases (thanks ferny530) + Fix: Fixed appearance of black and white blocks in /Env edges (thanks Jonty) + Fix: Fixed a bug in Position.Equals(), which may have caused unnecessary position updates in Player.UpdateVisibleEntities (thanks boblol0909) + Fix: Fixed a little crash in Chat.GetRawMessageType (thanks ruggedbear) + Fix: Fixed a case where Player.CanPlace checked permissions against the wrong world's BuildSecurity controller (thanks Xesdra) + Fix: Fixed "/Commands @Rank" showing hidden commands (thanks Hellenion) + Fix: Fixed a typo in /Worlds (thanks Kevinsweijen) + Fix: Fixed BlockDBAutoEnable and BlockDBAutoEnableRank config keys being placed in the "General" section. + Change: Improved config.xml format (now version 152). Configs created with fCraft 0.611+ will not be usable with older versions of fCraft. + Change: Rewrote ServerGUI startup code to make it more responsive (thanks to 800craft for the idea). + Change: Added information about command-line options to the README, and fixed some typos. + Change: MaxConnectionsPerIP restriction no longer applies to localhost. + Change: Increased block-ratio limit for "random", "cloudy", and "marbled" brushes from 1000 to 10,000. + +0.610 + Feature: Added /Fill2D (/f2d), /Triangle (/t), and /TriangleW (/tw) draw commands. + Feature: Added ability to filter /Worlds by rank. + Feature: Added /Static (a way to repeat selection commands). + Feature: Added MaxUndoStates config key (how many steps back can /Undo go), and ConfigGUI bindings. + Feature: Added experimental IP-range lookup to /Info (CIDR notation). Changed /Info pattern-matching single-character symbol from '.' to '?' + Feature: Added pagination to /Players. + Feature: /Spectate and /SpecPatrol now show what commands, PMs, and rank messages the target types in. + Feature: Added a way to change server salt without needing a restart, using "/Reload Salt" + Feature: /Measure now shows statistics about block types in the selection. + Feature: /Gen now has two new templates: "ocean" and "empty" + Fix: Many fixes in name-retyping functionality. + Fix: Addressed stability issues in LineWrapper and Undo/Redo. + Fix: Fixed /WLock not canceling all draw commands (as intended). + Fix: Fixed some outdated descriptions, links, and tooltips in ConfigGUI. + Fix: Fixed being able to BanIP/BanAll/UnbanIP/UnbanAll invalid IP addresses (0.0.0.0 and 255.255.255.255). + Fix: Fixed /Info printing a blank page (instead of the last page) if given offset is greater than the total number of matches. + Fix: Fixed rank prefixes being appended to players' names if DisplayedName is set. + Fix: Fixed a couple rare crashes/hangs in ServerGUI when parsing console commands. + Fix: Fixed /Ban and /Unban announcing when they're not supposed to. + Fix: Fixed a potential crash in /BanInfo when looking up an IP address directly (no player name). + Fix: Fixed another obscure way of revealing hidden players. + Fix: Fixed Beta client kick message (thanks Voziv). + Fix: Fixed /Commands defaulting to printing ALL commands. + Fix: Fixed /Undo trying to re-cancel canceled commands. + Fix: Fixed uncaught "Illegal characters in path." exception when trying to save a /Gen'erated map to an invalid filename. + Fix: Fixed typos in /ReplaceNot and /UndoArea. + Change: Added a warning on startup if name verification is disabled. + Change: Tweaks and improvements to /Info, /WInfo, /BanInfo, /SetInfo, and /Commands. + Change: Removed code that tries next port if the given one could not be used. Now fCraft either starts on the port specified in config, or dies. + Change: Commands now complain about being given too many parameters (in addition to complaining about too few parameters). This should help catch mistyped commands. + Change: Added an option to disable giant-tree generation in ConfigGUI's map generator. + Change: Changed log format for bans/kicks to include more information, and fixed log message format for "Player ____ logged in from an IP shared by banned players..." + Change: Included config.xml in the automated data backup. + Change: ConfigGUI tweaks and improvements. + Change: Changes some aliases: + - Added /Whois and /WhoWas as aliases for /Info. + - Set "/B" and "/About" to be aliases for /BInfo + Change: Changed some ConfigKey defaults: + - BlockDBEnabled defaults to "true" + - WomEnableEnvExtensions defaults to "false" + - IRCBotAnnounceServerEvents defaults to "true" + Change: Discontinued ServerWinService (formerly fCraftWinService) subproject. + API: Improved Vector3I, Vector3F, and Position classes a bit. + API: New events: DrawOperation.Beginning / Began / Ended + API: Changed the way PlayerInfo stores empty string fields. Instead of a 0-length string, it now uses nulls. + Optimization: Sped up PlayerDB loading/saving, and boolean ConfigKey lookup. + +0.604 + Feature: Added support for repeating previously-entered player and world names. Just put a dash (-) in place of the player/world name when using a command, and fCraft will fill in the most-recently-used name. + Fix: Fixed an occasional SecurityController-related crash in /Gen, /WLoad, and ConfigGUI world editor. + Fix: Fixed an very rare problem in /Shutdown and /Restart commands. + Fix: Fixed a rare crash that only happened happened if: MaxPlayers>128, MaxPlayersPerWorld=128, main is full, AND connecting player has a reserved slot. + Fix: Fixed naming of paste/pastenot and pastex/pastenotx + Fix: Fixed a permission exploit in /WBring (thanks to panda100123) + Fix: Fixed /Rules section finder accidentally being case-sensitive. + Fix: Fixed an issue with MCSharp/MCZall/MCLawl/MCQuai/MCStorm/MCForge/MCWhatever importer. + Change: Decreased shading strength in IsoCat renderer. + Change: Added /Who as an alias for /Players, by request. + Change: Clarified ConfigGUI description of name verification settings. + Change: Normalized command name capitalization. Command names must now start with an uppercase letter, for consistency. + API: Fixed inconsistencies in PlayerDB.txt header row format declaration. + +0.603 + Fix: /Cancel can no longer be repeated with "/" (no longer replaces previously-called command). + Fix: Fixed /Rotate and /Mirror messing up if a paste command is in progress. + Fix: Re-enabled crash interception and reporting. + +0.602 + Feature: One-click paste commands are now the default. Two-click aligned paste commands are now known as pastex/pastenotx (or px/pnx) + Feature: Added /ReplaceBrush (/RB) command: shortcut for /Brush replacebrush + /Cuboid + Feature: Added a way to change number of copy/paste slots per rank from ConfigGUI. + Fix: Fixed a few typos and missing help entries. + Fix: Fixed DrawOp completion message and log entry. + Fix: Fixed /Paste and /PasteNot not properly clipping against world boundaries. + Fix: Fixed ConfigGUI not preserving environment settings for worlds. + Fix: Fixed ConfigGUI losing some worlds and showing some errors when importing from 0.5xx. + Fix: Fixed /Bring not being usable from console. + Fix: Fixed /WRename crashing if invalid characters (from filesystem's point of view) are present in the new name. + Fix: Fixed /SpecPatrol not checking for Spectate permission. + Fix: Fixed /UndoArea'd and /UndoPlayer'ed blocks having "Restored" flag set in /BInfo + Fix: Fixed a detached checkbox on "Worlds" tab of ConfigGUI. + Fix: Fixed /Say, /Staff, and /Me getting logged twice. + Fix: Fixed per-world backup settings being cleared on server restart. + Fix: Fixed a rare NullReferenceException in WorldManager.LoadWorldList that occurred if the main world was missing, and the fallback world had access restrictions. + Fix: Fixed some issues with BlockDB settings in "Add World" and "Edit World" popups of ConfiGUI. + Fix: Fixed a rare ConfigGUI crash when resetting the rank tab whole one of the old ranks is selected. + Change: Sped up /EllipsoidH and /SphereH slightly (thanks to Redshift). + Change: Improved error handling in /Env when setting level. + Change: Double-buffered the preview on "Chat" tab of ConfigGUI, to get rid of flicker. + Change: Clarified auto-freeze message when trying to kick/ban someone without a required reason string. + +0.601 + Fixed several issues with /Undo and drawing commands. + +0.600 + Chat: Added a /Timer command for creating publicly-announced countdowns/timers. + Chat: Added a way to cancel partial messages: /Nvm + Chat: Removed "Banned player ___ tried to log in" and "Player ___ tried to log in from a banned IP" messages. + Chat: /Say, /Staff, and /Me commands can no longer repeated using "/" shortcut. + Chat: Added a warning when trying to PM deafened players. + Chat: Added a shortcut for chatting to same rank: "@@ Message" + Chat: Increased the number of blank lines sent by /Clear to 30, to accommodate players with smaller-than-normal font sizes. + Chat Fix: Fixed /Say, /Roll, /Staff, and /Me sometimes bypassing Chat permission, mute check, or spam detection check. + Chat Fix: Fixed rank chat being logged twice. + Chat Fix: Fixed some system messages getting through to deafened players. + Chat Fix: /Deafen now clears the screen when toggled on. + Commands: Rank and zone names now autocomplete. + Commands: Players can no longer freeze, mute, kick, ban, unban, promote, or demote themselves. + Commands: /Players can now be called with an optional WorldName parameter, to only list players in a particular world. + Commands: /WLoad now takes two more optional parameters: build rank and access rank names. + Commands: /Info now shows last leave reason, idle time, freeze/mute information, and ban exemption status. + Commands: Augmented /Rules to support several rule categories. + Commands: Added /Spectate and /SpecPatrol (/SPat) commands. + Commands: Added /WBring (brings target player to a world); + Commands: Added /Spawn command (brings you to spawn). Typing "/TP" no longer does the same thing, and /TP now always requires Teleport permission. + Commands: Algorithm that /Patrol uses to pick next target was improved. + Commands: Renamed Lock/Unlock to WLock/WUnlock, for consistency. + Commands: Merged /ReloadConfig and /AutoRankReload into /Reload command. Merged /ImportRanks and /ImportBans into /Import command. Combined functionality of /LockAll into /WLock, and functionality of /UnlockAll into /WUnlock. + Commands: /Info and /Worlds now support pagination. + Commands: When world access permissions are changed with /WAccess, players who lost ability to join the world will automatically be moved to main. + Commands: Frozen player can no longer call most commands, with exception of: /Clear, /Info, /BanInfo, /RankInfo, /ServerInfo, /Ranks, /Rules, /Where, /Help, /Commands, /Colors, /Worlds, /Zones, and /ZInfo. + Commands: /Bring and /WBring now give the option of overriding worlds' access minimum-rank. Explicitly blacklisted players will continue to be barred. + Commands: Added /InfoSwap command, for swapping two players' ranks and PlayerDB records. + Commands: Added a way to abort a server shutdown or restart ("/Shutdown abort" or "/Restart abort"). + Commands: /RankInfo now shows copy/paste slots, idle kick time, and permission limits. + Commands: Most time-related commands now take the compact format (e.g. 1m30s, 45s, 24h, etc). + Commands: Color-coded /Commands. + Commands: Limited /Mute duration to range between 1 second and 700 days (100 weeks). + Commands: /SetInfo now allows changing players' DisplayedName. + Commands: A warning is now shown when a player is kicked/banned, and other players are still connected from the IP. + Commands: Added a way to add IP ban exemptions using /BanEx (banning all players from IP except a particular one). + Commands: Added automatic countdowns to /Shutdown and /Restart. + Commands: Added more stats to /ServerInfo. + Commands: /MassRank now defaults to silent, and takes a "reason" parameter. + Commands: Added "/St" as an alias for /Staff. Changed "/L" from being alias for /Join, to being alias for /Lava. Removed "/Cub" alias for "/Cuboid". + Commands Fix: Fixed /UnbanIP and /UnbanAll not announcing the unban reason. + Commands Fix: Fixed /Info not formatting names of offline players. + Commands Fix: Fixed /Restart breaking under Mono if working path contains spaces. + Commands Fix: Fixed freeze/unfreeze printing wrong ranks in "You can only freeze players ranked ____ or lower" messages. + Commands Fix: Fixed a rare crash in /Rules when used from console. + Commands Fix: /BanIP and /BanAll no longer allow to ban the IP if it's shared by any players whom caller is not allowed to ban. + Commands Fix: Fixed /Ignore and /Unignore printing all matches at once on autocompletion. + Commands Fix: Fixed a case where /BringAll could reveal hidden players. + Commands Fix: Fixed a few bugs in /DumpStats, expanded player listing to top-5/bottom-5, and added "BlocksDrawn" stat. + Commands Fix: Fixed /Rank and /TP commands showing up in /Commands for ALL players. + Commands Fix: Fixed a couple rare bugs in /Roll + Commands Fix: Fixed broken /BringAll support for bringing multiple ranks at once. + Commands Fix: Fixed several obscure ways of revealing presence of hidden players via commands like /ServerInfo and /Worlds. + Config: New config keys: BackupDataOnStartup (default: true), RestartInterval (default: 0), HeartbeatToWoMDirect (default: true), BlockDBEnabled (default: true), BlockDBAutoEnable (default: true), and BlockDBAutoEnableRank (default: DefaultRank), WoMEnableEnvExtensions (default: false). + Config: Added Spectate permission that covers /Spectate and /SpecPatrol. + Config: Added DrawAdvanced permission that covers /Brush, /Sphere, /SphereH, /Torus, and /Restore commands. + Config: Added ManageBlockDB permission that covers /BlockDB. + Config: Added UndoOthersActions permission that covers /UndoPlayer and /UndoArea. + Config: /Reload can now update more config keys without needing a restart: AutoRankEnabled, BandwidthUseMode, ConsoleName, DefaultRank, DefaultBuildRank, PatrolledRank. + Config: Removed obsolete ShowBannedConnectionMessages config key. + Config Fix: Fixed DefaultRank/DefaultBuildRank/PatrolledRank settings not being applied. + ConfigGUI: Added BlockDB settings to "Worlds" tab and "Add/Edit World" dialog. + ConfigGUI: Reorganized Rank tab. + ConfigGUI: Relaxed restrictions on rank prefixes. + ConfigGUI: Added "Insert Color" and "Insert Keyword" buttons to announce/rule/greeting editors in ConfigGUI, and a "Reset" button to revert edits. + ConfigGUI: Any errors/warnings that are produced while loading worlds.xml in ConfigGUI are now displayed at startup. + ConfigGUI Fix: ConfigGUI no longer loses worlds' whitelists/blacklists. + ConfigGUI Fix: Fixed several bugs related to renaming worlds. + ConfigGUI Fix: Fixed several "Reset" bugs. + ConfigGUI Fix: Fixed "Enable colors in IRC" checkbox remaining disabled in ConfigTool. + ConfigGUI Fix: Fixed ConfigTool's Chat tab not updating to reflect changes in rank colors/prefixes. + ConfigGUI Fix: Fixed sort order in Worlds tab when sorting by world size. + ConfigGUI Fix: Fixed some rank dropdown menus in ConfigGUI showing "(lowest rank)" instead of "(default rank)" as the default option. + Drawing: Introduced a brush/pattern system for draw commands. Rewrote (almost) all existing draw commands to take advantage of that new infrastructure. Brushes are: Normal (default), Checkered, Random, Cloudy, Marbled, Replace, ReplaceNot, ReplaceBrush. + Drawing: Added a way to /Undo up to five commands back. Use /Redo to reverse it. + Drawing: Added /Torus drawing command, written and contributed by M1_Abrams. + Drawing: Added support for multiple copy/paste slots, using /CopySlot. Number of slots of configurable per-rank. + Drawing: /Replace and /ReplaceNot can now take just one blocktype. The replacement type is inferred from the type of block player is holding when clicking. + Drawing: /Undo now cancels the last drawing commands. + Drawing: Added a new block name aliases: "g" for grass, "w" for water, "a" for air. + Drawing Fix: Added a missing volume check to /Line, and fixed /CuboidW volume calculations. + Drawing Fix: Fixed /Copy and /Cut reporting incorrect orientation. + FrontEnd: Colored ConfigCLI log messages: red for errors, yellow for warnings, dark gray for debug, default (gray) for everything else. Call with "--noconsolecolor" parameter to disable. + FrontEnd: ServerCLI now handles all updater modes properly, and downloads updater/restarts if needed. + FrontEnd: Improved window titles. + FrontEnd Fix: Fixed ServerCLI writing errors to stdout instead of stderr. + Misc: Players who log out hidden will now log in silently, still hidden. + Misc: More keywords are now usable in announcements, rules, and greeting files: {SERVER_NAME}, {MOTD}, {RANK}, {PLAYER_NAME}, {TIME}, {WORLD}, {PLAYERS}, and {WORLDS} + Misc: Kicked or disconnected players no longer have their TotalTime increased for the time idled. + Misc: fCraft will refuse to start up if running old versions of mono (2.4 or earlier). + Misc: Crash reporter now prints a reference number for submitted reports. + Misc: AutoRank now only evaluates online players on a timer. A full database sweep will only be done on startup or on-demand. This should notably reduce average CPU use on large databases. + Misc: Added AutoRank pseudoplayer (similar to Console), who is now credited with all autorank promotions/demotions. + Misc: Increased the allowed clicking distance when manually clicking blocks, to account for lag. + Misc: IRC bot now announces channel kicks and nick changes. + Misc: When a player with a reserved slot tries to join a full world, the longest-idle player will be kicked to make room. + Misc: Frozen, demoted, and banned players are now automatically unhidden. Frozen players are undeafened. Banned players are also automatically unfrozen and unmuted. + Misc: When a player's rank is changed, their bindings are now reset (to avoid issues with /Water, /Lava, and /Solid), and any in-progress selection is canceled (draw commands, /Zone, /ZTest, /BInfo, /Measure, etc) + Misc: Frozen players now have a little blue star appended to their names, similar to the red star for banned players. + Misc: Moved all Heartbeat-related operations to the background thread (fixes lag spikes when minecraft.net times out). + Misc: Added a dynamically updated compass at the top-right of the screen, visible to players who use WoM 1.8.8+ + Misc: Made fCraft.sln default to "Release" configuration when loaded. + Misc: Added a check for libMonoPosix on server startup. This allows catching misconfigured Mono setups early. + Misc: Ranks now default to "white" color ("&f") instead of "none" color (""). + Misc: Edited the "failed to load main world" message to sound less menacing, since it's usually caused just by main world filling up. + Misc: Sped up /PruneDB, /MassRank, map generation. Reduced memory use of draw commands, undo buffers, and ConfigTool. + Misc Fix: Fixed "duplicate metadata key" warning in FCMv3 maps. + Misc Fix: Fixed a zone-related race condition when exporting to Opticraft map format. + Misc Fix: Fixed fCraft being unable to start if fCraft.dll is in the root directory of a drive (e.g. in "C:/") + Misc Fix: Fixed a bug that caused bandwidth to sometimes be wasted on needlessly updating position of non-moving players. + Misc Fix: Fixed a clock drift problem in periodic SchedulerTasks. + Misc Fix: Fixed a case where, in some rank configurations, players would see themselves as offline when hidden (in /Info, /BInfo, /Players, etc). + PlayerDB: Added 46th column: PlayerInfo.IsHidden ('h' if hidden, otherwise blank). + PlayerDB: Added 47th column: PlayerInfo.LastModified (UTC unix timestamp). Accuracy is not yet guaranteed. + PlayerDB: Added 48th column: PlayerInfo.DisplayedName (string or blank). Overrides "Name" field for purposes of displaying formatted name (ClassyName). + PlayerDB: Removed 14th column (FailedLoginCount) due to disuse. + PlayerDB: PlayerDB now writes out changes to TotalTime incrementally for online players, instead of applying the change on-logout. + Worlds: Added a way to change some environment and texture settings per-map, using /Env. + Worlds: Per-world settings on backup intervals now work. + Worlds: Added BlockDB - a system for tracking edits per-block. Added related /BlockDB and /BInfo commands. + Worlds: Added /UndoPlayer and /UndoArea commands for undoing players' actions using BlockDB. + Worlds: If a world has a missing map file, a warning is now shown, and a default flatgrass map is created. + Worlds: Added a way to set per-rank main world, using /WMain. + Worlds: Added /Restore command, inspired by good old MinerCPP. Selectively loads a map from another file or backup. + Worlds: Allowed non-standard dimensions (neither multiples of 16, nor powers of 2). Added a warning about glitches when using nonstandard dimensions. + Worlds: Added LoadedBy, LoadedOn, MapChangedBy, and MapChangedOn to /WInfo and to worlds.xml + Worlds: MapGenerator tweaks: "default" template is now known as "random," "mountains" template now has more cliffs and snow on mountaintops, "island" template is less rough and has nice beaches, and water is now automatically removed from "desert" maps. + Worlds Fix: Fixed a crash that occurred if worlds.xml existed but contained no worlds. + Worlds Fix: Fixed an exploit that allowed players to bypass world access permissions in certain rank configurations. + Worlds Fix: Fixed some rare crashes in /WLoad. + Worlds Fix: Fixed /WAccess and /WBuild occasionally not saving the world list after adding players to the access/build whitelists. + Worlds Fix: Fixed being unable to change just the capitalization when renaming worlds from ConfigTool, under Windows. + Zones: Added /ZRename command to rename zones. + Zones: Added /ZMark command to use zone boundaries for drawing or making selections. + Zones Fix: Fixed /ZAdd crash when adding more than one personal zone for the same player (/ZAdd +Name). + Zones Fix: Fixed a rare case where two players could simultaneously create a zone with the same name on the same world, which would crash one of the players. + Zones Fix: /Zones no longer crashes if used on a world with a missing or corrupt map file. + Zones Fix: Tightened security checks of /ZAdd and /ZRemove to prevent a permission exploit. + API: Added specialized MetadataCollection class to manage per-map and per-player metadata, and ZoneCollection for zones. + API: Added ReSharper-compatible annotations and more argument checks throughout the codebase. + API: fCraft now consistently uses [X,Y,Z] for coordinates (where X/Y are horizontal, and Z is vertical), and [Width,Length,Height] for dimensions (Width/Length horizontal, Height vertical). + API: Merged Session and Player classes. All session-related functionality is in Player.Networking.cs file. + API: New line-wrapping algorithm provides better performance and safety. Now using "\n" instead of "&N" to force a newline. + API: Renamed World.PlayerList to World.Players, Server.PlayerList to Server.Players, and added PlayerDB.PlayerInfoList + API: New LINQ-style API for working with sets of players. This API is now used for all messaging and packet-sending. Old API (Server.Send* and World.Send*) was removed. + API: New unified API for handling player-generated chat (Chat static class), and new events (Chat.Sending and Chat.Sent). + API: All cancellable events now implement ICancellableEvent. + API: MapDAT and MapNBT now implement IMapConverter.LoadHeader. + API: Converted IClassy.GetClassyName(), Rank.GetFullName(), and Player.GetListName() to properties. + API: Renamed ConfigKey.GetBool() to ConfigKey.Enabled(). Added ConfigKey.TryGetInt() and ConfigKey.TryGetBool() methods. + API: CommandManager.RegisterCommand() now checks for reserved command names (currently "ok" and "nvm"), and does a better job at handling command name capitalization differences. + API: Added ability to override Player.MaxBlockPlacementRange, Player.SocketTimeout, Heartbeat.Delay, and Heartbeat.Timeout. + API: Renamed RankManager.ParseRank(string) to Rank.Parse(string) + API: All draw/undo/copy/paste commands have been moved to fCraft.Drawing namespace. + API: Added support for multiple /help sections per command. + API: Added CommandCategory.Debug, removed CommandDescriptor.HelpHander and HelpHandler delegate. + API: Moved Server.Player* events to Player.* and Server.PlayerInfo* to PlayerInfo.* + API: Expanded the confirmation system (/OK) to allow custom callbacks, decoupled from the command system. + API Fix: "Bad heartbeat" reply from minecraft.net no longer triggers UrlChanged event. + API Fix: Fixed a couple bugs in BoundingBox.GetIntersection and BoundingBox.Contains. + API Fix: Fixed /Replace and /ReplaceNot not firing Player.PlacedBlock + events. + API Fix: Uncaught exceptions that occur while parsing player messages no longer send crash reports when in DEBUG configuration. + +0.537 + Fixed another heartbeat-related issue caused by minecraft.net updates. + +0.536 + Fix: Fixed a rare crash in DoChangeRank when promoting/demoting offline players. + Fix: Fixed a case where world access/build whitelist additions were not immediately saved. + Fix: Fixed several errors in the way time spans were displayed. + Fix: Unhandled exceptions in background task callbacks will no longer take down the server + Change: Changed the heartbeat handler to use GET instead of POST (workaround for minecraft.net problems). + Change: Removed AutoLauncher. + +0.535 + Workaround for minecraft.net heartbeat changes. + +0.534 + Feature: Added config key (in seconds). It should help people who are suffering from Mono memory leaks. This setting can only currently be modified by editing config.xml + Fix: Fixed /wload and /gen resetting the world's hidden status. + Fix: Fixed "Apply" button not getting enabled in ConfigTool when world list is changed. + Fix: Fixed /cw reporting incorrect volume (fixes #3308137). + Fix: Added a confirmation when /wrename will result in overwriting a map file, and fixed a related crash (fixes #3311994). + Fix: Fixed FormatException in error handling code in World.AcceptPlayer + Fix: Fixed /info not showing formatted names for players who appear offline. + Fix: Fixed several issues with renaming worlds from ConfigTool. + Fix: Fixed /restart breaking under Mono if path contains spaces. + Fix: Fixed /staff, /me, and /roll commands not requiring the Chat permission. + Fix: Fixed "Use IRC colors" checkbox remaining disabled in ConfigTool. + Change: Allowed all printable ASCII characters in chat except '&'. + Change: Heartbeat tweaks to prevent "Bad heartbeat" from popping up. + Optimization: ConfigTool starts up faster, and uses much less memory. + +0.533 + Fix: Fixed a regression in 0.530 that erased some strings in PlayerInfo and IPBanInfo objects, such as ban reasons. + Change: Increased heartbeat interval to 27.5 seconds. + Change: Reduced jitter of player movement. + +0.532 + Feature: Added "/worlds loaded" command. + Fix: Fixed a bug that prevented worlds from accepting any block changes after a map change (/wload or /gen) + Fix: Fixed "Player _____ joined main." displaying twice when players connect. + Change: Added /colours as an alias to /colors + Change: Adjusted /dumpstats security checks, and made it require EditPlayerDB permission (instead of Import). + +0.531 + Feature: /sinfo now shows bandwidth use, fCraft version, and .NET/Mono version. + Feature: Added ability to use coordinates with mark (e.g. "/mark 0 0 0"). + Fix: Fixed non-default rank settings not saving properly from ConfigTool. + Fix: Fixed a serialization bug in IPBanList that happened if a ban reason was not given, or if an IP address was not associated with any player. + Fix: Fixed a few minor bugs in /wmain. + Change: Allowed the caret (^) in chat messages. + Change: Reduced movement lag. + Change: /version command has been removed due to redundancy with /sinfo. + Change: Detailed reasons of player disconnects are now logged better. + Change: Increased Minecraft.net heartbeat interval slightly. + API: Added new events: Scheduler.TaskAdded, Scheduler.TaskExecuting, Scheduler.TaskExecuted, and Scheduler.TaskRemoved. These events are only available when fCraft.dll is built with DEBUG_SCHEDULER flag because of their performance impact. + Optimization: Removed the scheduler overhead from having unloaded worlds. + Optimization: Sped up MapGenerator by about 10%. + +0.530 + Feature: New adaptive bandwidth-saving mechanism for updating player positions, controlled by the new BandwidthUseMode config key. + Feature: Added OptiCraft blocktype names and blocks' numeric IDs as aliases. + Fix: Fixed block bindings not being applied to drawing commands. + Fix: Fixed fCraft getting confused if worlds.xml contains duplicates. + Fix: Fixed "/" (repeat last command) not rewinding the command, which caused some issues with command arguments. + Fix: Fixed "Max connections per IP" setting not saving in ConfigTool. + Fix: Fixed AnnounceRankChangeReasons checkbox being erroneously tied to RequireRankChangeReasons key in ConfigTool. + Fix: Added some missing tooltips to ConfigTool. + Fix: Marked 10.0.0.0/8 IP range as LAN. + Fix: Fixed an issue with balanced name verification (thanks wootalyzer). + Fix: Fixed server announcing hidden players' departure (#3292633). Thanks to Hellenion. + Fix: Fixed /bring not checking per-rank permissions (#3292303). Thanks to Denny + Fix: Fixed some header bugs in the D3 map converter. + Fix: Fixed /wload crashing instead of printing an error message under Mono if the path was malformed. + Fix: Fixed /banip not recognizing invalid IPs (e.g. broadcast). + Fix: Fixed antispam resetting players' mute time. + Fix: Fixed Scheduler being able to add tasks after the shutdown process started. + Fix: Reduced problems associated with players trying to connect while the server is shutting down. + Fix: Added autorank.xml to the list of protected files. + Fix: Fixed a crash in backup system that happened if two backups ran simultaneously and the total size or number of backup files was limited. + Fix: Fixed a case where duplicate worlds could be created. + Change: When players rejoin a world (e.g. /wflush or /wload), "joined world" message is no longer shown. + Change: /mute no longer allows shortening players' mute time, only to extend it. + Change: Allowed player names with periods. + Change: Removed SaveOnShutdown config key, tweaked range checks on some keys, grouped key tags into section tags. + Change: When saving config.xml, keys with default values are now commented out. + API: New events! PlayerHideChanged, PlayerInfoCreating, PlayerInfoCreated, PlayerInfoRankChanging, and PlayerInfoRankChanged. + API: Server.StartServer() now throws specific exceptions on failure, instead of returning false. + API: Removed some obsolete Server events. + API: Switched to 100% Utc dates/times internally. PlayerDB now uses UTC Unix timestamps to serialize all dates/times. + Optimization: Sped up map generation. + Optimization: Reduced PlayerDB saving interval, reduced PlayerDB filesize, reduced CPU and memory use of PlayerDB saving, reduced memory footprint of loaded PlayerDB, improved IPAddress lookup performance in PlayerDB. + +0.522 + Feature: Added more functionality to hollow ellipsoid and sphere, thanks to Redshift. Now it is possible to define outer/inner blocktypes. + Feature: Added OptiCraft map format support, courtesy of LgZ-optical + Fix: Fixed a serious error that caused a crash when main world became full. This bug affected 0.520 and 0.521. + Fix: Fixed AutoLauncher not working under Mono (thanks to ven000m for spotting). + Fix: Fixed "Add World" dialog crashing if closed while generating a world. + Fix: Fixed /restart using ShutdownReason.ShuttingDown (instead of ShutdownReason.Restarting) + Fix: Fixed a crash in ConfigTool if MaxPlayers was set to be less than MaxPlayersPerWorld. + Fix: When loading old configs (pre-0.520), MaxPlayersPerWorld is automatically set to match MaxPlayers, to avoid unexpected side effects. + API: Added WorldCreating and WorldCreated events. + API: Moved world-related commands from Server to a new class, WorldManager. + API: Renamed RankList to RankManager, and CommandList to CommandManager, for consistency. + +0.521 + Feature: Added a /clear command, to clear in-game chat. Also works from fCraftConsole and fCraftUI. + Fix: Added missing chat commands. + Fix: Cosmetic fixes in /zremove. + +0.520 + Feature: Removed the server-wide 128 player limit. Now the limit is per-world. + Feature: You can now repeat the last command by typing "/" + Feature: Added a way to use color codes in chat (with % instead of &), a UseColorCodes permission, and a /colors command. + Feature: Added support for multi-line messages or commands. Add " /" to the end of a line to continue typing it. + Feature: Added "/commands", to replace "/help commands". It allows filtering commands by rank, category, permissions, etc. Aliases: /cmdlist, /cmds + Feature: New permissions: ViewPlayerIPs, BringAll + Feature: New config switches: AnnounceRankChangeReasons and RequireKickReason. + Feature: Replaced LimitOneConnectionPerIP config key (boolean) with a more flexible MaxConnectionsPerIP (int). + Feature: Added /bringall command, for moving everyone (or only certain ranks) from your world (or optionally all worlds, or a list of other worlds) to your location. + Feature: /bring can be limited per-rank + Feature: When file is not found on case-sensitive filesystems, fCraft tries to syggest similar filenames. + Feature: Added new constants to use in greeting.txt: {TIME}, {PLAYER_NAME}, {WORLD}, {PLAYERS}, {WORLDS}, {MOTD} + Feature: Improved compatibility with SurvivalTest clients. + Fix: Fixed /banip not kicking other players from the same IP (besides the named player) + Fix: Added missing blocktype validation to MapFCMv2, MapFCMv3, MapMyne, and MapNBT. + Fix: Fixed a rare shutdown crash in fCraftUI. + Fix: Improved the way PlayerDB.Load handles errors in individual records. A single corrupted entry will no longer prevent the rest from loading. + Fix: Fixed fCraftUI abruptly quitting on crash / failure to start, instead of showing the error message. Use "--exitoncrash" command-line switch to reproduce old behavior. + Fix: Fixed ConfigTool's World tab not prompting to delete map files when deleting worlds. + Fix: Fixed a bug in MCSharp importer (#3281305) + Fix: Fixed a list of permissions in /rinfo not being sorted. + Fix: Fixed /mute rank limit dropdown sometimes getting out of sync. + Fix: Fixed IRC bot redundantly announcing kicks when someone has been banned. + Fix: Fixed a bug where fCraft would fail to start if PlayerDB.txt was truncated/empty. + Change: Moved "IP" config key from General to Advanced. + Change: Added /spin as an alias to /rotate (for MCLawl users) + API: New events: PlayerClicking, PlayerClicked, PlayerPlacingBlock, PlayerPlacedBlock, PlayerListChanged, PlayerBeingKicked, PlayerKicked + Optimization: Reduced CPU use of draw commands by ~30%. Sped up PlayerDB loading by ~8%, and reduced memory use when saving. Also reduced PlayerDB filesize by about 15% + +0.512 + Feature: /zones can now be used from console, and can be used on any world (active or inactive). + Feature: Improved D3 map importer. + Fix: Added missing filename sanity/safety checks to /dumpstats (#3267362, thanks to Hellenion) + Fix: Fixed mixed up messages shown in /waccess and /wbuild (#3179189) + Fix: Fixed crash reports sometimes failing to submit. + Fix: Fixed /dumpstats crashing if a rank had 0 players. + Fix: Made Mono version detection less crashy (#3267476, thanks to Hellenion again) + Fix: Fixed MapUtility.LoadHeader terminating early, before going through all fallback converters. + Fix: Fixed ConfigTool tab order. + Fix: Fixed fCraft reporting inaccurate CPU usage figures. + Fix: Fixed a NullReferenceException in /rank if an invalid player name was given. + Fix: Fixed fCraft project not building under Windows if path contains spaces. + Change: Added "/serverreport" as an alias for "/sinfo" + Change: Improved the way fCraft handles and reports map loading errors. + API Change: Moved all filename constants (e.g. "PlayerDB.txt") to Paths class. + +0.511 + Feature: Further improvements to Mono support. fCraft will now utilize mono-sgen whenever possible. + Fix: Fixed fCraftUI ignoring command-line arguments. + Fix: Fixed broken /paste and /pastenot volume checks. + Fix: Fixed crash reports failing to submit. + Fix: Fixed a timezone-related bug that messed up players' "total time on server" stat. + Change: Added a warning if running on old versions of Mono (2.6 or lower). + +0.510_r489 + Fix: Fixed unnecessary GDI+ dependence in fCraftConsole under Mono + Fix: Fixed EnumKeyAttribute not taking blank values (as in case of ProcessPriority config key). + +0.510 + Feature: The fCraft updater has been rewritten and expanded. It now works under all OSes, and provides more features. + Feature: fCraft now handles differences between case-sensitive and case-insensitive filesystems much better. /wrename and /wload are also aware of the filesystem. + Feature: fCraft now checks presence, filename capitalization, and integrity of all map files on the world list at startup. + Feature: New ConfigTool settings for updater, checkbox for showing "player connected/left" messages, and "/mute" permission settings. + Feature: Added "/credits" command to ConfigUI. + Feature: Implemented "--norestart" and "--exitoncrash" command-line switches for fCraft. + Feature: fCraft event system is being rewritten with modding support in mind. + Feature: Improved MCSharp/MCZall/MCLawl map support. + Fix: Software errors that happen while parsing player and console commands will no longer cause disconnects/crashes. + Fix: IPBanList will attempt to recover records lost due to a recent regression in 0.505. + Fix: Fixed /wrename errors in cases where names differ only in capitalization. + Fix: Fixed "Copy Existing World" showing the current world on the list in AddWorldPopup. + Fix: Fixed "Chat" tab preview not updating when settings are reset. + Fix: Fixed per-world whitelist/blacklist not saving when worlds are edited with AddWorldPopup. + Fix: Fixed non-verified connections that get kicked from affecting that name's PlayerInfo. + Fix: Server.GenerateSalt() now uses a cryptographically secure PRNG. + Fix: Fixed verification problems on servers with HeartbeatEnabled=false. + Fix: Fixed a wrong message being shown by /rank if the target is already same or higher/lower rank. + Fix: Fixed /winfo showing incorrect information for last lock/unlock event. + Fix: Fixed tabbing order on ConfigUI. + Fix: Improved the way Server.InitLibrary and Logger.Log errors are handled. + Fix: Added missing validation checks to LoadHeader in several map converters. + Fix: Fixed players being able to complete a draw command or /zadd, if they were demoted before clicking for the second time. + Fix: Added missing /paste and /pastenot volume checks. + Fix: Fixed a bug in /zinfo that showed incorrect creation/editing times. + Fix: Better error messages for unrecognized block names in command parameters. + Fix: /restart now works from ConfigUI under Mono (but still broken in fCraftConsole). + Fix: Fixed handling of command-line arguments surrounded with quotes. + Fix: Fixed a bug at ConfigTool.ConfigUI.xAllowSecurityCircumvention_CheckedChanged (#3181907, thanks to clhdrums87). + Fix: Fixed /winfo counting hidden players (#3237877, thanks to Hellenion). + Fix: Fixed MCSharp map exporter mixing up map dimensions (#3159501, thanks to dogwatch). + Change: Improved the way fCraft project is built. Now all binaries are copied to /trunk/bin/Release or /trunk/bin/Debug. + Change: Improved error tolerance of Scheduler.EndShutdown(). + Change: Expanded Logger.CheckForCommonErrors(). + Change: Renamed config keys (old ones can still be loaded): IRCBot to IRCBotEnabled, AutomaticUpdates to UpdaterMode. + Change: Added /wadd as an alias for /wload. + Change: Renamed Mcc namespace to fCraft.MapConversion. + Change: Decreased timeout for fCraft update check. + Change: Added a confirmation prompt to /rank when adding an unrecognized player name to the database. + Optimization: Sped up heartbeat sending and world-related operations, increased world saving interval to 90 seconds, reduced per-player idle CPU usage, halved CPU usage of draw commands. + +0.506 + Feature: Added hollow ellipsoid drawing (/eh) and hollow sphere drawing (/sph), contributed by Conrad "Redshift" Morgan. + Fix: Fixed a regression in 0.503 that caused some IP ban records to be lost. I am terribly sorry! + Fix: Fixed a path-related error in Map.SaveBackup + Fix: Fixed a possible disconnect in Player.CheckBlockSpam is antigrief was not configured properly. + Fix: Fixed a bug in ConfigTool.SaveWorldList. + Fix: Fixed "Limit the number of blocks that can be undone" checkbox misbehaving (thanks to ven000m for bug report). + Change: By popular demand /sphere2 is now the default behavior of /sphere. + Change: Removed "oldSalt" check for name verification, to avoid having the server write to config.xml unnecessarily. + Change: Removed ".backup" file creation, by popular demand. It was kinda useless. + +0.505 + Feature: All draw commands now inform player if any blocks were skipped due to local permission issues (e.g. zones). + Fix: Fixed a regression that caused backups to be saved in the wrong path. Backups were being saved to ./backups/ instead of ./maps/backups/ + Fix: Fixed a permission issue in /replace and /replacenot. + Fix: Fixed /info not displaying a list of players from same IP. + Fix: Addressed a security issue with salt generation. + +0.504 + Fixed a crash bug related to SecurityController.MinRank + +0.503 + Feature: Added /deafen command. It temporarily blocks all incoming messages (makes you go "deaf"), to allow taking screenshots, recording videos, or just playing without the wall of text. + Feature: Added ShowBannedConnectionMessages config key (default: true). When disabled, no "banned player tried to log in" messages are shown. + Fix: Fixed PlayerInfo.frozenOn not being set. + Fix: Fixed kick messages for players affected by /banall + Fix: Fixed /unbanall reason not being shown. + Fix: /where now shows what world you are on. + Fix: Fixed a couple minor bugs in /waccess and /wbuild. + Fix: Fixed a NullReferenceException in SecurityController constructor. + Fix: Made several tweaks to improve the first-run experience. + +0.502 + Fix: Adjusted for yet another change in the way minecraft.net replies to heartbeats (should fix all heartbeat issues for now). + Fix: Added a check to /zremove to prevent players from deleting zones to gain access to areas from which they're barred. + Fix: Fixed being able to use /me or /staff to write empty messages. + Fix: Fixed frozen players not getting defrosted when kicked. + Fix: Formatting fixes in "___ was previously frozen" message + Fix: Fixed a mini-bug in Map.ValidateHeader() + Change: Renamed /wremove to /wunload for consistency. "/wremove" is still usable as an alias. + +0.501 + Feature: Added /sphere and /sphere2, based on code contributed by SystemX17. + Feature: Added new config setting: ShowConnectedMessages (default: true) + Feature: Added a notification for players who logged in frozen. + Fix: Fixed frozen players twitching. + Fix: Fixed IRC bot not stripping color codes in messages relayed from IRC to the game. + Fix: Fixed lastSeen and lastLoginDate not being set for first-time players who failed to log in (e.g. IP-banned). + Fix: Fixed /bring not working properly on frozen players. + Fix: Fixed a case where it was possible to add access restrictions to main map. + Fix: Fixed per-world whitelists/blacklists being lost when editing worlds.xml via ConfigTool. + Fix: Fixed AutoRankAll showing wrong number of players promoted/demoted (just a formatting bug, no promotions were affected) + +0.500 + Commands: Added /line drawing command, kindly contributed by SystemX17 + Commands: Added /restart command, to restart the server remotely (NOTE: does not work for fCraftWinService yet) + Commands: Added /unmute command (same as "/mute name 0") + Commands: Added an option to silently hide (no disconnect/connect message). + Commands: Added /ignore and /unignore commands to block chat from another player (for the duration of your visit). + Commands: /whide and /wunhide commands added to hide/unhide worlds from the list. + Commands: /worlds now takes an optional param: "/worlds all" lists visible+hidden worlds, and "/worlds hidden" lists hidden worlds only. + Commands: List of player names from the same IP is now shown in /info. + Commands: /help now shows what rank you need to be to use a command (for most commands). + Commands: /bring now works on frozen players. + Commands: /bring now takes an optional second name/parameter, to teleport someone to someone + Commands: /info now shows whether a player is hidden (if the command caller has permission to see). + Commands: Fixed a case where teleporting to a player who is currently changing worlds brings one to a wrong location. + Commands: Fixed bindings (/water, /lava, /solid, and /bind) not being applied to draw commands. + Commands: /wsave and /gen can now create directories if needed. + Commands: Fixed a bug in /roll + Commands: Sped up /paste, /pastenot, /replace, and /replacenot slightly. + ConfigTool: New "Chat" tab for controlling appearance and content of in-game chat. + ConfigTool: Added ability to customize MeColor (for /me messages, default: purple) and WarningColor (for major errors/warnings, default: red) in config. + ConfigTool: AddWorldPopup can now show detailed info for maps of all supported formats. + ConfigTool: Improved tooltips for several items in ConfigUI. + FrontEnd: Uncaught exceptions that happen while executing commands from fCraftUI console are now handled by the crash reporter (instead of actually crashing). + FrontEnd: Added /clear command for fCraftUI and fCraftConsole - clears log screen. + FrontEnd: The URL is no longer highlighted after first heartbeat, to avoid stealing focus from console. + Generator: Fixed "flatgrass" template + Generator: Fixed alphabetical order in the list of templates + Generator: MapGenerator now embeds generation parameters into the map file metadata + Generator: Fixed several template loading bugs. + Generator: Optimized AddBeaches to be about 2x faster + Generator: Added a missing dimension check to /gen + IRC: Messages relayed from game to IRC now have color codes preserved. + IRC: Kicks, Bans, unbans, promotions, and demotions can now be relayed to IRC. + IRC: IRC bot now relays /say messages, and /me messages (both /me on IRC and /me on the server). + IRC: IRC Error handling is greatly improved. + IRC: (Threaded) If one of the bots loses connection, one of the other bots take over parsing input. + IRC: Increased IRC reconnect delay to 15 seconds. + Maps: Switched to a new file format (FCMv3) that allows for future additions like block tracking and physics. + Maps: Much of MCC (map conversion) API was rewritten for better consistency and performance. + Maps: Improved support for loading indev (pre-infinite) maps, including Omen-generated maps. + Maps: Improved resilience for loading maps with unknown blocktypes. Instead of failing, it just replaces any unknown blocks with air. + Maps: Added consistent header validation to all map format loaders. + Maps: Fixed maps not autosaving when zones are added or removed. + Maps: Zone definitions are now serialized to XML. Zones saved with this revision of fCraft will no longer be readable by older version of fCraft. + Maps: Fixed a bug when loading JTE's format maps. + Maps: Fixed format of backup filenames. + PlayerDB: Added new fields: bannedUntil, frozenOn, frozenBy, mutedUntil, mutedBy, IRCPassword, online, leaveReason + PlayerDB: Fixed PlayerInfo.ID not being assigned for new players until sever restart / DB reload. + PlayerDB: Fixed off-by-one error with PlayerInfo.timesVisited + PlayerDB: Fixed /me, /say, rank chat, and /staff not counting towards PlayerInfo.linesWritten + PlayerDB: Made PlayerDB more resilient to errors. Reduced filesize slightly by omitting default values for more date fields, and removing milliseconds from "totalTime" + PlayerDB: Improved forwards compatibility of PlayerDB by removing maximum-field-count check. + Security: Worlds can now have player whitelists and blacklists (just like zones) for access and build permissions. + Security: Added a security check to /wsave, /wload, and /gen to prevent reading or overwriting files outside the map folder. + Security: Added a per-rank security option to prevent players from whitelisting themselves, removing themselves from blacklists, or lowering their rank's restrictions on worlds and zones. + Security: /mute and /freeze are now persistent even if the player reconnects. + Security: Relaxed SetTile packet checks. Players will no longer be kicked due to Minecraft client bugs. + Security: Fixed an exploit that allowed changing steps to doublesteps even inside zoned areas. + Security: Improved consistency of permission dependencies. + Security: Fixed a security exploit in /wmain that allowed players with ManageWorlds permission to gain access to restricted worlds. + Security: Fixed a bug that prevented banning players who were not yet in PlayerDB. + Security: Added an option to only allow paid players (PaidPlayersOnly) + Stability: Made crash reporting more reliable. A failed crash report will no longer prevent error message from being logged and displayed. + Stability: Fixed a crash in AddWorldPopup if dialog is closed while a map is still loading. + Stability: To improve compatibility with some bots, removed "almost there..." message on the loading screen + Stability: Disabled error reporting of common non-fCraft-related errors (like wrong runtime versions, or wrong Mono permissions). + Stability: Added "libMonoPosixHelper.so" and "UnauthorizedAccessException" to Logger.CheckForCommonErrors, and made "Could not load file or assembly" check locale-independent. + Stability: A backup file is now created whenever PlayerDB, IP ban list, config, or world list are saved (e.g. PlayerDB.txt.backup) + Stability: Crash reporter now sends last 25 lines of the log. + Stability: Added a fallback overload for Player.Message() that bypasses string format, to make the whole thing less prone to format errors. + System: fCraft now takes command-line arguments (at startup) to override the following: + - working directory (--path=/your/path/here) + - log directory (--logpath=/your/path/here) + - map directory (--logpath=/your/path/here) - also overridable in a more user-friendly way via ConfigTool (MapPath key) + - config file location (--config=/your/path/and/filename.xml) + System: Added an option to change the name of Console pseudoplayer in the config. + System: Player.GetClassyName() and PlayerInfo.GetClassyName() now add an asterisk after banned players' names + System: Fixed hidden players remaining hidden in some cases. + System: Fixed PacketWriter.MakeWrappedMessage ignoring uppercase hex colorcodes + System: Implemented NoPartialPositionUpdates and RelayAllUpdates config keys. + System: Several little glitches and inefficiencies fixed in movement handling code. + System: Fixed "player connected" and other messages showing up too early in-game (before map loaded). + System: Fixed kick message for players affected by /banall + System: Added HeartbeatEnabled config key (default: true) + System: Fixed a case where heartbeat could be sent from a different IP than the listening port on systems with multiple NICs/IPs and no configured static IP. + System: Increased the write buffer for writing out PlayerDB, reducing I/O time by ~20% on .NET, and ~35% on Mono + System: New Scheduler API. + System: Modified /wload to avoid loading the map twice in case confirmation is needed. + System: Changes to AutoRankEnabled will now be applied when /reloadconfig is called. + System: Fixed unnecessary confirmation being asked when world name matches file name in /wload + System: Eliminated the heartbeat thread by switching to async i/o. + System: Improved startup and shutdown speed. + System: Added a "how to promote yourself" message for fresh installations of fCraft. + System: Fixed Server.OnPlayerDisconnected event firing for unregistered sessions. + System: Made Color.Parse() and Color.GetName() translate fCraft-specific color assignments (e.g "&S" for "System") + System: Added better detection for SMP/Alpha clients trying to connect. + System: Sped up AutoRankAll + +0.499 + Feature: Added an option to disable name verification for players connecting from LAN. + Feature: Added 'flat' template to MapGenerator, allowing to make flat maps with any theme. + Feature: Added 'grass' pseudotheme to /gen command, which is basically the 'Forest' theme with trees disabled. + Fix: Fixed /wflush leaving ghosts/clones behind. + Fix: Fixed muted players being able to use /say and /staff + Fix: Fixed /baninfo ignoring permission checks. + Fix: Fixed "draw limit" checkbox getting disabled in ConfigTool if a player had 'CopyAndPaste' permission but no 'Draw' permission. + Fix: Fixed several off-by-1 errors involving random number generation, notably in MapGenerator and Server.GenerateSalt() + Fix: Fixed potential crash in MapGenerator.SealLiquids + Fix: Fixed a potential crash in Map.SaveBackup() if the map filename contained brackets. + Fix: Fixed a forced disconnect when trying to feed /gen a theme or template with brackets in the name. + +0.498 + Change: Disabled zlib support due to more potential memory leaks. fffuuuuu.... + Fix: Fixed Island, Bay, and Ice templates broken by the last patch. + Fix: MapGeneratorArgs: snowTransition was interpreted as absolute (instead of relative) altitude, and marbledHeightmap incorrectly defaulted to 'true'. + Change: Updated readme and changelog. + +0.497 + Feature: Added accurate progress reporting from MapGenerator. Coming soon: cancellation. + Feature: New MapGenerator options: BeachHeight, CliffSmoothing, LandSlope, UnderwaterSlope, MaxHeightVariation, MaxDepthVariation. + Feature: Added UI controls for snow/cliff/beach settings to AddWorldPopup. + Feature: Added a list of popular IRC networks to ConfigTool. + Change: Improved PlayerDB lookup performance by reducing locking. + Fix: Fixed MapGenerator calculating slope incorrectly at the water boundary. + Fix: Fixed "Add Ore" requiring "Add Caves" in MapGenerator. + Fix: Fixed water/lava caves not sealing liquids properly. + Change: Set MapGenerator to default to 256x256x96 + Change: AddWorldPopup Generator tab layout overhaul + Change: Organized MapGeneratorArgs and added serialization/deserialization for all the new options. + +0.496 + Fix: Fixed File.Replace() failing if fCraft was set up on a partition/volume other than the one where temp folder is. + Fix: Added missing tooltip for "Edit Greeting" + Fix: Fixed /sinfo including hidden players in the player count. + +0.495 + Feature: Added a way to edit the greeting message, by editing "greeting.txt" + Feature: When a player gets banned, any previously-banned accounts from same IP are listed. + Fix: Fixed renaming worlds from ConfigTool's "worlds" tab not always working. + Fix: Fixed several filesystem-related errors on non-Windows systems by using Path.Combine() instead of string concatenation. + Fix: Possible workaround for missing heartbeats. + Fix: Fixed detached "default build rank" controls in ConfigTools. + Fix: Fixed overwrite confirmation popping up for /wload when loading map into an unloaded world with the same name. + Fix: Fixed a case where Forester could get into an infinite loop when there's not enough room to place requested number of trees. + +0.494 + Feature: New map generator features: + - Adjustable water level. + - Realistic trees (powered by Forester) + - Snow on mountaintops + - Realistic cliffs + - Beaches + - New template (peninsula) and theme (swamp). + - Option to delay heightmap bias until after marbling/inversion. + Feature: Added a way to load .mine maps. + Feature: Added confirmation for potentially destructive commands: /importranks, /importbans, /massrank, /wload overwrites, /gen overwrites, /wsave overwrites, /zremove. + Feature: Added /staff chat and ReadStaffChat permission. + Feature: Added wireframe cuboid (/cw) + Feature: Added an optional second param for /ch (cuboid hollow) that specifies the blocktype of the "filling". + Feature: Added a way to specify default build permission for new worlds. + Fix: Fixed /info, /baninfo, and /banip not handling players whose name contain only digits. + Fix: Fixed DoChangeRank mixing up RankChangeType. + Fix: Added missing fields to MapGeneratorArgs serializer. + Fix: Fixed per-rank draw limits not being calculated correctly in some cases. + Fix: Replaced File.Delete/File.Move calls with a more reliable File.Replace call. This should slightly improve reliability of map/playerdb.txt/ipbans.txt/worlds.xml saving. + Fix: Fixed a little ConfigTool bug that did not properly reset PatrolledRank after a rank deletion. + Fix: Fixed terminal colors not resetting in fCraftConsole after a fatal crash under Mono. + Fix: Fixed detached "Play" button in fCraftUI. + Fix: Added some basic error reporting to fCraftWinService. + Change: Tweaked AddWorldPopup layout to accommodate more options. + +0.493 + Feature: /info now supports autocompletion, wildcards, and IP search. + Feature: Experimental service wrapper for fCraft. + Feature: Added more editable properties to /setinfo: RankChangeType, BanReason, UnbanReason, RankChangeReason, LastKickReason + Feature: Various improvements to /info, including hide-aware "online" indicator. + Feature: Made Player.NoAccessMessage() print the minimum required rank, instead of just permission name(s). + Feature: Added wootalyzer's chunk rendering code to IsoCat. + Fix: Fixed ConfigTool not taking advantage of ZLibStream + Fix: Fixed a very improbable case of a thread getting spinlocked when calling IRC.Send() is preemptied, and if there are at least 2 IRC threads. + Fix: Fixed kick packets occasionally not being delivered due to a missing Flush() - thanks to wootalyzer for catching. + Fix: Excluded banned and inactive players from DumpStats + Fix: Fixed PlayerDB.Save() not escaping LastKickReason + Fix: Cleaned up extra messages sent by /unhide. + Fix: Fixed LastSeen not updating on-login (only on-logout). + Fix: Improved the way fCraft checks for ghost/leftover sessions. Thanks to wootalyzer. + Fix: If a player's name starts with a number, and you try to /tp to a coordinate that starts with the same number, you are no longer teleported to that player instead. + Fix: Fixed hidden players being detectable by checking LastSeen in /info. + Fix: To avoid performance degradation, GC calls have been centralized, delayed, limited to 1/minute, and moved to the tasks thread. + Fix: Added delay between recurring calls to PlayerDB.Save, AutoRankAll, and DoGC - to balance load. + Fix: Fixed rankChangeType not being updated. + Fix: Fixed some typos reported by tvc. + Change: Added "/uf" as an alias for "/unfreeze", by popular demand. + Change: Modified PlayerDB not to use ReaderWriterLockSlim, to avoid Mono issues with recursion. + Change: Renamed /editplayerinfo to /setinfo + Change: Made enum values in AutoRank (CriterionType, ConditionField, ComparisonOperation, ConditionScopeType, RankChangeType) case-insensitive. + Change: Added a reference to /baninfo in /info + Change: Made DetectMovementPacketSpam warn players instead of kicking. + +0.492 + Feature: Added EditPlayerDB permission. This permission affects the upcoming AutoRank commands, /editplayerinfo, and ability to promote/demote/ban unrecognized players. + Feature: When you kick a player, you are now shown the number of kicks they already have. + Feature: More stats are now logged! RankType, TimeSinceLastKick, LastSeen, BlocksDrawn, LastKickBy, LastKickReason. + Feature: Added "MaxUndo" config key for changing the default undo limit of 2,000,000 blocks per player. + Feature: /info command is augmented with new stats (if available). + Fix: Fixed ellipsoid command calculating volume incorrectly for draw limit checks. + Fix: Fixed overflowing color code in "Player logged in from an IP previously used by banned players" message. + Fix: Fixed PM recipient's name not getting logged properly. + Fix: Previously-unseen players no longer have their FirstLoginDate, LastLoginDate, or TimesVisited set. + Fix: /importranks now works correctly on players who are online at the time of import. + Change: Added block aliases by popular demand: "bedrock" as an alias for admincrete, "cotton" as alias for "white", and "aire" as alias for "air" + Change: PlayerDB.txt format changes: + - MaxFieldCount raised to 36 + - New fields added: UID (Int32), RankChangeType (enum, serialized as Int32), LastKickDate (DateTime), LastSeen (DateTime), BlocksDrawn (Int64), LastKickBy (string), LastKickReason (string). + - RankChangeBy, BannedBy, UnbannedBy, BanReason, UnbanReason, and RankChangeReason now default to empty string instead of "-" or null. + - If LastIP==IPAddress.None, empty string is saved instead of "255.255.255.255". + - DateTime and TimeSpan format shortened to exclude microseconds. See PlayerDB.ToCompactString() for details. + Change: AutoRank improvements (disabled in stable builds): + - Added /editplayerinfo command for easy editing of PlayerDB. + - Made AutoRank check players in-login (to accelerate promotion). + - AutoRank now runs on a scheduler. + - Fixed ConditionPreviousRank crash for played with default rank. + - Fixed banned players getting promoted by AutoRank. + - Fixed several serialization/loading issues in AutoRank. + - New fields added to ConditionIntRange: LastSeen, BlocksDrawn, TimeSinceRankChange, TimeSinceLastKick + - ConditionRankStatus renamed to ConditionRankChangeType + +0.491 + Feature: Added ability to run multiple parallel IRC bots, to help high-traffic servers deal with IRC lag. This also makes it possible to connect to multiple networks in the future. + Feature: Added new block aliases, by popular demand: "emerald"=teal, "fuchsia"=magenta, "cloth"=white, "lightgray"=gray, "lightgrey"=gray, "darkgray"=black, "darkgrey"=black, "brass"=gold + Fix: Temporarily disabled 64-bit zlib support while I track down memory leaks. Should fix OutOfMemoryException, I'm very sorry about overlooking that. + Fix: Fixed a bug in /info that displayed incorrect "total time on server". + Fix: Per-rank permission checks for /freeze now apply as intended + Fix: Fixed several improbable (but not impossible) threading deadlocks/crashes. Thanks to wootalyzer for catching these. + Change: Added IRCThreads config key (default: 1, allowed range: 1-3) + +0.490 + Feature: Testing a new map compressor/decompressor: ZLibStream. It should provide 50-200% speed increase, smaller map sizes, lower bandwidth use, and faster map loading. + Fix: Fixed /shutdown not waiting the right amount and mixing up seconds/milliseconds. + Fix: Fixed "No world found with the name..." showing player's own name. + Fix: Added a fail-safe exception handler to Player.WaitForKick() - a temporary fix. + Change: Previous verification salt is now stored, to allow players to validate even right after the server restarts. + Change: Restructured SVN. Stable code is now always in /trunk/ + +0.487 + Feature: Added contextual help (tooltips) for all of ConfigTool. Help for "Add World" popup is coming in next version. + Feature: Added rank limits for Freeze permission + Fix: Fixed /mute being unusable from console. + Change: Removed ViewOthersInfo requirement from /sinfo + Change: Added ViewOthersInfo requirement to /where + Change: Improvements to IRC tab layout in ConfigTool. + +0.486 + Quick fix for IRC Bot bug that made server unconnectable. + +0.485 + Feature: Added optional "delay" parameter to /shutdown (default: 5) + Feature: World names can now be autocompleted for many commands (like /join and /lock) + Fix: Fixed /unhide failing to reveal the player in certain situations. + Fix: Fixed /setspawn being usable on players from other worlds. + Fix: Fixed player names appearing white for all players. + Fix: Fixed a recursion loop in NoWorldMessage() + Fix: Fixed help entries for /setspawn and /shutdown + Fix: Fixed potential crash in Server.Shutdown() if called after failed initialization/startup. + Fix: Fixed potential deadlock/freeze in fCraftUI on shutdown. + Change: Improved crash reporting for TargetInvokationException and TypeInitializerException errors. + Change: All assemblies are now once again x86/x86_64/IA64 compatible. + Change: Server.OnPlayerConnected event now supports cancellation. + +0.484 + Feature: Added a way for IRC bot to use registered nicknames. + Feature: /banall-related kicks are now announced in chat. + Feature: /setspawn now actually changes player's spawn. Also, an optional PlayerName param allows temporarily changing other players' spawn point. + Feature: Several improvements to /info. Info now shows whether player's nick or IP was banned, and hides undefined information for players who haven't ever logged in. + Fix: Fixed crash in ConfigTool if announcements were disabled. + Fix: Fixed /banall not matching IPs properly, and not counting kicks towards PlayerInfo. + Fix: Fixed ConfigUI not shutting down properly if Server.Init() or Server.Start() failed. + Fix: Fixed Config.LoadLogOptions() always leaving Debug enabled. + Fix: Added Process.GetCurrentProcess().Refresh() to ServerInfo to improve accuracy of /sinfo stat reporting. + Fix: Improved /wsave and /wload path handling on non-Windows platforms. + Fix: Replaced some potentially unsafe string concatenation in Player.Message() with proper formatting. + Fix: Attempting a workaround for Mono memory leak in World.UnloadMap + Fix: Fixed ranks from pre-0.480 configs being loaded in wrong order. Sorry! + Change: Added a brief delay before reconnecting IRC bot after getting kicked from a channel. + Change: PlayerDB is now saved every 60 seconds (instead of every 30). + Change: Removed unused IRCBotQuitMsg config key, and added IRCRegisteredNick, IRCNickServ, and IRCNickServMessage keys. + Change: Added a popup with crash information to ConfigTool. + Change: Made announcements disabled by default. + +0.483 + Feature: Myne/MyneCraft/Hyvebuild/iCraft maps can now be loaded from ConfigTool's "Add World" dialog. + Fix: Fixed incorrect range checks for AnnouncementInterval and SaveInterval config keys (thanks to Shcaap for reporting). + Fix: Fixed /hide showing a slightly different "player left" message compared to normal disconnect (thanks to Silverrock for reporting). + Change: Added MC#-style "/z" alias for cuboid, by popular demand. + Change: Hidden players can now receive PMs. The sender still gets the "No player found" message, but PMs are actually sent. + +0.482 + Feature: Added new command aliases: "f" for "freeze", and "pat" for "patrol". + Fix: Fixed typos in mute-related messages. + Fix: Added missing triggers for OnPlayerListChanged event. + Fix: Fixed /reloadconfig not changing several variables (like colors). + Fix: Fixed antigrief defaulting to 35/5 (instead of unmetered). + Fix: Fixed heartbeat counting hidden players. + Fix: Fixed a rare ObjectDisposedException crash on shutdown when running fCraftUI. + Change: Added a more informative disconnect message to players connecting with Alpha client. + Change: Improved the way player disconnects are handled to be more reliable, and to drop disconnected players sooner. + Change: Improved the way PlayerDB saving is triggered. New schedule reduces overall disk use and reduces possible data loss in case of crashes. + +0.481 + Fixed a bug in Rank deserializer that set antiGriefBlock=antiGriefSeconds (messing up antigrief permissions). + Fixed a few more references to "player class" terminology instead of "rank". + +0.480 + Feature: Changed all "player classes" terminology to "ranks" for consistency with other servers. Several commands have been renamed to reflect that: /classes to /ranks, /user to /rank, /class to /rankinfo. All rank-related messages have also been updated. + Feature: Added "Raise"/"Lower" buttons to Ranks tab in ConfigTool, and made some other adjustments to the tab. You no longer need to assign a "rank" number to reorder ranks. + Fix: Fixed a crash in ConfigTool that happened if classes were added/renamed/deleted while PatrolledClass was not set. + Fix: Improved fCraftUI responsiveness on-startup. + Fix: Fixed several cases of Worlds tab going out-of-sync with the ranks tab. + Fix: /wremove now always saves the map before unloading. + Fix: Fixed Console being affected by chat spam/flood checks. + Fix: Fixed /zinfo not showing any separators between player names. + Fix: Fixed an extra comma appearing in the log entries for /replace and /replacenot calls. + Fix: Fixed Security and IRC tabs not being reset by "Reset All Defaults" + Fix: Fixed bugged error messages in /tp and /bring when world access permission check fails. + Fix: If any class limits were set to default/"(own class)", the setting is now preserved. + Fix: Fixed a bug that didn't reset Hide limit if it was set to a deleted rank. + Change: Removed legacy zone loading code for pre-0.420 maps (due to changes in rank definition format). + Change: Added a more informative disconnect message for players trying to connect with Minecraft Alpha clients. + Change: Improved rank-based coloring in world list to check both access and build permissions. + Change: Added "/fetch" as alias for /bring, and renamed "/save" to "/wsave". + Change: Deleted some obsolete/disabled controls from ConfigTool, removed PingInterval config key, and removed placeholder ControlPhysics and AddLandmarks permissions. + Change: Made several changes to improve disconnect detection: + - Added occasional pings (every 5 seconds or so, depending on activity/inactivity). + - Reduced socket send/receive timeout from 12 to 10 seconds. + - Increased socket polling interval to improve performance. + Change: If a player tries to connect to a server twice, the FIRST connection is dropped instead of the LAST connection. + Refactor: Major changes were made to the way ranks are stored, and to RankList (formerly ClassList) API. + Optimization: Made IsoCat rendering faster, and made draw commands use less CPU. + +0.475 + Feature: /ztest now distinguishes between being allowed/denied by rank/class vs by whitelist/blacklist. + Fix: Fixed being unable to change rank/class restrictions with /zedit + Fix: Fixed a bug that reported wrong average CPU usage + Fix: Fixed a crash on saving in ConfigTool, related to new PatrolledClass config key. + +0.474 + Feature: "/mute" command to temporarily stop players from chatting. A new class permission (Mute) is added, granted to op+ by default. + Feature: "/shutdown" command to gracefully shutdown the server remotely. A new class permission (ShutdownServer) is added, granted to owner by default. + Feature: Added CPU usage information and loaded world count to /sinfo. + Fix: Added a way to edit "PatrolledClass" from ConfigTool. + Fix: Fixed zone whitelist/blacklist not updated correctly when edited. + Fix: Made teleporting to spawn (with /spawn or /tp, no parameters) accessible to all classes. + Fix: Adjusted for Notch's errors in handling player positions in teleport packets. Slightly improves accuracy of /tp, /bring, initial spawning, /patrol, frozen players, and speedhack detection. + Fix: Removed "/tp" bounds check when used with typed-in coordinates numbers. + Fix: Fixed an extremely unlikely case of PlayerDB/IPBanList overwriting on shutdown if server initialization failed. + Fix: Made "server shutting down" packets deliver more reliably (but still not guaranteed). + Fix: Fixed Console possibly not seeing hidden players. + Change: Added a basic file check to AutoLauncher + Change: Added error logging and crash reporting to Server.Shutdown + API: Renamed Server.ShutdownStart to Server.ShutdownBegin (for consistency). + +0.473 + Feature: Added zone coordinates to /zinfo + Fix: Fixed cross-world /tp teleporting the wrong player. + Fix: Fixed odd date formatting in /zinfo + +0.472 + Feature: Added /cut command + Feature: Added a new /patrol command. It allows cycling through low-ranked players on a map (with teleportation). The order is determined by how long it's been since someone checked on a player. A "Patrol" permission and "PatrolledClass" config key were added. + Feature: Added a shorthand notation to /zone for making personal zones: "/zone +PlayerName" creates a zone named after the player, with rank limit just above player's, and with player whitelisted. + Feature: Zone whitelist/blacklist now support player name autocompletion, and show rank colors. + Fix: Fixed server always binding to IPAddress.Any regardless of config settings. + Fix: TP/Bring no longer allow teleporting to hidden players on other worlds. + Fix: Server.GetPlayerList(string) now stops searching the list when it finds an exact match. + Fix: Fixed client-crash in /zedit when changing class. + Fix: Fixed possible corruption of edited zones upon map saving. + Fix: Fixed a range of improbable synchronization issues in the way Zone whitelist/blacklist is updated. As a fortunate side-effect, significantly reduced Zone.CanBuild() overhead. + Fix: Temporarily added mainThread.Abort() to Server.Shutdown() that's kicked in if mainThread does not stop within 5s of being notified of shutdown. I am investigating the real cause of the hang. + Fix: Fixed hidden players being revealed by promotion/demotion. + Change: Added less threatening error messages to IRC bot on network errors (SocketException and IOException). + Change: Disabled movement handling code while changing maps. + Refactor: Merged StandardCommands and ImportCommands into AdminCommands + +0.471 + Feature: Added ability to specify the server's IP (to bind to) manually, for machines with multiple NICs. A new config key "IP" (default: 0.0.0.0/automatic) is added. This will also fix problems with heartbeats being sent from a different IP. + Fix: Fixed a bug with player ghosts being left behind when switching worlds. + Fix: Fixed a case with kicked players' connections not being terminated at once (thanks to wootalyzer). + Fix: Fixed several bugs in hide/unhide functionality. + Fix: Fixed a rare crash in AddWorldPopup related to missing maps for existing worlds. + Fix: AddWorldPopup will no longer allow adding a world with a duplicate name. + Fix: Capped maximum players at the newly-discovered protocol limit of 128 (thanks to wootalyzer). + Change: ConfigTool fields now provide better feedback if validation fails. + Change: Changed heartbeat interval to 40 seconds (down from 50). + Change: Added a special error handler for rare/impossible case of mainWorld not being joinable. + Change: Made fatal error messages in fCraftConsole print in red. Better color support coming soon. + +0.470 + Feature: Personal zones - per-zone whitelist and blacklist of individual players. The lists can now be defined with /zadd and edited with /zedit. + Feature: Added cave/ore generator from Omen (BETA). + Feature: Added "/maps" and "/levels" as aliases for "/worlds". + Feature: Added a way to limit the "Hide" permission per-class. Now you can specify whom can a class hide from. + Feature: Zones now remember date/time of creation and last edit, and names of original creator and last editor. + Fix: Unauthorized players can no longer PM or use bring/freeze/unfreeze/kick/tp/where on hidden players. They get the standard "player not found" message. + Fix: Fixed hidden players being counter towards /players total. + Fix: Fixed a potential crash in /wflush when using on unloaded maps. + Fix: Doing /banip or /banall on unrecognized players no longer ip-bans 255.255.255.255. Old IPBanList entries relating to 255.255.255.255 will be automatically discarded. + Fix: Fixed /replace command uses not being logged. + Fix: Fixed /worlds command not showing world count. + Fix: Fixed a client-disconnect when trying to unban a player who is not in the database. + Fix: Fixed "/join PlayerName" not translating to "/tp PlayerName" correctly. + Fix: Fixed a very unlikely client-disconnect in ChangeClass if the entered class name contains curved braces. + Change: Changed default name for "/flip" command to "/mirror", and fixed command's help. Either term still works. + Change: Added more block name aliases: + - Air: delete, erase + - Stone: cement, concrete + - Grass: gras (common typo) + - Obsidian: onyx + Change: Improved formatting of /players, /worlds, and draw command messages. + +0.463 + Fix: Fixed special colors showing up even if ClassColorsInChat is disabled. + Fix: Fixed potential client-crash in World.SendToAll(), and related client-crash in lock/unlock. + +0.462 + Fix: The "OK" button in AddWorldPopup no longer stays grayed out. + Fix: Fixed "Error occured while loading" in the file information dialog for non-FCM files. + Fix: Fixed a crash in /bring when used without any parameters. + Fix: Fixed Server.UrlEncode not escaping 0x00-0x0F characters properly. + Change: Slightly reduced default tree density in MapGenerator. + +0.461 + Feature: New EXPERIMENTAL "/reloadconfig" command, and associated ReloadConfig permission. Use with caution! + Fix: Fixed a case where AddWorldPopup would allow clicking "OK" with a null map. + Fix: Fixed player crash if /freeze or /unfreeze is used without arguments. + Fix: Fixed player crash on FormatException in /zones and Server.ShowPlayerConnectedMessage. + Fix: Fixed potential player crash if LimitOneConnectionPerIP is enabled. + +0.460 + Feature: Added ShowPlayerConnectedMessage and ClassColorsInWorldNames settings to config. + Feature: Added /measure command. + Feature: Class colors and prefixes are now applied consistently EVERYWHERE. + Feature: Banned players now see their ban reason/memo when they get kicked. + Feature: A special error message is shown to players trying to connect with Alpha/SMP client. + Feature: IsoCat (ConfigTool isometric preview) rendering quality improved: better shadows and slight altitude-based gradient. + Feature: Added new blocktype name aliases: "solid", "golden", "halfstep", "halfblock", "steps". + Feature: Added player count for each class in /classes + Fix: Fixed wrong "MaxBackupsSize" range in ConfigTool. + Fix: MaxBackupsSize setting is now applied correctly. + Fix: IRC color codes are now stripped from forwarded messages. + Fix: IRC bot now uses ServerName for "real name". + Fix: Formatting improved in many error messages. + Fix: Commas in ban/unban/promote/demote reasons no longer cause problems with PlayerDB. + Fix: Removed extra comma from /worlds list that showed in place of hidden/inaccessible worlds. + Change: Added 5-second delay between retries in AutoLauncher + Change: "Player connected" message condensed to a single line by absorbing "joined world" message. + Change: Removed ChangeName permission (and the ability to change names) due to possibility of abuse and confusion. Related /nick and /whois commands were also removed. + Change: /tp can now fall back to behave like /join if player name is not found (for all those times when you mistype /tp instead of /join). Conversely, /join falls back to /tp if player was not found. + Optimization: Reduced bandwidth use with an adaptive movement packet skipping algorithm. Should reduced outbound traffic by at least 15%. + Optimization: BlockUpdate memory usage slightly reduced. + +0.455 + Feature: AutoLauncher (BETA) allows restarting the fCraftConsole server if it crashed. Although it honestly shouldn't crash at all! + Fix: Allowed renaming worlds in cases where only capitalization is different. + Fix: If an ip-banned player has more than one connection, all connections are now terminated. + Fix: Fixed nonrectangular MCSharp maps getting messed up by the loader. + Fix: Increased the default timeout from 10 to 12 seconds, to help people with extremely laggy connections. + Fix: When autocompletion finds more than one player name matching a pattern, server now prints the list of names (instead of "no player found" message) Affects PMs, tp, bring, freeze, unfreeze, kick. + Change: Improved OS detection in crash reporter. + +0.454 + Feature: /replace, /replacenot, /paste, and /pastenot can now work with multiple blocks. See /help for details. + Fix: Replace no longer behaves same as replacenot. + Fix: Spaces in IRC messages are now preserved. + Fix: Help entries for paste/pastenot corrected. + Fix: /cinfo (without parameters) now shows player's own class info. + +0.453 + Feature: It is now possible to reverse last /undo (by typing /undo again). + Change: IRC message colors are applied slightly differently now. + Change: Added more automated crash reporting hooks - to MainLoop and Tasks. + Change: More help messages improved. + Change: Added a more informative message for people running outdated versions of Mono and .Net runtime. + Optimization: Slightly reduced memory usage of /undo, /paste, and PlayerDB.Load() + +0.452 + Feature: New /replacenot command allows replacing all blocks EXCEPT the specified one. + Feature: New /flip and /rotate commands to manipulate copied blocks. + Feature: /players list is now in alphabetical order. + Fix: Trying to specify an unrecognized block type for draw commands (e.g. "/c ai" instead of "/c air") now shows a proper error message. + Fix: Fixed /gen command crashing players if coordinates are typed in before theme/template. + Fix: Fixed /e ellipsoids coming out crooked. + Fix: "joined for the first time" messages are now shown correctly. + Fix: Fixed /replace not working correctly with /undo + Fix: Made IRC bot strip nonprintable characters from messages, and ignore empty messages. + Fix: World permissions are no longer reset when doing /wload. Also a permission reminder is now shown when creating new worlds. + Change: Improved some help messages. + Change: Improved IRC error reporting. + Change: Added world name back to "Almost there..." join screen. + Change: /pasteonly is now called /paste, and /paste is now called /pastenot (for consistency with other custom servers) + Optimization: Slightly reduced memory usage of massive drawing commands (over undo limit). + +0.451 + Feature: Improved the way fCraft handles fatal errors (crashes). Crash logs are now created more reliably. + Feature: Added automated crash reporting to fragmer.net (opt-out). + Feature: Added an announcement for players logging in from IPs associated with other previously-banned players. + Fix: Curved braces - "{}" - in server name no longer cause problems. + Fix: "Leaving the map" kick disabled temporarily, will be made configurable per-class soon. + Fix: banall/unbanall commands produce less chat spam. + Fix: AFK kicks no longer count towards /info number-of-times-kicked. + Change: Added class colors to /info, and fixed formatting bug. + Change: Cleaned up "Advanced" tab in ConfigTool. + - Removed obsolete config keys: PolicyColorCodesInChat, PolicyIllegalCharacters, and RunOnStartup + - Added SubmitCrashReports and IRCDelay config keys. + +0.450 + Feature: IRC bot rewritten for better performance, flexibility, and safety. + Feature: New IRC options: IRCBotAnnounceIRCJoins and IRCBotAnnounceServerJoins. + Fix: Fixed messages containing only a single slash ('/') crashing client thread. + Change: Removed obsolete IRCBotMsgs config key. + Change: Added OnPlayerSentMessage event to Server. + Change: IRC bot's commands (e.g. !status) are temporarily unavailable. + +0.445 + IMPORTANT! Please update ASAP, a security was identified in previous version. + Fix: If you need to start message with a slash, you can now escape the slash (type "//") + Fix: Fixed endless update loop (if automatic updates were enabled) + Fix: Private messages with a space between '@' and the player name now work. + Fix: Hacking-related kicks are now announced globally. + Fix: Fixed "/help" giving wrong message if help entry was not found. + Change: Minor changes in MapGenerator/Noise + +0.444 + Feature: New stat command, /sinfo (server info). + Fix: Ban reason/memo now shown for normal bans. + Fix: /cinfo for unlimited classes no longer shows "draw limit: 0" + Fix: Fixed IRC message colors, and removed IRC color codes from log. + +0.443 + FEATURE: New /wflush command. It allows finishing all in-progress draw commands at maximum speed (over 100k blocks/second) then forces a map rejoin. + Feature: Added more customization options for colors: PrivateMessageColor and IRCMessageColor in Config. + Feature: /players list is now sorted alphabetically. + Feature: Added "Check Port" button to ConfigTool to see if port is open. Uses www.utorrent.com/testport + Feature: Added "Play" button to fCraftUI to open server URL in the browser + Feature: World names in /worlds list are now colored according to each world's build permission. + Feature: Added speedhack detection & prevention (BETA) and corresponding Permission.UseSpeedHack. All your classes will be granted this permission by default. Edit the config to customize it. + Fix: When using /mark outside of map boundaries, draw limits are no longer calculated incorrectly (thanks to Darnest). + Fix: Fixed possible crash in Map.CheckZones() on maps created with /gen + Fix: Fixed compilation issue with "tileset.png" on case-sensitive filesystems. + Fix: Fixed class-wide messages not wrapping correctly. + Optimization: antigrief and antispam checking overhead reduced. + +0.442 + Fix: Addresses a security issue. + Fix: "OK" button in AddWorldPopup is now always disabled if no map is set. + Fix: "Feature size" label is now updater correctly when map dimensions are changed. + Fix: Added missing player notifications for /undo and /cancel commands. + Change: MapGenerator's default map size changed to 128x128x80 + +0.441 + Feature: Added cuboid limits to /cinfo + Fix: Fixed a bug in BoundingBox that made /paste and zones behave incorrectly + Fix: Fixed ban/unban commands not kicking the player if entered name capitalization did not match actual capitalization + Fix: Fixed "flatgrass" being broken when using "/gen" + Fix: Fixed "/ban" rank checks affecting "/unban" + Change: Optimized zone permission checks + +0.440 + FEATURE: New MapGenerator with tons of options. You can also save/load custom templates for it. + FEATURE: AddWorldPopup interface overhaul. + FEATURE: JTE, D3, and Myne/Mynecraft/Hyvebuild/iCraft map format converters added. + Feature: Improved syntax of the /gen command. See "/help gen" for details. + Feature: Reason/memo is now shown publicly when someone gets banned, unbanned, or kicked. + Feature: Teleport and Bring work across worlds - world permissions still apply. + Feature: Added 4 new Config keys: RequireBanReason, RequireClassChangeReason, AnnounceKickAndBanReasons, AnnounceClassChanges. By default "AnnounceKickAndBanReasons" and "AnnounceClassChanges" are enabled. + Feature: Added 5 new fields to PlayerDB: previousClass, classChangeReason, timesKicked, timesKickedOthers, timesBannedOthers. + Feature: Added a lot of new information to /info. + Feature: You can now save map preview images (isometric) from AddWorldPopup. + Feature: ConfigTool now shows warnings about any errors logged during Config.Load + Fix: Yet another workaround for VerifyNames false negatives (thanks to SpaceManiac). + Fix: Fixed being able to paste admincrete/water/lava without permission (part of Player.CanPlace overhaul). + Fix: Trying to go over class draw limit no longer causes "Another drawing command is in progress" error. + Fix: Mcc.MapUtility.TryLoading() and Map.LoadHeaderOnly() now release file handles as soon as possible. This should fix some "file is being used by another process" errors. + Fix: Map spawn should be parsed correctly for MCSharp/MCZall maps + Fix: If a map doesn't start with a gzip header, it no longer has a chance to abort the whole load process. + Fix: Fixed blocktype names being case-sensitive. + Fix: Added a more informative message when idle players are kicked. + Fix: Fixed undo buffer not getting cleared when doing paste/pasteonly + Fix: Fixed /waccess exception kicking players. + Fix: 100% Empty maps no longer crash AddWorldPopup with OutOfMemoryException + Fix: Fixed "player is already banned" message when doing /BanAll + Fix: Fixed no public message appearing when doing /BanAll + Fix: Fixed cuboid/ellipsoid/cuboidh logging blocktype code instead of blocktype name. + Fix: Improved the message that's shown when idle players are kicked. + Fix: Documented ability to /tp to specific coordinates. + Fix: AddWorldPopup no longer crashes if user closes "Load Map" dialog before selecting any file. + Fix: When drawing affects (in part or in full) a restricted zone, the zone is left untouched, and the rest of draw command finishes (part of Player.CanPlace overhaul). + Fix: Fixed crash in AddWorldPopup.AsyncLoad() if map loading failed. + Fix: LegacyRankMapping is now parsed correctly + Fix: Renaming a class no longer causes "Worlds" tab dropdown menus to go out-of-sync. + Change: AddWorldPopup preview image quality improved. + Change: Updated to newest version of NBTag library. + Change: Frozen players can no longer build/delete. + Change: Removed public "undo" message, added log message instead. + Change: Removed AnnounceUnverifiedNames settings. + Change: When building/deleting permission is denied, the exact reason is given (instead of a vague "you cannot do that" message) (part of Player.CanPlace overhaul). + +0.435 + Feature: Added /copy, /paste, and /pasteonly commands. Rotation coming soon. + Feature: Added CopyAndPaste permission, granted to op and owner by default. + Feature: Added MapGenType.Cliffs - similar to Mountains type, but with cliffs! + Fix: Fixed /paint command + Fix: Fixed /baninfo now showing any information if used without parameters. + Change: Replaced surface rocks in MapGenTheme.Hell with Obsidian. + +0.434 + Feature: new /bind command allows mapping any block to any other. Now you can place those unbuildable blocks (water, lava, grass, double-step) in any way you wish. For compatibility, old commands (/water, /solid, /lava, /grass) have been preserved. + Change: "step" added as an alias for Stair block. "doublestep" added as an alias for DoubleStair. + +0.433 + Feature: /winfo now shows who locked/unlocked the map, and how long ago. + Fix: Fixed a error message that showed up when announcements.txt was blank. + Fix: Permission check added back to lock/unlock commands. + Fix: "mapinfo" added back as alias for /winfo + Fix: Fixed locked maps not getting unloaded when all players left + +0.432 + Fix: List of worlds printed by /worlds is now sorted alphabetically. + Fix: Fixed an obscure half-disconnect bug that left players with id=0 who happened to be on the main world in a semi-disconnected state IF someone else was kicked halfway through the login process before it was assigned an id. + +0.431 + Fix: Fixed still messed-up /worlds command. + Change: Downgraded standard Session.IoLoop messages that occur on player disconnect from Warning to Debug. + +0.430 + Fix: Fixed messed-up /worlds command. Sorry about that. + Change: Downgraded standard Session.IoLoop messages that occur on player disconnect from Warning to Debug. + +0.429 + Feature: Command-handler infrastructure has been redesigned. This will allow much more flexibility for devs and plugin makers later. + Feature: "/help commands" now only shows available commands by default. Type "/help commands all" to see all commands. + Feature: /worlds command now only available worlds by default. Type "/worlds all" to include worlds inaccessible to you. + Feature: Help entries added for /zedit, /mark, /version, /rules, and /say. + Feature: ConfigTool World tab columns are now sortable. + Fix: Fixed "max backups" in ConfigTool being limited to 100. Now it's at a more reasonable limit of 100000. + Fix: Fixed Map.Backup() deleting the newest (instead of the oldest) backup if the total number of files was limited. + Fix: Fixed an ERROR that popped up if a player got disconnected during the login process (while player=null). + Fix: FormatException crash fixed that caused whole server to go down instead of a player thread. + Fix: Several draw-related bugs and crashes fixed. + Fix: Fixed crash in /winfo when used on unloaded worlds. + Fix: Fixed a bug in Config that caused antigrief to be re-enabled every load. + Fix: Fixed a bug in ConfigTool that didn't update AntiGriefSeconds properly when changing between classes. + Change: Many /help entries have been improved. + Change: Split /class command into two: /classes (aka /ranks) shows a list of classes, and /class (aka /cinfo or /classinfo) shows permission list (missed from last release notes). + Change: Added /spawn as an alias for /tp (missed from last release notes). + + +0.428 + Feature: Line wrapping. Yes, finally. + Feature: Per-class draw limits. Defaults are: owner unlimited, op unlimited, regular 4096 (16x16x16), guest 512 (8*8*8) + Feature: Per-class antigrief detection. Defaults are: owner unmetered, op umetered, regular 45/6 (7.5 blocks/second), guest 35/5 (7 blocks/second) + Feature: Memory usage of draw commands has been reduced by 6 bytes per block (37% improvement). + Fix: Extra color codes fixed in /players command. + Fix: Fixed a bug with /undo that caused more than one command to be undone at once. + Fix: Fixed maps generated with "Realistic terrain" (MapGenerator) having an empty layer at the bottom of the map. + Change: Default classes have been tweaked: + - regular can now use draw commands + - op can now lock/unlock + Change: Improved arrangement of Permission list items. + Change: Removed obsolete ConfigKey variables AntigriefBlockCount and AntigriefBlockInterval + + +0.427 + Feature: Added /zedit command for changing zone permissions after creation + Feature: New aliases for drawing commands: /c /e /h /r /m + Fix: Fix for cuboid command kicking players with "Block.Undefined" if called with no optional BlockType parameter + Fix: Fixed zone definitions getting corrupted. + Fix: Allowed /save command to be used from console and from remote worlds. Previously you had to join the world to save it. + Fix: Fixed potential crash in /winfo when looking up unloaded worlds. + Fix: Fixed /zone allowing duplicate names, then kicking the client. + Fix: Fixed /ztest showing the standard "you are not allowed to build in this zone" message instead of the expected zone list. + Fix: Fixed incorrect description of draw log messages, and backup error messages + Fix: Fixed false triggering of "Player asked to be released from its world, but the world did not contain the player" error + Change: Fruitified fragmer's name + + +0.426 + Feature: Announcements! You can define a list of announcements to show periodically. A random line from announcements.txt will be shown to all players at specified interval (in minutes). + Feature: /replace command. + Feature: /undo command has been improved for more flexibility and better performance. + Feature: /winfo (alias: /mapinfo) is added for displaying world name, player count, map dimensions, and access/build permissions. + Fix: Fixed a bug that caused duplicate entries to be added to PlayerDB when doing /importranks or /importbans. + Change: New ConfigKeys: AnnouncementColor (default: green) and AnnouncementInterval (default: 5 minutes). + Change: Draw command stride increased to 16x16x16, for better theoretical performance. + + +0.425 + Feature: Added hollow draw command (/cubh) and aliases (/cuboidh and /bhb) + Feature: /players now shows class colors and a total number of players. Breakdown of /players by world is coming soon + Feature: Added "Joined world WorldName" message that shows when changing worlds + Fix: Improved several /help entries + Fix: Fixed a bug in Map.SaveBackup() that could cause a crash if MaxBackups was set + Fix: Fixed a bug in ConfigUI where "Delete World" button could leave the map file behind and/or cause an error + Fix: Calling lock/unlock from console without a WorldName now shows a nicer error message + Change: Improved safety checks of /join command + Change: Added a catch-all exception handler into Server.MainLoop + Change: Added exception handlers for EVERY single piece of code that requires file access + Change: Added more thread safety in MapCommands.WorldRename that resolves a potential race condition with Map.Save + Change: Modified IPBanList to use the standard temp folder (instead of fCraft working path) for its temp files + + +0.424 + Fix: Fixed /unhide and normal connection getting different messages + Fix: Fixed client crash when viewing /rules if the rule file has blank lines + Fix: Worlds with capital letters in the name are joinable again + Fix: Potential crash in DoClassChange() if target player is already same rank AND is not currently on the server + Change: Moved the command list to /help commands + Change: Performance and thread-safety improvements to PlayerDB + Change: Disabled some more debugging code to avoid taking up unnecessary resources + + +0.423 + Feature: /importbans now also supports "banned-ip.txt" + Feature: Logs are now stored in /logs/ folder. Your fCraft.log file will be moved there automatically and renamed to "_OldLog.log" + Feature: Log splitting now works by day or by session. Set "LogMode" key in config.xml or see Logging tab in ConfigTool. + Feature: All maps are now stored in /maps/ folder. Your map files will be moved automatically first time you start ConfigTool/fCraftConsole/fCraftUI + Feature: Reserved slots now work + Fix: Fixed a bug in fCraft.Server.LoadWorldListXML that could make all worlds hidden. + Fix: "player connected" message shown when someone connects, instead of just "player joined world" + Fix: "player left the server" message is not shown if the player got kicked before LoginSequence finished (e.g. if server was full) + Fix: Added back IP logging for incoming connections + Fix: Fixed ConfigTool-generated maps not having correct spawn point + Fix: Fixed /user showing both promote and demote messages at once + Fix: Removed references to old "/drawundo" command name in "/undo" messages + Fix: Fixed "Unrecognized entry ignored: LegacyRankMapping =" warning + Change: Made /importbans and /importranks save PlayerDB at once + Change: fCraft now forcibly resets its working directory to the same path where the executable is + Change: Removed "delete old file?" prompt when renaming worlds in AddWorldPopup. Now it just does it, to match tabWorlds' DataGridView behavior + Change: Added additional error checks to Map.Backup() + + +0.422 + Feature: Further improved network performance by introducing nonblocking (lock-free) data structure to packet output buffer + Fix: Fixed "world creation failed" error if worlds.xml defined a non-existing world to be main + Fix: Added back missing messages when doing /user on offline users + Fix: Fixed player name color not being updated when their class changes + Fix: Added missing /help entries for /importbans and /importranks + Change: Improved help messages for /lock, /unban, /unbanip, /undo, /wload, /wmain, and /wremove + Change: Tweaked default values of BlockUpdateThrottling and maxBlockUpdatesPerTick for maximum lag-free performance + + +0.421 + Feature: Added the ability to promote/demote offline and unseen (not in PlayerDB) players + Feature: Added the ability to ban unseen players + Feature: Added ability to import bans and ranks from MCSharp/MCZall formats. An associated "Import" permission was added. See "/help importbans" and "/help importranks" for more information. Importer for ip-bans and iCraft/Hyvebuild/Myne format are coming soon. + Feature: Improved BlockUpdateQueue performance (faster drawing commands!) by switching to a lock-free data structure + Fix: Fixed startup errors not being logged if config.xml has not been parsed yet + Fix: Fixed ConfigUI input box not aligning properly when window is resized + Fix: Fixed "flatgrass" generated maps being 1 block off vertically + Change: Updated PlayerDB and IPBanList to use standard temporary folder (instead of dumping temp files into fCraft directory) + Change: Added "logging starts" marker that's written to console and log on-startup + Change: fCraftUI log now displays the last 2000 lines (up from 1000 lines) + + +0.420 + Feature: New "Worlds" tab in ConfigTool for managing your world list + Feature: Isometric map previews and map generation in ConfigTool + Feature: Implemented a class-ID system that allows adding/renaming/deleting classes without breaking compatibility. DeleteClassPopup was added for this + Feature: Added LimitOneConnectionPerIP option to config & configtool + Feature: Worlds can now be hidden from the /worlds list + Feature: Added /whois command for checking if a player is using real name or nickname. BE CAREFUL WITH "ChangeName" PERMISSION! + Feature: fCraft now generates crash.txt on unhandled fatal errors + Feature: Implemented theme support, and added 2 new terrain types (Coast and River) for MapGenerator. See "/help gen" for details, or use ConfigTool. + Feature: Pressing Up/Down keys in fCraftUI will show previously typed commands. + Fix: Fixed a bug in /wmain and /wload that sometimes made the server unconnectable after changing map + Fix: Limits on the /kick command now apply correctly + Fix: Fixed /wbuild and /waccess showing wrong messages + Fix: Fixed hidden players unable to build due to grief protection + Fix: Added /nick and /whois entries to /help + Fix: Removed the ability to call draw commands from console (caused crash) + Fix: Fixed /lava checking PlaceWater permission instead of PlaceLava + Fix: A debug message about "attempting to unregister an non-existent player" is no longer logged every time VerifyNames fails + Fix: When using /where command on other players, correct coordinates are now shown] + Fix: Player's classes are now updated in fCraftUI list when promoted/demoted + Fix: PMs are now logged as LogType.PrivateChat (instead of generic LogType.WorldChat) + Fix: /hide and /unhide now use Color.Sys instead of hardcoded Color.Yellow + Fix: Player's nicknames (as set by /nick) are applied correctly and preserved when changing worlds + Fix: Added missing checks for "Chat" permissions + Fix: Color buttons in ConfigTool now show correct background color under Mono + Fix: Fixed help entry to /freeze showing incorrect information + Fix: Corrected tab order in all ConfigTool forms + Change: Added /blb as another alias for /cuboid + Change: Removed ReservedSlotBehavior config key + Change: Disabled word wrapping in rules.txt editor popup + Change: /unhide now resets the player's nick + Change: Both player's real name and nickname (if any) are now recorded in the log + Change: Added a warning to /info when looking up nicknames (instead of real names) + Change: Heartbeat salt generation (used to verify names) is now more secure + Change: Removed CanPlaceHardenedBlocks from permission list + Change: All map generator terrain types have been tweaked for improved appearence + Change: Centered all ConfigTool popup windows (AddWorldPopup, ColorPicker, DeleteClassPopup, TextEditorPopup) + Change: Hid unneeded icons and buttons from ConfigTool popups + Change: Changed the way zones store rank restriction (as part of the compatibility assurance), and added versioning to zone definitions + Change: Map.Save() now creates temporary files in the OS-specific temporary folder, not in same folder where fCraft is installed + Change: World permissions are now updated automatically when a class is deleted + Refactor: Improved consistency in code naming conventions, especially in IRC code + Refactor: Improved code organization in many areas + + +0.410 + Implemented per-world access and build permissions. + Changed worlds.txt format to worlds.xml. Your old worlds.txt file will be converted automatically. + New world-related commands: + /wmain changes the main world + /waccess and /wbuild allow setting per-world permissions + /wremove allows deleting worlds + /wrename allows renaming worlds without reloading + /wload command is significantly improved (see /help wload for more info). The functionality of the old /load command has been merged with /wload. Now /load is an alias for /join, together with /l, /j, and /goto. + Removed SaveAndLoad permission - it became redundant with ManageWorlds. Please check your config to ensure that permissions remain consistent. + Added new and previously-missing (/lockall, /unlockall, /where, /me, /rules, /setspawn) commands to /help. + Disabled verify-names check for localhost (127.0.0.1) when running in Balanced mode. + When player is denied access to some command, server now names the specific permission that's needed. + Fixed a potential desync issue when one player rejoins a world, and another joins it simultaneously. + Removed /genh command and /compass alias for the /where command. + Added a fail-safe exception check when executing console commands. Now the server will stay up even if an exception occurs while executing console command. + Improved the way server reacts to fatal errors on startup. Now there is a clear indication that server cannot start. + "default map" is now referred to as "main world" for consistency. + Improved garbage collection. + + +0.405 + Added back support for vanilla (server_level.dat) maps. + Fixed MCSharp importer leaving nonstandard blocks in the map, causing client crashes. Now all MCSharp-specific blocks are converted to normal types. + Fixed visual player list not updating in fCraftUI. + Added /players command to list ALL players on the server (in all worlds) + Made the BlockType parameter optional for /cuboid and /ellipsoid. Now it takes blocktype of the second click (or last-previously-used-blocktype when using /mark) in the absence of the optional parameter. + ConfigTool: Added a new tab for IRC setting. + ConfigTool: Fixed a bug that prevented the last 2 checkboxes on the permission list from being saved properly. + IRC: Split IRCForwardAll into IRCForwardFromServer and IRCForwardFromIRC to make it easier to manage which messages you want to see. + IRC: Added IRCBotQuitMsg to allow users to define their own quit message for the bot. + IRC: Fixed a bug with infinite reconnects on receiving error 433 Nickname already in use. + Zones: Changed behavior of overlapping zones: + - Before: To touch a block, AT LEAST ONE zone has to allow it + - Now: To touch a block, ALL ZONES have to allow it + Zones: Added /ztest command that lists all zones affecting a particular block. It can be used to detect and resolve zone overlaps. + Zones: Added proper /help entries for /zone, /zones, /zremove, and /ztest, and contextual help for /zone and /zones. + Zones: Made zone commands inaccessible from console (it would cause a crash, since zones are world-specific). + Fixed OnPlayerDisconnected and OnPlayerListChanged sometimes firing at the wrong time. + Added better error handling to Map.Save() in case the target file is not writable. + Updated /help entries for /lock, /unlock, and /load commands. + + +0.404 + Fixed zone borders being off by 1 block. + Fixed a rare crash on shutdown. + Fixed heartbeat reporting incorrect port number to heartbeat if the port specified in config was not available. + Adjusted default class permissions slightly, by popular demand: + - regulars can now place Water and Lava + - ops can now use draw commands + IRC: Added a config option 'IRCMsgs' to toggle join/leave messages on/off. Default is disabled to prevent massive spam. + IRC: Fixed a possible crash in IRCComm, related to static initializers. + ConfigTool: Fixed many bugs in permission checkbox list. + ConfigTool: Added visual editor for rules.txt + ConfigTool: Added stub tab for IRC settings (coming soon). + + +0.403 + Fixed spawnpoint being read incorrectly when using /load. + Added a note to ban*/unban* commands that's shown when player name is not found: "Use the FULL player name for ban/unban commands." + Fixed a thread crash (not server-wide crash) in Session.IoLoop that can be caused by corrupted or malicious handshake packets. + Fixed a potential crash when using /info on offline players from console. + Fixed a potential crash when using /gen from console. + Added more fail-safe checks that ensure that map spawn point is always within the map boundaries. + Started logging IPs of connections that try sending corrupted or malicious handshake packets. + + +0.402 + Added back /load command, and hooked up MCC. Currenly supported: fCraft/fcm, MCSharp/lvl, indev/mclevel, MinerCPP and LuaCraft (dat). Vanilla support coming soon. + Fixed worlds being unloaded before all block updates are processed (thus aborting draw commands). + Fixed several potential multiworld-related crashes and freezes. + Made Server.UnregisterPlayer check ALL worlds to remove the player, not just most recent one. + IRC: Added an information message to notify users of the IRC Bot's online status when joining the server. + IRC: Added the ability to send NOTICES. + IRC: !status/!help can both be sent to the bot in channel now and it will respond appropriately. + IRC: Added a check to see if the IRC Bot is actually enabled and online before sending a notification that it IS online. + Drawing-related state is now reset when changing worlds. + Added a limiter to the number of block updates that can be processed per tick (even if there are no players in a world). Fixes server becoming unresponsive when doing enormous drawing commands (millions of blocks). + Allowed players to /join the world that they are already on. + + +0.401 + *IDLE/AFK CHECKING* This is configurable per-class. + Added /version command, and added revision to the version string. + Added an option parameter to /lock and /unlock to target a specific world, and added global /lockall and /unlockall commands. + Removed all references to the old pre-multiworld "map.fcm." Backups now work again. + Fixed players not spawning in the right place when changing worlds. + IRC Bot will now handle terminations by the IRCd and restart itself upon any errors. + IRC Bot can now send from inside the server TO IRC! Use the prefix '#' in-game to send any message to IRC. Forwarding all server chat may be added if requested. To send from the bot to the server, use the bot's nickname and either ':', ',' or nothing, then your message. + Improved performance of the IRC bot and the task scheduler. + Fixed a bug with getting the server url from the externalurl.txt file crashing the IRC MessageHandler() when the file didn't exist. FORWARD_ALL is now enabled for both SERVER and IRC for 100% transparent chatting. + Fixed a bug in the server-to-irc code that forwarded a blank message if there was nothing after '#' in server chat. + Handshake packet is now written when doing /join, to replace MOTD with a more informative "Now loading [worldname]". + + +0.400 + *MULTIWORLD*. It's finally here. See /help for information on new commands: /worlds, /wload, /join + Added a persistent world list, saved in worlds.txt - this is subject to change. The file contains a list of world names, each corresponding to an .fcm file. The first world listed at the top of worlds.txt is the default/main one. + *IRC BOT*. Thanks to the efforts by Destroyer661, fCraft now has an optional IRC bot for relaying chat and commands. + A field has been added in the config to allow forwarding ALL IRC CHANNEL TRAFFIC to the server. This will override the prefix and make it inactive. 'IRCBotForwardAll' is set to false by default. + *ZONES*. It is now possible to make guest zones and op zones (and anything in-between) on any map. See /help for information on new commands: /zones, /zone, /zremove. + Added 2 new permissions: ManageZones and ManageWorlds. You might need to update your config.xml to add these permissions to appropriate classes. + Added /compass command (aka /where). + Fixed players sometimes not seeing their /kick message. + Added a way to teleport to specific coordinates (/tp x y h) + Fixed heartbeats timing out when running fCraft under Mono. + Fixed Config.ApplyConfig setting the wrong server throttling variables. + Fixed air blocks appearing underwater when using /gen + Tweaked /gen parameters to make "island" a bit less ugly. + Updated text in fCraftConsole. + + +0.330 + Added /cuboid and /ellipsoid drawing commands. + Improved memory usage. + Added "Draw" permission, and made /draw check "DeleteAdmincrete" permission when replacing blocks, and removed unused "PlaceGlitchedSand" permission. + Added "Port" and "BackupOnlyWhenChanged" config lines, and corresponding ConfigTool fields. + Fixed bug that deleted ALL backups when you check "don't delete old backups." That was embarrassing. + + +0.325 + Added an experimental low-latency mode (faster server response, but more bandwidth required). + Improved hacking detection: added anti-chat-spam and anti-grief (block spam) checks. + New more aggressive anti-hacking checks for incoming movement and SetTile packets. + Fixed an issue with fCraftUI console log getting truncated in the wrong places. + Improved Mono compatibility (fixes HttpWebRequest failures). + + +0.324 + Added /me command. + Added step stacking. + Fixed a case where disconnected players would leave a ghost behind. + Improved shutdown speed. + Implemented ProcessPriority setting. + +0.323 + Fixed players not being able to delete admincrete after promotion. + Fixed a bug that allowed players to noclip into walls when using /tp or /bring. + Fixed a security issue with "VerifyNames: Balanced" + Added prototype of the security tab in ConfigTool (work in progress). + + +0.322 + Added a range check to Player.SetTile, to prevent certain types of grief bots. + Fixed a bug where map spawnpoint did not change after doing /load. + Fixed some typos. + + +0.321 + Fixed welcome messages not showing. + Renamed /alias to /nick, and added a permission check. + + +0.320 + Finally last fix for the disconnect problem. Sorry about that. + + +0.319 + Quick fix for disconnect problem. + + +0.318 + Fixed VerifyNames not saving properly when using ConfigTool. + Fixed several potential crashers in Session and World. + Fixed a bug that sometimes wrongly showed "class name not recognized". + Added class name autocompletion for class chat (@@classname blah) + Started working on the new /load mode that allows loading different sized maps, and loading much faster (not done yet). + + +0.317 + Optimized handling of player movement and some block/tile updates, making servers more responsive and less laggy. + Fixed a possible crash that sometimes happened when server has 1 slot left and 2 people tried to join at once. + Fixed a case where a Session object was not properly disposed. + Fixed resizing issue in UpdateWindow and reduced connection timeout in Updater + Disabled ShowPrefixesInChat and ShowPrefixesInList by default (breaks skin support) + Added Player.Send, a safe wrapper for Player.session.Send + Fixed /tp, /bring, and /setspawn commands that were broken in the last revision. + Fixed ConfigTool not saving "IsPublic" setting correctly. + + +0.316 + Added partial class-name matching for /class and /user commands. + Fixed /banip and /banall not kicking the banned players immediately. + Added /rules command. Create "rules.txt" in the server folder to specify. + Fixed a bug where player movement sometimes got a little out of sync. + Optimized /load speed a bit. + Added nicer application icons. + + +0.315 + Added visual color picker to ConfigTool + Added lightweight console interface for fCraft + Refactored the main update loop + + +0.314 + Added AutomaticUpdates option. + Integrated the color picker to ConfigTool. + Improved hacking detection. + + +0.313 + Fixed a few glitches left after switching file formats. + Improved memory usage when saving maps. + Fixed an updater-related crash. + + +0.312 + New map format (FCM v2) should provide faster loading times. + server_level.dat loader is now functional. + Fixed possible crash if the default port was not available. + Automatic updater should now work properly. More update options to come. + + +0.311 + Preview version of automatic updater. + + +0.310 + Cleaned up some VC# crapfiles. + Added class prefixes and colors. + Added PlaceWater and PlaceLava back to permissions. + Fixed rank-limited permissions. + Other assorted glitches fixed. + + +0.305 + removed config.xml from svn. + + +0.304 + More potential crashes fixed. + + +0.303 + Fixed a couple potential crashes in ConfigTool and Config.DefineClass + + +0.302 + Fixed a few minor ConfigTool bugs. + + +0.301 + Forgot to save config from World.cs after parsing. Minor update. + + +0.300 + ConfigTool finished. Note that some of the options do not have an effect yet. + +*version log was not kept before this point* diff --git a/ConfigGUI/AddWorldPopup.Designer.cs b/ConfigGUI/AddWorldPopup.Designer.cs new file mode 100644 index 0000000..0e7b092 --- /dev/null +++ b/ConfigGUI/AddWorldPopup.Designer.cs @@ -0,0 +1,2580 @@ +namespace fCraft.ConfigGUI { + partial class AddWorldPopup { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.lX2 = new System.Windows.Forms.Label(); + this.lX1 = new System.Windows.Forms.Label(); + this.lDim = new System.Windows.Forms.Label(); + this.nMapWidth = new System.Windows.Forms.NumericUpDown(); + this.nMapLength = new System.Windows.Forms.NumericUpDown(); + this.nMapHeight = new System.Windows.Forms.NumericUpDown(); + this.bShow = new System.Windows.Forms.Button(); + this.xFloodBarrier = new System.Windows.Forms.CheckBox(); + this.cTheme = new System.Windows.Forms.ComboBox(); + this.lTheme = new System.Windows.Forms.Label(); + this.bGenerate = new System.Windows.Forms.Button(); + this.cWorld = new System.Windows.Forms.ComboBox(); + this.tFile = new System.Windows.Forms.TextBox(); + this.bBrowseFile = new System.Windows.Forms.Button(); + this.lPreview = new System.Windows.Forms.Label(); + this.bOK = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.cBackup = new System.Windows.Forms.ComboBox(); + this.cAccess = new System.Windows.Forms.ComboBox(); + this.cBuild = new System.Windows.Forms.ComboBox(); + this.lName = new System.Windows.Forms.Label(); + this.lAccess = new System.Windows.Forms.Label(); + this.lBuild = new System.Windows.Forms.Label(); + this.lBackup = new System.Windows.Forms.Label(); + this.tName = new System.Windows.Forms.TextBox(); + this.bPreviewPrev = new System.Windows.Forms.Button(); + this.bPreviewNext = new System.Windows.Forms.Button(); + this.xHidden = new System.Windows.Forms.CheckBox(); + this.fileBrowser = new System.Windows.Forms.OpenFileDialog(); + this.statusStrip = new System.Windows.Forms.StatusStrip(); + this.progressBar = new System.Windows.Forms.ToolStripProgressBar(); + this.tStatus1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.tStatus2 = new System.Windows.Forms.ToolStripStatusLabel(); + this.previewLayout = new System.Windows.Forms.TableLayoutPanel(); + this.preview = new fCraft.ConfigGUI.CustomPictureBox(); + this.lDetailSize = new System.Windows.Forms.Label(); + this.sFeatureScale = new System.Windows.Forms.TrackBar(); + this.sRoughness = new System.Windows.Forms.TrackBar(); + this.lRoughness = new System.Windows.Forms.Label(); + this.xMarbledMode = new System.Windows.Forms.CheckBox(); + this.xLayeredHeightmap = new System.Windows.Forms.CheckBox(); + this.xMatchWaterCoverage = new System.Windows.Forms.CheckBox(); + this.sWaterCoverage = new System.Windows.Forms.TrackBar(); + this.lMatchWaterCoverageDisplay = new System.Windows.Forms.Label(); + this.lRoughnessDisplay = new System.Windows.Forms.Label(); + this.lFeatureSizeDisplay = new System.Windows.Forms.Label(); + this.lMaxHeight = new System.Windows.Forms.Label(); + this.lMaxHeightUnits = new System.Windows.Forms.Label(); + this.lMaxDepth = new System.Windows.Forms.Label(); + this.lMaxDepthUnits = new System.Windows.Forms.Label(); + this.lBias = new System.Windows.Forms.Label(); + this.sBias = new System.Windows.Forms.TrackBar(); + this.xAddTrees = new System.Windows.Forms.CheckBox(); + this.bSavePreview = new System.Windows.Forms.Button(); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabExisting = new System.Windows.Forms.TabPage(); + this.tExistingMapInfo = new System.Windows.Forms.TextBox(); + this.tabLoad = new System.Windows.Forms.TabPage(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.lFolder = new System.Windows.Forms.Label(); + this.tFolder = new System.Windows.Forms.TextBox(); + this.bBrowseFolder = new System.Windows.Forms.Button(); + this.lFormatList = new System.Windows.Forms.Label(); + this.lFile = new System.Windows.Forms.Label(); + this.tLoadFileInfo = new System.Windows.Forms.TextBox(); + this.tabCopy = new System.Windows.Forms.TabPage(); + this.tCopyInfo = new System.Windows.Forms.TextBox(); + this.lWorldToCopy = new System.Windows.Forms.Label(); + this.tabFlatgrass = new System.Windows.Forms.TabPage(); + this.bFlatgrassGenerate = new System.Windows.Forms.Button(); + this.nFlatgrassDimX = new System.Windows.Forms.NumericUpDown(); + this.lFlatgrassX1 = new System.Windows.Forms.Label(); + this.lFlatgrassDimensions = new System.Windows.Forms.Label(); + this.lFlatgrassX2 = new System.Windows.Forms.Label(); + this.nFlatgrassDimZ = new System.Windows.Forms.NumericUpDown(); + this.nFlatgrassDimY = new System.Windows.Forms.NumericUpDown(); + this.tabTerrain = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.gGenOptions = new System.Windows.Forms.GroupBox(); + this.xAddCliffs = new System.Windows.Forms.CheckBox(); + this.xAddRuins = new System.Windows.Forms.CheckBox(); + this.xOre = new System.Windows.Forms.CheckBox(); + this.xAddBeaches = new System.Windows.Forms.CheckBox(); + this.xAddSnow = new System.Windows.Forms.CheckBox(); + this.xWater = new System.Windows.Forms.CheckBox(); + this.xCaves = new System.Windows.Forms.CheckBox(); + this.gTemplates = new System.Windows.Forms.GroupBox(); + this.cTemplates = new System.Windows.Forms.ComboBox(); + this.lUseTemplate = new System.Windows.Forms.Label(); + this.bBrowseTemplate = new System.Windows.Forms.Button(); + this.bSaveTemplate = new System.Windows.Forms.Button(); + this.gMapSize = new System.Windows.Forms.GroupBox(); + this.nMaxDepthVariation = new System.Windows.Forms.NumericUpDown(); + this.nMaxHeightVariation = new System.Windows.Forms.NumericUpDown(); + this.lMaxHeightVariationUnits = new System.Windows.Forms.Label(); + this.lMaxDepthVariationUnits = new System.Windows.Forms.Label(); + this.xWaterLevel = new System.Windows.Forms.CheckBox(); + this.nWaterLevel = new System.Windows.Forms.NumericUpDown(); + this.lWaterLevelLabel = new System.Windows.Forms.Label(); + this.nMaxDepth = new System.Windows.Forms.NumericUpDown(); + this.nMaxHeight = new System.Windows.Forms.NumericUpDown(); + this.gTerrainFeatures = new System.Windows.Forms.GroupBox(); + this.lLoweredCorners = new System.Windows.Forms.Label(); + this.nLoweredCorners = new System.Windows.Forms.NumericUpDown(); + this.cMidpoint = new System.Windows.Forms.ComboBox(); + this.lMidpoint = new System.Windows.Forms.Label(); + this.lRaisedCorners = new System.Windows.Forms.Label(); + this.nRaisedCorners = new System.Windows.Forms.NumericUpDown(); + this.lBiasDisplay = new System.Windows.Forms.Label(); + this.gHeightmapCreation = new System.Windows.Forms.GroupBox(); + this.lBelowFunc = new System.Windows.Forms.Label(); + this.lAboveFunc = new System.Windows.Forms.Label(); + this.lBelowFuncUnits = new System.Windows.Forms.Label(); + this.lAboveFuncUnits = new System.Windows.Forms.Label(); + this.sAboveFunc = new System.Windows.Forms.TrackBar(); + this.sBelowFunc = new System.Windows.Forms.TrackBar(); + this.xDelayBias = new System.Windows.Forms.CheckBox(); + this.xInvert = new System.Windows.Forms.CheckBox(); + this.sDetailScale = new System.Windows.Forms.TrackBar(); + this.lDetailSizeDisplay = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.gCaves = new System.Windows.Forms.GroupBox(); + this.lCaveSizeDisplay = new System.Windows.Forms.Label(); + this.lCaveDensityDisplay = new System.Windows.Forms.Label(); + this.xCaveLava = new System.Windows.Forms.CheckBox(); + this.xCaveWater = new System.Windows.Forms.CheckBox(); + this.sCaveSize = new System.Windows.Forms.TrackBar(); + this.lCaveSize = new System.Windows.Forms.Label(); + this.sCaveDensity = new System.Windows.Forms.TrackBar(); + this.lCaveDensity = new System.Windows.Forms.Label(); + this.gTrees = new System.Windows.Forms.GroupBox(); + this.xGiantTrees = new System.Windows.Forms.CheckBox(); + this.lTreeHeightUnits = new System.Windows.Forms.Label(); + this.nTreeHeightVariation = new System.Windows.Forms.NumericUpDown(); + this.lTreeHeightVariation = new System.Windows.Forms.Label(); + this.nTreeHeight = new System.Windows.Forms.NumericUpDown(); + this.lTreeHeight = new System.Windows.Forms.Label(); + this.lTreeSpacingUnits = new System.Windows.Forms.Label(); + this.nTreeSpacingVariation = new System.Windows.Forms.NumericUpDown(); + this.lTreeSpacingVariation = new System.Windows.Forms.Label(); + this.nTreeSpacing = new System.Windows.Forms.NumericUpDown(); + this.lTreeSpacing = new System.Windows.Forms.Label(); + this.gSnow = new System.Windows.Forms.GroupBox(); + this.lSnowTransitionUnits = new System.Windows.Forms.Label(); + this.lSnowTransition = new System.Windows.Forms.Label(); + this.lSnowAltitudeUnits = new System.Windows.Forms.Label(); + this.nSnowTransition = new System.Windows.Forms.NumericUpDown(); + this.nSnowAltitude = new System.Windows.Forms.NumericUpDown(); + this.lSnowAltitude = new System.Windows.Forms.Label(); + this.gCliffs = new System.Windows.Forms.GroupBox(); + this.xCliffSmoothing = new System.Windows.Forms.CheckBox(); + this.lCliffThresholdUnits = new System.Windows.Forms.Label(); + this.sCliffThreshold = new System.Windows.Forms.TrackBar(); + this.lCliffThreshold = new System.Windows.Forms.Label(); + this.gBeaches = new System.Windows.Forms.GroupBox(); + this.lBeachHeight = new System.Windows.Forms.Label(); + this.lBeachExtentUnits = new System.Windows.Forms.Label(); + this.lBeachHeightUnits = new System.Windows.Forms.Label(); + this.nBeachHeight = new System.Windows.Forms.NumericUpDown(); + this.nBeachExtent = new System.Windows.Forms.NumericUpDown(); + this.lBeachExtent = new System.Windows.Forms.Label(); + this.xSeed = new System.Windows.Forms.CheckBox(); + this.nSeed = new System.Windows.Forms.NumericUpDown(); + this.xAdvanced = new System.Windows.Forms.CheckBox(); + this.lMapFileOptions = new System.Windows.Forms.Label(); + this.lCreateMap = new System.Windows.Forms.Label(); + this.folderBrowser = new System.Windows.Forms.FolderBrowserDialog(); + this.xBlockDB = new System.Windows.Forms.CheckBox(); + ((System.ComponentModel.ISupportInitialize)(this.nMapWidth)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMapLength)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMapHeight)).BeginInit(); + this.statusStrip.SuspendLayout(); + this.previewLayout.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.preview)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sFeatureScale)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sRoughness)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sWaterCoverage)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sBias)).BeginInit(); + this.tabs.SuspendLayout(); + this.tabExisting.SuspendLayout(); + this.tabLoad.SuspendLayout(); + this.tabCopy.SuspendLayout(); + this.tabFlatgrass.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimX)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimZ)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimY)).BeginInit(); + this.tabTerrain.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.gGenOptions.SuspendLayout(); + this.gTemplates.SuspendLayout(); + this.gMapSize.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxDepthVariation)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxHeightVariation)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nWaterLevel)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxDepth)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxHeight)).BeginInit(); + this.gTerrainFeatures.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nLoweredCorners)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nRaisedCorners)).BeginInit(); + this.gHeightmapCreation.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sAboveFunc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sBelowFunc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sDetailScale)).BeginInit(); + this.gCaves.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sCaveSize)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.sCaveDensity)).BeginInit(); + this.gTrees.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeHeightVariation)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeHeight)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeSpacingVariation)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeSpacing)).BeginInit(); + this.gSnow.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nSnowTransition)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSnowAltitude)).BeginInit(); + this.gCliffs.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sCliffThreshold)).BeginInit(); + this.gBeaches.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nBeachHeight)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nBeachExtent)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSeed)).BeginInit(); + this.SuspendLayout(); + // + // lX2 + // + this.lX2.AutoSize = true; + this.lX2.Location = new System.Drawing.Point( 219, 21 ); + this.lX2.Name = "lX2"; + this.lX2.Size = new System.Drawing.Size( 13, 13 ); + this.lX2.TabIndex = 6; + this.lX2.Text = "×"; + // + // lX1 + // + this.lX1.AutoSize = true; + this.lX1.Location = new System.Drawing.Point( 140, 21 ); + this.lX1.Name = "lX1"; + this.lX1.Size = new System.Drawing.Size( 13, 13 ); + this.lX1.TabIndex = 5; + this.lX1.Text = "×"; + // + // lDim + // + this.lDim.AutoSize = true; + this.lDim.Location = new System.Drawing.Point( 13, 21 ); + this.lDim.Name = "lDim"; + this.lDim.Size = new System.Drawing.Size( 61, 13 ); + this.lDim.TabIndex = 3; + this.lDim.Text = "Dimensions"; + // + // nMapWidth + // + this.nMapWidth.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapWidth.Location = new System.Drawing.Point( 80, 19 ); + this.nMapWidth.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMapWidth.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapWidth.Name = "nMapWidth"; + this.nMapWidth.Size = new System.Drawing.Size( 54, 20 ); + this.nMapWidth.TabIndex = 0; + this.nMapWidth.Value = new decimal( new int[] { + 128, + 0, + 0, + 0} ); + this.nMapWidth.ValueChanged += new System.EventHandler( this.MapDimensionChanged ); + // + // nMapLength + // + this.nMapLength.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapLength.Location = new System.Drawing.Point( 159, 19 ); + this.nMapLength.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMapLength.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapLength.Name = "nMapLength"; + this.nMapLength.Size = new System.Drawing.Size( 54, 20 ); + this.nMapLength.TabIndex = 1; + this.nMapLength.Value = new decimal( new int[] { + 128, + 0, + 0, + 0} ); + this.nMapLength.ValueChanged += new System.EventHandler( this.MapDimensionChanged ); + // + // nMapHeight + // + this.nMapHeight.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapHeight.Location = new System.Drawing.Point( 238, 19 ); + this.nMapHeight.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMapHeight.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nMapHeight.Name = "nMapHeight"; + this.nMapHeight.Size = new System.Drawing.Size( 54, 20 ); + this.nMapHeight.TabIndex = 2; + this.nMapHeight.Value = new decimal( new int[] { + 64, + 0, + 0, + 0} ); + this.nMapHeight.ValueChanged += new System.EventHandler( this.nHeight_ValueChanged ); + // + // bShow + // + this.bShow.Location = new System.Drawing.Point( 305, 6 ); + this.bShow.Name = "bShow"; + this.bShow.Size = new System.Drawing.Size( 74, 23 ); + this.bShow.TabIndex = 2; + this.bShow.Text = "Show"; + this.bShow.UseVisualStyleBackColor = true; + this.bShow.Click += new System.EventHandler( this.bShow_Click ); + // + // xFloodBarrier + // + this.xFloodBarrier.AutoSize = true; + this.xFloodBarrier.Location = new System.Drawing.Point( 254, 65 ); + this.xFloodBarrier.Name = "xFloodBarrier"; + this.xFloodBarrier.Size = new System.Drawing.Size( 84, 17 ); + this.xFloodBarrier.TabIndex = 4; + this.xFloodBarrier.Text = "Flood barrier"; + this.xFloodBarrier.UseVisualStyleBackColor = true; + this.xFloodBarrier.CheckedChanged += new System.EventHandler( this.xFloodBarrier_CheckedChanged ); + // + // cTheme + // + this.cTheme.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cTheme.Location = new System.Drawing.Point( 281, 6 ); + this.cTheme.Name = "cTheme"; + this.cTheme.Size = new System.Drawing.Size( 87, 21 ); + this.cTheme.TabIndex = 3; + // + // lTheme + // + this.lTheme.AutoSize = true; + this.lTheme.Location = new System.Drawing.Point( 235, 9 ); + this.lTheme.Name = "lTheme"; + this.lTheme.Size = new System.Drawing.Size( 40, 13 ); + this.lTheme.TabIndex = 19; + this.lTheme.Text = "Theme"; + // + // bGenerate + // + this.bGenerate.Font = new System.Drawing.Font( "Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bGenerate.Location = new System.Drawing.Point( 6, 6 ); + this.bGenerate.Name = "bGenerate"; + this.bGenerate.Size = new System.Drawing.Size( 95, 47 ); + this.bGenerate.TabIndex = 0; + this.bGenerate.Text = "Generate"; + this.bGenerate.UseVisualStyleBackColor = true; + this.bGenerate.Click += new System.EventHandler( this.bGenerate_Click ); + // + // cWorld + // + this.cWorld.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cWorld.FormattingEnabled = true; + this.cWorld.Location = new System.Drawing.Point( 85, 7 ); + this.cWorld.Name = "cWorld"; + this.cWorld.Size = new System.Drawing.Size( 214, 21 ); + this.cWorld.TabIndex = 1; + this.cWorld.SelectedIndexChanged += new System.EventHandler( this.cWorld_SelectedIndexChanged ); + // + // tFile + // + this.tFile.Location = new System.Drawing.Point( 72, 87 ); + this.tFile.Name = "tFile"; + this.tFile.ReadOnly = true; + this.tFile.Size = new System.Drawing.Size( 233, 20 ); + this.tFile.TabIndex = 3; + // + // bBrowseFile + // + this.bBrowseFile.Location = new System.Drawing.Point( 311, 85 ); + this.bBrowseFile.Name = "bBrowseFile"; + this.bBrowseFile.Size = new System.Drawing.Size( 74, 23 ); + this.bBrowseFile.TabIndex = 4; + this.bBrowseFile.Text = "Browse"; + this.bBrowseFile.UseVisualStyleBackColor = true; + this.bBrowseFile.Click += new System.EventHandler( this.bBrowseFile_Click ); + // + // lPreview + // + this.lPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lPreview.AutoSize = true; + this.lPreview.Location = new System.Drawing.Point( 250, 487 ); + this.lPreview.Name = "lPreview"; + this.lPreview.Size = new System.Drawing.Size( 54, 28 ); + this.lPreview.TabIndex = 1; + this.lPreview.Text = "Preview"; + this.lPreview.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // bOK + // + this.bOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.bOK.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bOK.Location = new System.Drawing.Point( 766, 533 ); + this.bOK.Name = "bOK"; + this.bOK.Size = new System.Drawing.Size( 100, 25 ); + this.bOK.TabIndex = 16; + this.bOK.Text = "OK"; + this.bOK.UseVisualStyleBackColor = true; + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Location = new System.Drawing.Point( 872, 533 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 25 ); + this.bCancel.TabIndex = 17; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + // + // cBackup + // + this.cBackup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cBackup.FormattingEnabled = true; + this.cBackup.Location = new System.Drawing.Point( 293, 12 ); + this.cBackup.Name = "cBackup"; + this.cBackup.Size = new System.Drawing.Size( 93, 21 ); + this.cBackup.TabIndex = 7; + this.cBackup.SelectedIndexChanged += new System.EventHandler( this.cBackup_SelectedIndexChanged ); + // + // cAccess + // + this.cAccess.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cAccess.FormattingEnabled = true; + this.cAccess.Location = new System.Drawing.Point( 113, 38 ); + this.cAccess.Name = "cAccess"; + this.cAccess.Size = new System.Drawing.Size( 113, 21 ); + this.cAccess.TabIndex = 3; + this.cAccess.SelectedIndexChanged += new System.EventHandler( this.cAccess_SelectedIndexChanged ); + // + // cBuild + // + this.cBuild.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cBuild.FormattingEnabled = true; + this.cBuild.Location = new System.Drawing.Point( 113, 65 ); + this.cBuild.Name = "cBuild"; + this.cBuild.Size = new System.Drawing.Size( 113, 21 ); + this.cBuild.TabIndex = 5; + this.cBuild.SelectedIndexChanged += new System.EventHandler( this.cBuild_SelectedIndexChanged ); + // + // lName + // + this.lName.AutoSize = true; + this.lName.Location = new System.Drawing.Point( 43, 15 ); + this.lName.Name = "lName"; + this.lName.Size = new System.Drawing.Size( 64, 13 ); + this.lName.TabIndex = 0; + this.lName.Text = "World name"; + // + // lAccess + // + this.lAccess.AutoSize = true; + this.lAccess.Location = new System.Drawing.Point( 13, 41 ); + this.lAccess.Name = "lAccess"; + this.lAccess.Size = new System.Drawing.Size( 94, 13 ); + this.lAccess.TabIndex = 2; + this.lAccess.Text = "Access permission"; + // + // lBuild + // + this.lBuild.AutoSize = true; + this.lBuild.Location = new System.Drawing.Point( 25, 68 ); + this.lBuild.Name = "lBuild"; + this.lBuild.Size = new System.Drawing.Size( 82, 13 ); + this.lBuild.TabIndex = 4; + this.lBuild.Text = "Build permission"; + // + // lBackup + // + this.lBackup.AutoSize = true; + this.lBackup.Location = new System.Drawing.Point( 243, 15 ); + this.lBackup.Name = "lBackup"; + this.lBackup.Size = new System.Drawing.Size( 44, 13 ); + this.lBackup.TabIndex = 6; + this.lBackup.Text = "Backup"; + // + // tName + // + this.tName.Location = new System.Drawing.Point( 113, 12 ); + this.tName.Name = "tName"; + this.tName.Size = new System.Drawing.Size( 113, 20 ); + this.tName.TabIndex = 1; + this.tName.Validated += new System.EventHandler( this.tName_Validated ); + this.tName.Validating += new System.ComponentModel.CancelEventHandler( this.tName_Validating ); + // + // bPreviewPrev + // + this.bPreviewPrev.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bPreviewPrev.Location = new System.Drawing.Point( 222, 490 ); + this.bPreviewPrev.Name = "bPreviewPrev"; + this.bPreviewPrev.Size = new System.Drawing.Size( 22, 22 ); + this.bPreviewPrev.TabIndex = 0; + this.bPreviewPrev.Text = "<"; + this.bPreviewPrev.UseVisualStyleBackColor = true; + this.bPreviewPrev.Click += new System.EventHandler( this.bPreviewPrev_Click ); + // + // bPreviewNext + // + this.bPreviewNext.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bPreviewNext.Location = new System.Drawing.Point( 310, 490 ); + this.bPreviewNext.Name = "bPreviewNext"; + this.bPreviewNext.Size = new System.Drawing.Size( 22, 22 ); + this.bPreviewNext.TabIndex = 2; + this.bPreviewNext.Text = ">"; + this.bPreviewNext.UseVisualStyleBackColor = true; + this.bPreviewNext.Click += new System.EventHandler( this.bPreviewNext_Click ); + // + // xHidden + // + this.xHidden.AutoSize = true; + this.xHidden.Location = new System.Drawing.Point( 246, 40 ); + this.xHidden.Name = "xHidden"; + this.xHidden.Size = new System.Drawing.Size( 132, 17 ); + this.xHidden.TabIndex = 8; + this.xHidden.Text = "Hide from the world list"; + this.xHidden.UseVisualStyleBackColor = true; + this.xHidden.CheckedChanged += new System.EventHandler( this.xHidden_CheckedChanged ); + // + // statusStrip + // + this.statusStrip.Items.AddRange( new System.Windows.Forms.ToolStripItem[] { + this.progressBar, + this.tStatus1, + this.tStatus2} ); + this.statusStrip.Location = new System.Drawing.Point( 0, 561 ); + this.statusStrip.Name = "statusStrip"; + this.statusStrip.Size = new System.Drawing.Size( 984, 22 ); + this.statusStrip.TabIndex = 13; + this.statusStrip.Text = "statusStrip1"; + // + // progressBar + // + this.progressBar.Name = "progressBar"; + this.progressBar.Size = new System.Drawing.Size( 100, 16 ); + this.progressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.progressBar.Visible = false; + // + // tStatus1 + // + this.tStatus1.Name = "tStatus1"; + this.tStatus1.Size = new System.Drawing.Size( 44, 17 ); + this.tStatus1.Text = "status1"; + // + // tStatus2 + // + this.tStatus2.Name = "tStatus2"; + this.tStatus2.Size = new System.Drawing.Size( 44, 17 ); + this.tStatus2.Text = "status2"; + // + // previewLayout + // + this.previewLayout.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.previewLayout.ColumnCount = 3; + this.previewLayout.ColumnStyles.Add( new System.Windows.Forms.ColumnStyle( System.Windows.Forms.SizeType.Percent, 50F ) ); + this.previewLayout.ColumnStyles.Add( new System.Windows.Forms.ColumnStyle( System.Windows.Forms.SizeType.Absolute, 60F ) ); + this.previewLayout.ColumnStyles.Add( new System.Windows.Forms.ColumnStyle( System.Windows.Forms.SizeType.Percent, 50F ) ); + this.previewLayout.Controls.Add( this.bPreviewPrev, 0, 1 ); + this.previewLayout.Controls.Add( this.bPreviewNext, 2, 1 ); + this.previewLayout.Controls.Add( this.lPreview, 1, 1 ); + this.previewLayout.Controls.Add( this.preview, 0, 0 ); + this.previewLayout.Location = new System.Drawing.Point( 417, 12 ); + this.previewLayout.Name = "previewLayout"; + this.previewLayout.RowCount = 2; + this.previewLayout.RowStyles.Add( new System.Windows.Forms.RowStyle( System.Windows.Forms.SizeType.Percent, 100F ) ); + this.previewLayout.RowStyles.Add( new System.Windows.Forms.RowStyle( System.Windows.Forms.SizeType.Absolute, 28F ) ); + this.previewLayout.Size = new System.Drawing.Size( 555, 515 ); + this.previewLayout.TabIndex = 12; + // + // preview + // + this.preview.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.preview.BackColor = System.Drawing.Color.Black; + this.previewLayout.SetColumnSpan( this.preview, 3 ); + this.preview.Location = new System.Drawing.Point( 3, 3 ); + this.preview.Name = "preview"; + this.preview.Size = new System.Drawing.Size( 549, 481 ); + this.preview.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.preview.TabIndex = 17; + this.preview.TabStop = false; + // + // lDetailSize + // + this.lDetailSize.AutoSize = true; + this.lDetailSize.Location = new System.Drawing.Point( 21, 22 ); + this.lDetailSize.Name = "lDetailSize"; + this.lDetailSize.Size = new System.Drawing.Size( 71, 13 ); + this.lDetailSize.TabIndex = 23; + this.lDetailSize.Text = "Feature scale"; + // + // sFeatureScale + // + this.sFeatureScale.AutoSize = false; + this.sFeatureScale.Location = new System.Drawing.Point( 98, 19 ); + this.sFeatureScale.Maximum = 7; + this.sFeatureScale.Minimum = -1; + this.sFeatureScale.Name = "sFeatureScale"; + this.sFeatureScale.Size = new System.Drawing.Size( 116, 27 ); + this.sFeatureScale.TabIndex = 0; + this.sFeatureScale.Value = 2; + this.sFeatureScale.ValueChanged += new System.EventHandler( this.sFeatureSize_ValueChanged ); + // + // sRoughness + // + this.sRoughness.AutoSize = false; + this.sRoughness.Location = new System.Drawing.Point( 98, 85 ); + this.sRoughness.Maximum = 80; + this.sRoughness.Minimum = 20; + this.sRoughness.Name = "sRoughness"; + this.sRoughness.Size = new System.Drawing.Size( 116, 27 ); + this.sRoughness.TabIndex = 4; + this.sRoughness.TickFrequency = 10; + this.sRoughness.Value = 50; + this.sRoughness.ValueChanged += new System.EventHandler( this.sRoughness_ValueChanged ); + // + // lRoughness + // + this.lRoughness.AutoSize = true; + this.lRoughness.Location = new System.Drawing.Point( 31, 87 ); + this.lRoughness.Name = "lRoughness"; + this.lRoughness.Size = new System.Drawing.Size( 61, 13 ); + this.lRoughness.TabIndex = 25; + this.lRoughness.Text = "Roughness"; + // + // xMarbledMode + // + this.xMarbledMode.AutoSize = true; + this.xMarbledMode.Location = new System.Drawing.Point( 281, 18 ); + this.xMarbledMode.Name = "xMarbledMode"; + this.xMarbledMode.Size = new System.Drawing.Size( 64, 17 ); + this.xMarbledMode.TabIndex = 2; + this.xMarbledMode.Text = "Marbled"; + this.xMarbledMode.UseVisualStyleBackColor = true; + // + // xLayeredHeightmap + // + this.xLayeredHeightmap.AutoSize = true; + this.xLayeredHeightmap.Location = new System.Drawing.Point( 281, 41 ); + this.xLayeredHeightmap.Name = "xLayeredHeightmap"; + this.xLayeredHeightmap.Size = new System.Drawing.Size( 48, 17 ); + this.xLayeredHeightmap.TabIndex = 3; + this.xLayeredHeightmap.Text = "Cliffs"; + this.xLayeredHeightmap.UseVisualStyleBackColor = true; + // + // xMatchWaterCoverage + // + this.xMatchWaterCoverage.AutoSize = true; + this.xMatchWaterCoverage.Location = new System.Drawing.Point( 6, 125 ); + this.xMatchWaterCoverage.Name = "xMatchWaterCoverage"; + this.xMatchWaterCoverage.Size = new System.Drawing.Size( 133, 17 ); + this.xMatchWaterCoverage.TabIndex = 5; + this.xMatchWaterCoverage.Text = "Match water coverage"; + this.xMatchWaterCoverage.UseVisualStyleBackColor = true; + this.xMatchWaterCoverage.CheckedChanged += new System.EventHandler( this.xMatchWaterCoverage_CheckedChanged ); + // + // sWaterCoverage + // + this.sWaterCoverage.AutoSize = false; + this.sWaterCoverage.Enabled = false; + this.sWaterCoverage.Location = new System.Drawing.Point( 145, 124 ); + this.sWaterCoverage.Maximum = 100; + this.sWaterCoverage.Name = "sWaterCoverage"; + this.sWaterCoverage.Size = new System.Drawing.Size( 163, 27 ); + this.sWaterCoverage.TabIndex = 6; + this.sWaterCoverage.TickFrequency = 10; + this.sWaterCoverage.Value = 50; + this.sWaterCoverage.ValueChanged += new System.EventHandler( this.sWaterCoverage_ValueChanged ); + // + // lMatchWaterCoverageDisplay + // + this.lMatchWaterCoverageDisplay.AutoSize = true; + this.lMatchWaterCoverageDisplay.Location = new System.Drawing.Point( 314, 129 ); + this.lMatchWaterCoverageDisplay.Name = "lMatchWaterCoverageDisplay"; + this.lMatchWaterCoverageDisplay.Size = new System.Drawing.Size( 27, 13 ); + this.lMatchWaterCoverageDisplay.TabIndex = 33; + this.lMatchWaterCoverageDisplay.Text = "50%"; + // + // lRoughnessDisplay + // + this.lRoughnessDisplay.AutoSize = true; + this.lRoughnessDisplay.Location = new System.Drawing.Point( 216, 87 ); + this.lRoughnessDisplay.Name = "lRoughnessDisplay"; + this.lRoughnessDisplay.Size = new System.Drawing.Size( 27, 13 ); + this.lRoughnessDisplay.TabIndex = 34; + this.lRoughnessDisplay.Text = "50%"; + // + // lFeatureSizeDisplay + // + this.lFeatureSizeDisplay.AutoSize = true; + this.lFeatureSizeDisplay.Location = new System.Drawing.Point( 215, 22 ); + this.lFeatureSizeDisplay.Name = "lFeatureSizeDisplay"; + this.lFeatureSizeDisplay.Size = new System.Drawing.Size( 25, 13 ); + this.lFeatureSizeDisplay.TabIndex = 35; + this.lFeatureSizeDisplay.Text = "1×1"; + // + // lMaxHeight + // + this.lMaxHeight.AutoSize = true; + this.lMaxHeight.Location = new System.Drawing.Point( 10, 54 ); + this.lMaxHeight.Name = "lMaxHeight"; + this.lMaxHeight.Size = new System.Drawing.Size( 64, 13 ); + this.lMaxHeight.TabIndex = 40; + this.lMaxHeight.Text = "Peak height"; + // + // lMaxHeightUnits + // + this.lMaxHeightUnits.AutoSize = true; + this.lMaxHeightUnits.Location = new System.Drawing.Point( 140, 54 ); + this.lMaxHeightUnits.Name = "lMaxHeightUnits"; + this.lMaxHeightUnits.Size = new System.Drawing.Size( 21, 13 ); + this.lMaxHeightUnits.TabIndex = 41; + this.lMaxHeightUnits.Text = "+/-"; + // + // lMaxDepth + // + this.lMaxDepth.AutoSize = true; + this.lMaxDepth.Location = new System.Drawing.Point( 17, 80 ); + this.lMaxDepth.Name = "lMaxDepth"; + this.lMaxDepth.Size = new System.Drawing.Size( 57, 13 ); + this.lMaxDepth.TabIndex = 42; + this.lMaxDepth.Text = "Max depth"; + // + // lMaxDepthUnits + // + this.lMaxDepthUnits.AutoSize = true; + this.lMaxDepthUnits.Location = new System.Drawing.Point( 140, 80 ); + this.lMaxDepthUnits.Name = "lMaxDepthUnits"; + this.lMaxDepthUnits.Size = new System.Drawing.Size( 21, 13 ); + this.lMaxDepthUnits.TabIndex = 44; + this.lMaxDepthUnits.Text = "+/-"; + // + // lBias + // + this.lBias.AutoSize = true; + this.lBias.Location = new System.Drawing.Point( 26, 21 ); + this.lBias.Name = "lBias"; + this.lBias.Size = new System.Drawing.Size( 27, 13 ); + this.lBias.TabIndex = 50; + this.lBias.Text = "Bias"; + // + // sBias + // + this.sBias.AutoSize = false; + this.sBias.LargeChange = 10; + this.sBias.Location = new System.Drawing.Point( 59, 19 ); + this.sBias.Maximum = 100; + this.sBias.Name = "sBias"; + this.sBias.Size = new System.Drawing.Size( 116, 27 ); + this.sBias.TabIndex = 0; + this.sBias.TickFrequency = 20; + this.sBias.ValueChanged += new System.EventHandler( this.sBias_ValueChanged ); + // + // xAddTrees + // + this.xAddTrees.AutoSize = true; + this.xAddTrees.Checked = true; + this.xAddTrees.CheckState = System.Windows.Forms.CheckState.Checked; + this.xAddTrees.Location = new System.Drawing.Point( 13, 19 ); + this.xAddTrees.Name = "xAddTrees"; + this.xAddTrees.Size = new System.Drawing.Size( 53, 17 ); + this.xAddTrees.TabIndex = 5; + this.xAddTrees.Text = "Trees"; + this.xAddTrees.UseVisualStyleBackColor = true; + this.xAddTrees.CheckedChanged += new System.EventHandler( this.xAddTrees_CheckedChanged ); + // + // bSavePreview + // + this.bSavePreview.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bSavePreview.Enabled = false; + this.bSavePreview.Location = new System.Drawing.Point( 417, 533 ); + this.bSavePreview.Name = "bSavePreview"; + this.bSavePreview.Size = new System.Drawing.Size( 125, 25 ); + this.bSavePreview.TabIndex = 14; + this.bSavePreview.Text = "Save Preview Image..."; + this.bSavePreview.UseVisualStyleBackColor = true; + this.bSavePreview.Click += new System.EventHandler( this.bSavePreview_Click ); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.tabs.Controls.Add( this.tabExisting ); + this.tabs.Controls.Add( this.tabLoad ); + this.tabs.Controls.Add( this.tabCopy ); + this.tabs.Controls.Add( this.tabFlatgrass ); + this.tabs.Controls.Add( this.tabTerrain ); + this.tabs.Location = new System.Drawing.Point( 12, 110 ); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size( 399, 448 ); + this.tabs.TabIndex = 11; + this.tabs.SelectedIndexChanged += new System.EventHandler( this.tabs_SelectedIndexChanged ); + // + // tabExisting + // + this.tabExisting.Controls.Add( this.tExistingMapInfo ); + this.tabExisting.Location = new System.Drawing.Point( 4, 22 ); + this.tabExisting.Name = "tabExisting"; + this.tabExisting.Padding = new System.Windows.Forms.Padding( 3 ); + this.tabExisting.Size = new System.Drawing.Size( 391, 422 ); + this.tabExisting.TabIndex = 0; + this.tabExisting.Text = "Existing Map"; + this.tabExisting.UseVisualStyleBackColor = true; + // + // tExistingMapInfo + // + this.tExistingMapInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tExistingMapInfo.Font = new System.Drawing.Font( "Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tExistingMapInfo.Location = new System.Drawing.Point( 6, 6 ); + this.tExistingMapInfo.Multiline = true; + this.tExistingMapInfo.Name = "tExistingMapInfo"; + this.tExistingMapInfo.ReadOnly = true; + this.tExistingMapInfo.Size = new System.Drawing.Size( 373, 410 ); + this.tExistingMapInfo.TabIndex = 0; + this.tExistingMapInfo.TabStop = false; + // + // tabLoad + // + this.tabLoad.Controls.Add( this.label3 ); + this.tabLoad.Controls.Add( this.label2 ); + this.tabLoad.Controls.Add( this.lFolder ); + this.tabLoad.Controls.Add( this.tFolder ); + this.tabLoad.Controls.Add( this.bBrowseFolder ); + this.tabLoad.Controls.Add( this.lFormatList ); + this.tabLoad.Controls.Add( this.lFile ); + this.tabLoad.Controls.Add( this.tLoadFileInfo ); + this.tabLoad.Controls.Add( this.tFile ); + this.tabLoad.Controls.Add( this.bBrowseFile ); + this.tabLoad.Location = new System.Drawing.Point( 4, 22 ); + this.tabLoad.Name = "tabLoad"; + this.tabLoad.Padding = new System.Windows.Forms.Padding( 3 ); + this.tabLoad.Size = new System.Drawing.Size( 391, 422 ); + this.tabLoad.TabIndex = 1; + this.tabLoad.Text = "Load File"; + this.tabLoad.UseVisualStyleBackColor = true; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point( 211, 3 ); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size( 151, 65 ); + this.label3.TabIndex = 1; + this.label3.Text = "\r\n- MinerCPP and LuaCraft (.dat)\r\n- D3 (.map)\r\n- JTE\'s (.gz)\r\n- OptiCraft (.save)" + + ""; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point( 6, 126 ); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size( 173, 26 ); + this.label2.TabIndex = 5; + this.label2.Text = "Supported folder formats:\r\n- Myne, MyneCraft, Hyvebuilt, iCraft"; + // + // lFolder + // + this.lFolder.AutoSize = true; + this.lFolder.Location = new System.Drawing.Point( 6, 162 ); + this.lFolder.Name = "lFolder"; + this.lFolder.Size = new System.Drawing.Size( 60, 13 ); + this.lFolder.TabIndex = 6; + this.lFolder.Text = "Load folder"; + // + // tFolder + // + this.tFolder.Location = new System.Drawing.Point( 72, 159 ); + this.tFolder.Name = "tFolder"; + this.tFolder.ReadOnly = true; + this.tFolder.Size = new System.Drawing.Size( 233, 20 ); + this.tFolder.TabIndex = 7; + // + // bBrowseFolder + // + this.bBrowseFolder.Location = new System.Drawing.Point( 311, 156 ); + this.bBrowseFolder.Name = "bBrowseFolder"; + this.bBrowseFolder.Size = new System.Drawing.Size( 74, 23 ); + this.bBrowseFolder.TabIndex = 8; + this.bBrowseFolder.Text = "Browse"; + this.bBrowseFolder.UseVisualStyleBackColor = true; + this.bBrowseFolder.Click += new System.EventHandler( this.bBrowseFolder_Click ); + // + // lFormatList + // + this.lFormatList.AutoSize = true; + this.lFormatList.Location = new System.Drawing.Point( 6, 3 ); + this.lFormatList.Name = "lFormatList"; + this.lFormatList.Size = new System.Drawing.Size( 144, 78 ); + this.lFormatList.TabIndex = 0; + this.lFormatList.Text = "Supported file formats:\r\n- fCraft and SpaceCraft (.fcm)\r\n- MCSharp and MCZall (.l" + + "vl)\r\n- Creative (original .dat)\r\n- Survival Test (.mine)\r\n- Survival Indev (.mcl" + + "evel)"; + // + // lFile + // + this.lFile.AutoSize = true; + this.lFile.Location = new System.Drawing.Point( 6, 90 ); + this.lFile.Name = "lFile"; + this.lFile.Size = new System.Drawing.Size( 47, 13 ); + this.lFile.TabIndex = 2; + this.lFile.Text = "Load file"; + // + // tLoadFileInfo + // + this.tLoadFileInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tLoadFileInfo.Font = new System.Drawing.Font( "Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tLoadFileInfo.Location = new System.Drawing.Point( 3, 185 ); + this.tLoadFileInfo.Multiline = true; + this.tLoadFileInfo.Name = "tLoadFileInfo"; + this.tLoadFileInfo.ReadOnly = true; + this.tLoadFileInfo.Size = new System.Drawing.Size( 379, 233 ); + this.tLoadFileInfo.TabIndex = 9; + this.tLoadFileInfo.TabStop = false; + // + // tabCopy + // + this.tabCopy.Controls.Add( this.tCopyInfo ); + this.tabCopy.Controls.Add( this.lWorldToCopy ); + this.tabCopy.Controls.Add( this.bShow ); + this.tabCopy.Controls.Add( this.cWorld ); + this.tabCopy.Location = new System.Drawing.Point( 4, 22 ); + this.tabCopy.Name = "tabCopy"; + this.tabCopy.Padding = new System.Windows.Forms.Padding( 3 ); + this.tabCopy.Size = new System.Drawing.Size( 391, 422 ); + this.tabCopy.TabIndex = 2; + this.tabCopy.Text = "Copy World"; + this.tabCopy.UseVisualStyleBackColor = true; + // + // tCopyInfo + // + this.tCopyInfo.Font = new System.Drawing.Font( "Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tCopyInfo.Location = new System.Drawing.Point( 6, 34 ); + this.tCopyInfo.Multiline = true; + this.tCopyInfo.Name = "tCopyInfo"; + this.tCopyInfo.ReadOnly = true; + this.tCopyInfo.Size = new System.Drawing.Size( 373, 100 ); + this.tCopyInfo.TabIndex = 3; + // + // lWorldToCopy + // + this.lWorldToCopy.AutoSize = true; + this.lWorldToCopy.Location = new System.Drawing.Point( 6, 11 ); + this.lWorldToCopy.Name = "lWorldToCopy"; + this.lWorldToCopy.Size = new System.Drawing.Size( 73, 13 ); + this.lWorldToCopy.TabIndex = 0; + this.lWorldToCopy.Text = "World to copy"; + // + // tabFlatgrass + // + this.tabFlatgrass.Controls.Add( this.bFlatgrassGenerate ); + this.tabFlatgrass.Controls.Add( this.nFlatgrassDimX ); + this.tabFlatgrass.Controls.Add( this.lFlatgrassX1 ); + this.tabFlatgrass.Controls.Add( this.lFlatgrassDimensions ); + this.tabFlatgrass.Controls.Add( this.lFlatgrassX2 ); + this.tabFlatgrass.Controls.Add( this.nFlatgrassDimZ ); + this.tabFlatgrass.Controls.Add( this.nFlatgrassDimY ); + this.tabFlatgrass.Location = new System.Drawing.Point( 4, 22 ); + this.tabFlatgrass.Name = "tabFlatgrass"; + this.tabFlatgrass.Padding = new System.Windows.Forms.Padding( 3 ); + this.tabFlatgrass.Size = new System.Drawing.Size( 391, 422 ); + this.tabFlatgrass.TabIndex = 3; + this.tabFlatgrass.Text = "Flatgrass"; + this.tabFlatgrass.UseVisualStyleBackColor = true; + // + // bFlatgrassGenerate + // + this.bFlatgrassGenerate.Location = new System.Drawing.Point( 6, 6 ); + this.bFlatgrassGenerate.Name = "bFlatgrassGenerate"; + this.bFlatgrassGenerate.Size = new System.Drawing.Size( 74, 50 ); + this.bFlatgrassGenerate.TabIndex = 0; + this.bFlatgrassGenerate.Text = "Generate"; + this.bFlatgrassGenerate.UseVisualStyleBackColor = true; + this.bFlatgrassGenerate.Click += new System.EventHandler( this.bGenerate_Click ); + // + // nFlatgrassDimX + // + this.nFlatgrassDimX.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimX.Location = new System.Drawing.Point( 153, 23 ); + this.nFlatgrassDimX.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nFlatgrassDimX.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimX.Name = "nFlatgrassDimX"; + this.nFlatgrassDimX.Size = new System.Drawing.Size( 54, 20 ); + this.nFlatgrassDimX.TabIndex = 2; + this.nFlatgrassDimX.Value = new decimal( new int[] { + 64, + 0, + 0, + 0} ); + // + // lFlatgrassX1 + // + this.lFlatgrassX1.AutoSize = true; + this.lFlatgrassX1.Location = new System.Drawing.Point( 213, 25 ); + this.lFlatgrassX1.Name = "lFlatgrassX1"; + this.lFlatgrassX1.Size = new System.Drawing.Size( 13, 13 ); + this.lFlatgrassX1.TabIndex = 3; + this.lFlatgrassX1.Text = "×"; + // + // lFlatgrassDimensions + // + this.lFlatgrassDimensions.AutoSize = true; + this.lFlatgrassDimensions.Location = new System.Drawing.Point( 86, 25 ); + this.lFlatgrassDimensions.Name = "lFlatgrassDimensions"; + this.lFlatgrassDimensions.Size = new System.Drawing.Size( 61, 13 ); + this.lFlatgrassDimensions.TabIndex = 1; + this.lFlatgrassDimensions.Text = "Dimensions"; + // + // lFlatgrassX2 + // + this.lFlatgrassX2.AutoSize = true; + this.lFlatgrassX2.Location = new System.Drawing.Point( 292, 25 ); + this.lFlatgrassX2.Name = "lFlatgrassX2"; + this.lFlatgrassX2.Size = new System.Drawing.Size( 13, 13 ); + this.lFlatgrassX2.TabIndex = 5; + this.lFlatgrassX2.Text = "×"; + // + // nFlatgrassDimZ + // + this.nFlatgrassDimZ.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimZ.Location = new System.Drawing.Point( 311, 23 ); + this.nFlatgrassDimZ.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nFlatgrassDimZ.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimZ.Name = "nFlatgrassDimZ"; + this.nFlatgrassDimZ.Size = new System.Drawing.Size( 54, 20 ); + this.nFlatgrassDimZ.TabIndex = 6; + this.nFlatgrassDimZ.Value = new decimal( new int[] { + 64, + 0, + 0, + 0} ); + // + // nFlatgrassDimY + // + this.nFlatgrassDimY.Increment = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimY.Location = new System.Drawing.Point( 232, 23 ); + this.nFlatgrassDimY.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nFlatgrassDimY.Minimum = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFlatgrassDimY.Name = "nFlatgrassDimY"; + this.nFlatgrassDimY.Size = new System.Drawing.Size( 54, 20 ); + this.nFlatgrassDimY.TabIndex = 4; + this.nFlatgrassDimY.Value = new decimal( new int[] { + 64, + 0, + 0, + 0} ); + // + // tabTerrain + // + this.tabTerrain.BackColor = System.Drawing.SystemColors.Window; + this.tabTerrain.Controls.Add( this.flowLayoutPanel1 ); + this.tabTerrain.Controls.Add( this.xSeed ); + this.tabTerrain.Controls.Add( this.nSeed ); + this.tabTerrain.Controls.Add( this.xAdvanced ); + this.tabTerrain.Controls.Add( this.bGenerate ); + this.tabTerrain.Controls.Add( this.lTheme ); + this.tabTerrain.Controls.Add( this.cTheme ); + this.tabTerrain.Location = new System.Drawing.Point( 4, 22 ); + this.tabTerrain.Name = "tabTerrain"; + this.tabTerrain.Padding = new System.Windows.Forms.Padding( 3 ); + this.tabTerrain.Size = new System.Drawing.Size( 391, 422 ); + this.tabTerrain.TabIndex = 5; + this.tabTerrain.Text = "Generator"; + this.tabTerrain.UseVisualStyleBackColor = true; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel1.AutoScroll = true; + this.flowLayoutPanel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.flowLayoutPanel1.Controls.Add( this.gGenOptions ); + this.flowLayoutPanel1.Controls.Add( this.gTemplates ); + this.flowLayoutPanel1.Controls.Add( this.gMapSize ); + this.flowLayoutPanel1.Controls.Add( this.gTerrainFeatures ); + this.flowLayoutPanel1.Controls.Add( this.gHeightmapCreation ); + this.flowLayoutPanel1.Controls.Add( this.gCaves ); + this.flowLayoutPanel1.Controls.Add( this.gTrees ); + this.flowLayoutPanel1.Controls.Add( this.gSnow ); + this.flowLayoutPanel1.Controls.Add( this.gCliffs ); + this.flowLayoutPanel1.Controls.Add( this.gBeaches ); + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel1.Location = new System.Drawing.Point( 0, 59 ); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size( 391, 365 ); + this.flowLayoutPanel1.TabIndex = 57; + this.flowLayoutPanel1.WrapContents = false; + // + // gGenOptions + // + this.gGenOptions.Controls.Add( this.xAddCliffs ); + this.gGenOptions.Controls.Add( this.xAddRuins ); + this.gGenOptions.Controls.Add( this.xOre ); + this.gGenOptions.Controls.Add( this.xAddBeaches ); + this.gGenOptions.Controls.Add( this.xAddTrees ); + this.gGenOptions.Controls.Add( this.xAddSnow ); + this.gGenOptions.Controls.Add( this.xFloodBarrier ); + this.gGenOptions.Controls.Add( this.xWater ); + this.gGenOptions.Controls.Add( this.xCaves ); + this.gGenOptions.Location = new System.Drawing.Point( 3, 3 ); + this.gGenOptions.Name = "gGenOptions"; + this.gGenOptions.Size = new System.Drawing.Size( 362, 91 ); + this.gGenOptions.TabIndex = 22; + this.gGenOptions.TabStop = false; + this.gGenOptions.Text = "Optional Modules"; + // + // xAddCliffs + // + this.xAddCliffs.AutoSize = true; + this.xAddCliffs.Location = new System.Drawing.Point( 254, 42 ); + this.xAddCliffs.Name = "xAddCliffs"; + this.xAddCliffs.Size = new System.Drawing.Size( 48, 17 ); + this.xAddCliffs.TabIndex = 69; + this.xAddCliffs.Text = "Cliffs"; + this.xAddCliffs.UseVisualStyleBackColor = true; + this.xAddCliffs.CheckedChanged += new System.EventHandler( this.xAddCliffs_CheckedChanged ); + // + // xAddRuins + // + this.xAddRuins.AutoSize = true; + this.xAddRuins.Enabled = false; + this.xAddRuins.Location = new System.Drawing.Point( 254, 19 ); + this.xAddRuins.Name = "xAddRuins"; + this.xAddRuins.Size = new System.Drawing.Size( 53, 17 ); + this.xAddRuins.TabIndex = 68; + this.xAddRuins.Text = "Ruins"; + this.xAddRuins.UseVisualStyleBackColor = true; + // + // xOre + // + this.xOre.AutoSize = true; + this.xOre.Location = new System.Drawing.Point( 110, 65 ); + this.xOre.Name = "xOre"; + this.xOre.Size = new System.Drawing.Size( 71, 17 ); + this.xOre.TabIndex = 67; + this.xOre.Text = "Ore veins"; + this.xOre.UseVisualStyleBackColor = true; + // + // xAddBeaches + // + this.xAddBeaches.AutoSize = true; + this.xAddBeaches.Location = new System.Drawing.Point( 110, 19 ); + this.xAddBeaches.Name = "xAddBeaches"; + this.xAddBeaches.Size = new System.Drawing.Size( 68, 17 ); + this.xAddBeaches.TabIndex = 58; + this.xAddBeaches.Text = "Beaches"; + this.xAddBeaches.UseVisualStyleBackColor = true; + this.xAddBeaches.CheckedChanged += new System.EventHandler( this.xAddBeaches_CheckedChanged ); + // + // xAddSnow + // + this.xAddSnow.AutoSize = true; + this.xAddSnow.Location = new System.Drawing.Point( 110, 42 ); + this.xAddSnow.Name = "xAddSnow"; + this.xAddSnow.Size = new System.Drawing.Size( 109, 17 ); + this.xAddSnow.TabIndex = 24; + this.xAddSnow.Text = "Snowy mountains"; + this.xAddSnow.UseVisualStyleBackColor = true; + this.xAddSnow.CheckedChanged += new System.EventHandler( this.xAddSnow_CheckedChanged ); + // + // xWater + // + this.xWater.AutoSize = true; + this.xWater.Checked = true; + this.xWater.CheckState = System.Windows.Forms.CheckState.Checked; + this.xWater.Location = new System.Drawing.Point( 13, 42 ); + this.xWater.Name = "xWater"; + this.xWater.Size = new System.Drawing.Size( 55, 17 ); + this.xWater.TabIndex = 20; + this.xWater.Text = "Water"; + this.xWater.UseVisualStyleBackColor = true; + this.xWater.CheckedChanged += new System.EventHandler( this.xWater_CheckedChanged ); + // + // xCaves + // + this.xCaves.AutoSize = true; + this.xCaves.Location = new System.Drawing.Point( 13, 65 ); + this.xCaves.Name = "xCaves"; + this.xCaves.Size = new System.Drawing.Size( 56, 17 ); + this.xCaves.TabIndex = 23; + this.xCaves.Text = "Caves"; + this.xCaves.UseVisualStyleBackColor = true; + this.xCaves.CheckedChanged += new System.EventHandler( this.xCaves_CheckedChanged ); + // + // gTemplates + // + this.gTemplates.Controls.Add( this.cTemplates ); + this.gTemplates.Controls.Add( this.lUseTemplate ); + this.gTemplates.Controls.Add( this.bBrowseTemplate ); + this.gTemplates.Controls.Add( this.bSaveTemplate ); + this.gTemplates.Location = new System.Drawing.Point( 3, 100 ); + this.gTemplates.Name = "gTemplates"; + this.gTemplates.Size = new System.Drawing.Size( 362, 52 ); + this.gTemplates.TabIndex = 21; + this.gTemplates.TabStop = false; + this.gTemplates.Text = "Templates"; + // + // cTemplates + // + this.cTemplates.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cTemplates.FormattingEnabled = true; + this.cTemplates.Location = new System.Drawing.Point( 98, 21 ); + this.cTemplates.Name = "cTemplates"; + this.cTemplates.Size = new System.Drawing.Size( 116, 21 ); + this.cTemplates.TabIndex = 4; + this.cTemplates.SelectedIndexChanged += new System.EventHandler( this.cTemplates_SelectedIndexChanged ); + // + // lUseTemplate + // + this.lUseTemplate.AutoSize = true; + this.lUseTemplate.Location = new System.Drawing.Point( 23, 24 ); + this.lUseTemplate.Name = "lUseTemplate"; + this.lUseTemplate.Size = new System.Drawing.Size( 69, 13 ); + this.lUseTemplate.TabIndex = 3; + this.lUseTemplate.Text = "Use template"; + // + // bBrowseTemplate + // + this.bBrowseTemplate.Location = new System.Drawing.Point( 222, 19 ); + this.bBrowseTemplate.Name = "bBrowseTemplate"; + this.bBrowseTemplate.Size = new System.Drawing.Size( 64, 23 ); + this.bBrowseTemplate.TabIndex = 1; + this.bBrowseTemplate.Text = "Browse"; + this.bBrowseTemplate.UseVisualStyleBackColor = true; + this.bBrowseTemplate.Click += new System.EventHandler( this.bBrowseTemplate_Click ); + // + // bSaveTemplate + // + this.bSaveTemplate.Location = new System.Drawing.Point( 292, 19 ); + this.bSaveTemplate.Name = "bSaveTemplate"; + this.bSaveTemplate.Size = new System.Drawing.Size( 64, 23 ); + this.bSaveTemplate.TabIndex = 0; + this.bSaveTemplate.Text = "Save"; + this.bSaveTemplate.UseVisualStyleBackColor = true; + this.bSaveTemplate.Click += new System.EventHandler( this.bSaveTemplate_Click ); + // + // gMapSize + // + this.gMapSize.Controls.Add( this.nMaxDepthVariation ); + this.gMapSize.Controls.Add( this.nMaxHeightVariation ); + this.gMapSize.Controls.Add( this.lMaxHeightVariationUnits ); + this.gMapSize.Controls.Add( this.lMaxDepthVariationUnits ); + this.gMapSize.Controls.Add( this.xWaterLevel ); + this.gMapSize.Controls.Add( this.nWaterLevel ); + this.gMapSize.Controls.Add( this.lWaterLevelLabel ); + this.gMapSize.Controls.Add( this.nMaxDepth ); + this.gMapSize.Controls.Add( this.nMaxHeight ); + this.gMapSize.Controls.Add( this.nMapWidth ); + this.gMapSize.Controls.Add( this.lMaxHeight ); + this.gMapSize.Controls.Add( this.lMaxHeightUnits ); + this.gMapSize.Controls.Add( this.lMaxDepth ); + this.gMapSize.Controls.Add( this.lMaxDepthUnits ); + this.gMapSize.Controls.Add( this.lX1 ); + this.gMapSize.Controls.Add( this.lDim ); + this.gMapSize.Controls.Add( this.lX2 ); + this.gMapSize.Controls.Add( this.nMapHeight ); + this.gMapSize.Controls.Add( this.nMapLength ); + this.gMapSize.Location = new System.Drawing.Point( 3, 158 ); + this.gMapSize.Name = "gMapSize"; + this.gMapSize.Size = new System.Drawing.Size( 362, 147 ); + this.gMapSize.TabIndex = 9; + this.gMapSize.TabStop = false; + this.gMapSize.Text = "Dimensions"; + // + // nMaxDepthVariation + // + this.nMaxDepthVariation.Location = new System.Drawing.Point( 167, 78 ); + this.nMaxDepthVariation.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMaxDepthVariation.Name = "nMaxDepthVariation"; + this.nMaxDepthVariation.Size = new System.Drawing.Size( 54, 20 ); + this.nMaxDepthVariation.TabIndex = 50; + // + // nMaxHeightVariation + // + this.nMaxHeightVariation.Location = new System.Drawing.Point( 167, 52 ); + this.nMaxHeightVariation.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMaxHeightVariation.Name = "nMaxHeightVariation"; + this.nMaxHeightVariation.Size = new System.Drawing.Size( 54, 20 ); + this.nMaxHeightVariation.TabIndex = 49; + this.nMaxHeightVariation.Value = new decimal( new int[] { + 4, + 0, + 0, + 0} ); + // + // lMaxHeightVariationUnits + // + this.lMaxHeightVariationUnits.AutoSize = true; + this.lMaxHeightVariationUnits.Location = new System.Drawing.Point( 227, 54 ); + this.lMaxHeightVariationUnits.Name = "lMaxHeightVariationUnits"; + this.lMaxHeightVariationUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lMaxHeightVariationUnits.TabIndex = 51; + this.lMaxHeightVariationUnits.Text = "blocks"; + // + // lMaxDepthVariationUnits + // + this.lMaxDepthVariationUnits.AutoSize = true; + this.lMaxDepthVariationUnits.Location = new System.Drawing.Point( 227, 80 ); + this.lMaxDepthVariationUnits.Name = "lMaxDepthVariationUnits"; + this.lMaxDepthVariationUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lMaxDepthVariationUnits.TabIndex = 52; + this.lMaxDepthVariationUnits.Text = "blocks"; + // + // xWaterLevel + // + this.xWaterLevel.AutoSize = true; + this.xWaterLevel.Location = new System.Drawing.Point( 31, 122 ); + this.xWaterLevel.Name = "xWaterLevel"; + this.xWaterLevel.Size = new System.Drawing.Size( 115, 17 ); + this.xWaterLevel.TabIndex = 48; + this.xWaterLevel.Text = "Custom water level"; + this.xWaterLevel.UseVisualStyleBackColor = true; + this.xWaterLevel.CheckedChanged += new System.EventHandler( this.xWaterLevel_CheckedChanged ); + // + // nWaterLevel + // + this.nWaterLevel.Location = new System.Drawing.Point( 152, 121 ); + this.nWaterLevel.Name = "nWaterLevel"; + this.nWaterLevel.Size = new System.Drawing.Size( 54, 20 ); + this.nWaterLevel.TabIndex = 45; + this.nWaterLevel.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // lWaterLevelLabel + // + this.lWaterLevelLabel.AutoSize = true; + this.lWaterLevelLabel.Location = new System.Drawing.Point( 212, 123 ); + this.lWaterLevelLabel.Name = "lWaterLevelLabel"; + this.lWaterLevelLabel.Size = new System.Drawing.Size( 38, 13 ); + this.lWaterLevelLabel.TabIndex = 47; + this.lWaterLevelLabel.Text = "blocks"; + // + // nMaxDepth + // + this.nMaxDepth.Location = new System.Drawing.Point( 80, 78 ); + this.nMaxDepth.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMaxDepth.Name = "nMaxDepth"; + this.nMaxDepth.Size = new System.Drawing.Size( 54, 20 ); + this.nMaxDepth.TabIndex = 4; + this.nMaxDepth.Value = new decimal( new int[] { + 12, + 0, + 0, + 0} ); + // + // nMaxHeight + // + this.nMaxHeight.Location = new System.Drawing.Point( 80, 52 ); + this.nMaxHeight.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nMaxHeight.Name = "nMaxHeight"; + this.nMaxHeight.Size = new System.Drawing.Size( 54, 20 ); + this.nMaxHeight.TabIndex = 3; + this.nMaxHeight.Value = new decimal( new int[] { + 22, + 0, + 0, + 0} ); + // + // gTerrainFeatures + // + this.gTerrainFeatures.Controls.Add( this.lLoweredCorners ); + this.gTerrainFeatures.Controls.Add( this.nLoweredCorners ); + this.gTerrainFeatures.Controls.Add( this.cMidpoint ); + this.gTerrainFeatures.Controls.Add( this.lMidpoint ); + this.gTerrainFeatures.Controls.Add( this.lRaisedCorners ); + this.gTerrainFeatures.Controls.Add( this.nRaisedCorners ); + this.gTerrainFeatures.Controls.Add( this.lBiasDisplay ); + this.gTerrainFeatures.Controls.Add( this.lBias ); + this.gTerrainFeatures.Controls.Add( this.sBias ); + this.gTerrainFeatures.Location = new System.Drawing.Point( 3, 311 ); + this.gTerrainFeatures.Name = "gTerrainFeatures"; + this.gTerrainFeatures.Size = new System.Drawing.Size( 362, 84 ); + this.gTerrainFeatures.TabIndex = 10; + this.gTerrainFeatures.TabStop = false; + this.gTerrainFeatures.Text = "Feature Bias"; + this.gTerrainFeatures.Visible = false; + // + // lLoweredCorners + // + this.lLoweredCorners.AutoSize = true; + this.lLoweredCorners.Location = new System.Drawing.Point( 207, 57 ); + this.lLoweredCorners.Name = "lLoweredCorners"; + this.lLoweredCorners.Size = new System.Drawing.Size( 86, 13 ); + this.lLoweredCorners.TabIndex = 66; + this.lLoweredCorners.Text = "Lowered corners"; + // + // nLoweredCorners + // + this.nLoweredCorners.Enabled = false; + this.nLoweredCorners.Location = new System.Drawing.Point( 299, 55 ); + this.nLoweredCorners.Maximum = new decimal( new int[] { + 4, + 0, + 0, + 0} ); + this.nLoweredCorners.Name = "nLoweredCorners"; + this.nLoweredCorners.Size = new System.Drawing.Size( 54, 20 ); + this.nLoweredCorners.TabIndex = 65; + this.nLoweredCorners.ValueChanged += new System.EventHandler( this.nLoweredCorners_ValueChanged ); + // + // cMidpoint + // + this.cMidpoint.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cMidpoint.Enabled = false; + this.cMidpoint.FormattingEnabled = true; + this.cMidpoint.Items.AddRange( new object[] { + "Lowered", + "Neutral", + "Raised"} ); + this.cMidpoint.Location = new System.Drawing.Point( 66, 54 ); + this.cMidpoint.Name = "cMidpoint"; + this.cMidpoint.Size = new System.Drawing.Size( 101, 21 ); + this.cMidpoint.TabIndex = 1; + // + // lMidpoint + // + this.lMidpoint.AutoSize = true; + this.lMidpoint.Location = new System.Drawing.Point( 6, 57 ); + this.lMidpoint.Name = "lMidpoint"; + this.lMidpoint.Size = new System.Drawing.Size( 47, 13 ); + this.lMidpoint.TabIndex = 64; + this.lMidpoint.Text = "Midpoint"; + // + // lRaisedCorners + // + this.lRaisedCorners.AutoSize = true; + this.lRaisedCorners.Location = new System.Drawing.Point( 215, 21 ); + this.lRaisedCorners.Name = "lRaisedCorners"; + this.lRaisedCorners.Size = new System.Drawing.Size( 78, 13 ); + this.lRaisedCorners.TabIndex = 61; + this.lRaisedCorners.Text = "Raised corners"; + // + // nRaisedCorners + // + this.nRaisedCorners.Enabled = false; + this.nRaisedCorners.Location = new System.Drawing.Point( 299, 19 ); + this.nRaisedCorners.Maximum = new decimal( new int[] { + 4, + 0, + 0, + 0} ); + this.nRaisedCorners.Name = "nRaisedCorners"; + this.nRaisedCorners.Size = new System.Drawing.Size( 54, 20 ); + this.nRaisedCorners.TabIndex = 2; + this.nRaisedCorners.ValueChanged += new System.EventHandler( this.nRaisedCorners_ValueChanged ); + // + // lBiasDisplay + // + this.lBiasDisplay.AutoSize = true; + this.lBiasDisplay.Location = new System.Drawing.Point( 178, 21 ); + this.lBiasDisplay.Name = "lBiasDisplay"; + this.lBiasDisplay.Size = new System.Drawing.Size( 21, 13 ); + this.lBiasDisplay.TabIndex = 57; + this.lBiasDisplay.Text = "0%"; + // + // gHeightmapCreation + // + this.gHeightmapCreation.Controls.Add( this.lBelowFunc ); + this.gHeightmapCreation.Controls.Add( this.lAboveFunc ); + this.gHeightmapCreation.Controls.Add( this.lBelowFuncUnits ); + this.gHeightmapCreation.Controls.Add( this.lAboveFuncUnits ); + this.gHeightmapCreation.Controls.Add( this.sAboveFunc ); + this.gHeightmapCreation.Controls.Add( this.sBelowFunc ); + this.gHeightmapCreation.Controls.Add( this.xDelayBias ); + this.gHeightmapCreation.Controls.Add( this.xInvert ); + this.gHeightmapCreation.Controls.Add( this.sDetailScale ); + this.gHeightmapCreation.Controls.Add( this.lDetailSizeDisplay ); + this.gHeightmapCreation.Controls.Add( this.label5 ); + this.gHeightmapCreation.Controls.Add( this.sFeatureScale ); + this.gHeightmapCreation.Controls.Add( this.lRoughnessDisplay ); + this.gHeightmapCreation.Controls.Add( this.lFeatureSizeDisplay ); + this.gHeightmapCreation.Controls.Add( this.xMatchWaterCoverage ); + this.gHeightmapCreation.Controls.Add( this.sWaterCoverage ); + this.gHeightmapCreation.Controls.Add( this.xLayeredHeightmap ); + this.gHeightmapCreation.Controls.Add( this.xMarbledMode ); + this.gHeightmapCreation.Controls.Add( this.sRoughness ); + this.gHeightmapCreation.Controls.Add( this.lMatchWaterCoverageDisplay ); + this.gHeightmapCreation.Controls.Add( this.lRoughness ); + this.gHeightmapCreation.Controls.Add( this.lDetailSize ); + this.gHeightmapCreation.Location = new System.Drawing.Point( 3, 401 ); + this.gHeightmapCreation.Name = "gHeightmapCreation"; + this.gHeightmapCreation.Size = new System.Drawing.Size( 362, 226 ); + this.gHeightmapCreation.TabIndex = 11; + this.gHeightmapCreation.TabStop = false; + this.gHeightmapCreation.Text = "Heightmap Creation"; + this.gHeightmapCreation.Visible = false; + // + // lBelowFunc + // + this.lBelowFunc.AutoSize = true; + this.lBelowFunc.Location = new System.Drawing.Point( 26, 193 ); + this.lBelowFunc.Name = "lBelowFunc"; + this.lBelowFunc.Size = new System.Drawing.Size( 113, 13 ); + this.lBelowFunc.TabIndex = 47; + this.lBelowFunc.Text = "Underwater steepness"; + // + // lAboveFunc + // + this.lAboveFunc.AutoSize = true; + this.lAboveFunc.Location = new System.Drawing.Point( 57, 159 ); + this.lAboveFunc.Name = "lAboveFunc"; + this.lAboveFunc.Size = new System.Drawing.Size( 82, 13 ); + this.lAboveFunc.TabIndex = 46; + this.lAboveFunc.Text = "Land steepness"; + // + // lBelowFuncUnits + // + this.lBelowFuncUnits.AutoSize = true; + this.lBelowFuncUnits.Location = new System.Drawing.Point( 314, 193 ); + this.lBelowFuncUnits.Name = "lBelowFuncUnits"; + this.lBelowFuncUnits.Size = new System.Drawing.Size( 42, 13 ); + this.lBelowFuncUnits.TabIndex = 45; + this.lBelowFuncUnits.Text = "100.0%"; + // + // lAboveFuncUnits + // + this.lAboveFuncUnits.AutoSize = true; + this.lAboveFuncUnits.Location = new System.Drawing.Point( 314, 159 ); + this.lAboveFuncUnits.Name = "lAboveFuncUnits"; + this.lAboveFuncUnits.Size = new System.Drawing.Size( 42, 13 ); + this.lAboveFuncUnits.TabIndex = 44; + this.lAboveFuncUnits.Text = "100.0%"; + // + // sAboveFunc + // + this.sAboveFunc.AutoSize = false; + this.sAboveFunc.LargeChange = 50; + this.sAboveFunc.Location = new System.Drawing.Point( 145, 157 ); + this.sAboveFunc.Maximum = 600; + this.sAboveFunc.Name = "sAboveFunc"; + this.sAboveFunc.Size = new System.Drawing.Size( 163, 27 ); + this.sAboveFunc.SmallChange = 20; + this.sAboveFunc.TabIndex = 43; + this.sAboveFunc.TickFrequency = 20; + this.sAboveFunc.Value = 300; + this.sAboveFunc.ValueChanged += new System.EventHandler( this.sAboveFunc_ValueChanged ); + // + // sBelowFunc + // + this.sBelowFunc.AutoSize = false; + this.sBelowFunc.LargeChange = 50; + this.sBelowFunc.Location = new System.Drawing.Point( 145, 190 ); + this.sBelowFunc.Maximum = 600; + this.sBelowFunc.Name = "sBelowFunc"; + this.sBelowFunc.Size = new System.Drawing.Size( 163, 27 ); + this.sBelowFunc.SmallChange = 20; + this.sBelowFunc.TabIndex = 42; + this.sBelowFunc.TickFrequency = 20; + this.sBelowFunc.Value = 300; + this.sBelowFunc.ValueChanged += new System.EventHandler( this.sBelowFunc_ValueChanged ); + // + // xDelayBias + // + this.xDelayBias.AutoSize = true; + this.xDelayBias.Location = new System.Drawing.Point( 281, 87 ); + this.xDelayBias.Name = "xDelayBias"; + this.xDelayBias.Size = new System.Drawing.Size( 75, 17 ); + this.xDelayBias.TabIndex = 40; + this.xDelayBias.Text = "Delay bias"; + this.xDelayBias.UseVisualStyleBackColor = true; + // + // xInvert + // + this.xInvert.AutoSize = true; + this.xInvert.Location = new System.Drawing.Point( 281, 64 ); + this.xInvert.Name = "xInvert"; + this.xInvert.Size = new System.Drawing.Size( 53, 17 ); + this.xInvert.TabIndex = 39; + this.xInvert.Text = "Invert"; + this.xInvert.UseVisualStyleBackColor = true; + // + // sDetailScale + // + this.sDetailScale.AutoSize = false; + this.sDetailScale.Location = new System.Drawing.Point( 98, 52 ); + this.sDetailScale.Maximum = 7; + this.sDetailScale.Minimum = -1; + this.sDetailScale.Name = "sDetailScale"; + this.sDetailScale.Size = new System.Drawing.Size( 116, 27 ); + this.sDetailScale.TabIndex = 1; + this.sDetailScale.Value = 7; + this.sDetailScale.ValueChanged += new System.EventHandler( this.sDetailSize_ValueChanged ); + // + // lDetailSizeDisplay + // + this.lDetailSizeDisplay.AutoSize = true; + this.lDetailSizeDisplay.Location = new System.Drawing.Point( 216, 55 ); + this.lDetailSizeDisplay.Name = "lDetailSizeDisplay"; + this.lDetailSizeDisplay.Size = new System.Drawing.Size( 25, 13 ); + this.lDetailSizeDisplay.TabIndex = 38; + this.lDetailSizeDisplay.Text = "1×1"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point( 30, 55 ); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size( 62, 13 ); + this.label5.TabIndex = 37; + this.label5.Text = "Detail scale"; + // + // gCaves + // + this.gCaves.Controls.Add( this.lCaveSizeDisplay ); + this.gCaves.Controls.Add( this.lCaveDensityDisplay ); + this.gCaves.Controls.Add( this.xCaveLava ); + this.gCaves.Controls.Add( this.xCaveWater ); + this.gCaves.Controls.Add( this.sCaveSize ); + this.gCaves.Controls.Add( this.lCaveSize ); + this.gCaves.Controls.Add( this.sCaveDensity ); + this.gCaves.Controls.Add( this.lCaveDensity ); + this.gCaves.Location = new System.Drawing.Point( 3, 633 ); + this.gCaves.Name = "gCaves"; + this.gCaves.Size = new System.Drawing.Size( 362, 91 ); + this.gCaves.TabIndex = 22; + this.gCaves.TabStop = false; + this.gCaves.Text = "Caves"; + this.gCaves.Visible = false; + // + // lCaveSizeDisplay + // + this.lCaveSizeDisplay.AutoSize = true; + this.lCaveSizeDisplay.Location = new System.Drawing.Point( 213, 57 ); + this.lCaveSizeDisplay.Name = "lCaveSizeDisplay"; + this.lCaveSizeDisplay.Size = new System.Drawing.Size( 33, 13 ); + this.lCaveSizeDisplay.TabIndex = 68; + this.lCaveSizeDisplay.Text = "100%"; + // + // lCaveDensityDisplay + // + this.lCaveDensityDisplay.AutoSize = true; + this.lCaveDensityDisplay.Location = new System.Drawing.Point( 213, 23 ); + this.lCaveDensityDisplay.Name = "lCaveDensityDisplay"; + this.lCaveDensityDisplay.Size = new System.Drawing.Size( 33, 13 ); + this.lCaveDensityDisplay.TabIndex = 67; + this.lCaveDensityDisplay.Text = "200%"; + // + // xCaveLava + // + this.xCaveLava.AutoSize = true; + this.xCaveLava.Location = new System.Drawing.Point( 260, 56 ); + this.xCaveLava.Name = "xCaveLava"; + this.xCaveLava.Size = new System.Drawing.Size( 82, 17 ); + this.xCaveLava.TabIndex = 65; + this.xCaveLava.Text = "Lava caves"; + this.xCaveLava.UseVisualStyleBackColor = true; + // + // xCaveWater + // + this.xCaveWater.AutoSize = true; + this.xCaveWater.Location = new System.Drawing.Point( 260, 22 ); + this.xCaveWater.Name = "xCaveWater"; + this.xCaveWater.Size = new System.Drawing.Size( 96, 17 ); + this.xCaveWater.TabIndex = 64; + this.xCaveWater.Text = "Flooded caves"; + this.xCaveWater.UseVisualStyleBackColor = true; + // + // sCaveSize + // + this.sCaveSize.AutoSize = false; + this.sCaveSize.LargeChange = 25; + this.sCaveSize.Location = new System.Drawing.Point( 98, 55 ); + this.sCaveSize.Maximum = 250; + this.sCaveSize.Minimum = 50; + this.sCaveSize.Name = "sCaveSize"; + this.sCaveSize.Size = new System.Drawing.Size( 116, 27 ); + this.sCaveSize.TabIndex = 63; + this.sCaveSize.TickFrequency = 50; + this.sCaveSize.Value = 100; + this.sCaveSize.ValueChanged += new System.EventHandler( this.sCaveSize_ValueChanged ); + // + // lCaveSize + // + this.lCaveSize.AutoSize = true; + this.lCaveSize.Location = new System.Drawing.Point( 39, 57 ); + this.lCaveSize.Name = "lCaveSize"; + this.lCaveSize.Size = new System.Drawing.Size( 53, 13 ); + this.lCaveSize.TabIndex = 62; + this.lCaveSize.Text = "Cave size"; + // + // sCaveDensity + // + this.sCaveDensity.AutoSize = false; + this.sCaveDensity.LargeChange = 25; + this.sCaveDensity.Location = new System.Drawing.Point( 98, 22 ); + this.sCaveDensity.Maximum = 500; + this.sCaveDensity.Minimum = 50; + this.sCaveDensity.Name = "sCaveDensity"; + this.sCaveDensity.Size = new System.Drawing.Size( 116, 27 ); + this.sCaveDensity.TabIndex = 61; + this.sCaveDensity.TickFrequency = 50; + this.sCaveDensity.Value = 200; + this.sCaveDensity.ValueChanged += new System.EventHandler( this.sCaveDensity_ValueChanged ); + // + // lCaveDensity + // + this.lCaveDensity.AutoSize = true; + this.lCaveDensity.Location = new System.Drawing.Point( 24, 23 ); + this.lCaveDensity.Name = "lCaveDensity"; + this.lCaveDensity.Size = new System.Drawing.Size( 68, 13 ); + this.lCaveDensity.TabIndex = 60; + this.lCaveDensity.Text = "Cave density"; + // + // gTrees + // + this.gTrees.Controls.Add( this.xGiantTrees ); + this.gTrees.Controls.Add( this.lTreeHeightUnits ); + this.gTrees.Controls.Add( this.nTreeHeightVariation ); + this.gTrees.Controls.Add( this.lTreeHeightVariation ); + this.gTrees.Controls.Add( this.nTreeHeight ); + this.gTrees.Controls.Add( this.lTreeHeight ); + this.gTrees.Controls.Add( this.lTreeSpacingUnits ); + this.gTrees.Controls.Add( this.nTreeSpacingVariation ); + this.gTrees.Controls.Add( this.lTreeSpacingVariation ); + this.gTrees.Controls.Add( this.nTreeSpacing ); + this.gTrees.Controls.Add( this.lTreeSpacing ); + this.gTrees.Location = new System.Drawing.Point( 3, 730 ); + this.gTrees.Name = "gTrees"; + this.gTrees.Size = new System.Drawing.Size( 362, 75 ); + this.gTrees.TabIndex = 12; + this.gTrees.TabStop = false; + this.gTrees.Text = "Trees"; + this.gTrees.Visible = false; + // + // xGiantTrees + // + this.xGiantTrees.AutoSize = true; + this.xGiantTrees.Location = new System.Drawing.Point( 260, 20 ); + this.xGiantTrees.Name = "xGiantTrees"; + this.xGiantTrees.Size = new System.Drawing.Size( 77, 17 ); + this.xGiantTrees.TabIndex = 68; + this.xGiantTrees.Text = "Giant trees"; + this.xGiantTrees.UseVisualStyleBackColor = true; + // + // lTreeHeightUnits + // + this.lTreeHeightUnits.AutoSize = true; + this.lTreeHeightUnits.Location = new System.Drawing.Point( 211, 47 ); + this.lTreeHeightUnits.Name = "lTreeHeightUnits"; + this.lTreeHeightUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lTreeHeightUnits.TabIndex = 67; + this.lTreeHeightUnits.Text = "blocks"; + // + // nTreeHeightVariation + // + this.nTreeHeightVariation.Location = new System.Drawing.Point( 160, 45 ); + this.nTreeHeightVariation.Maximum = new decimal( new int[] { + 32, + 0, + 0, + 0} ); + this.nTreeHeightVariation.Name = "nTreeHeightVariation"; + this.nTreeHeightVariation.Size = new System.Drawing.Size( 45, 20 ); + this.nTreeHeightVariation.TabIndex = 3; + this.nTreeHeightVariation.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // lTreeHeightVariation + // + this.lTreeHeightVariation.AutoSize = true; + this.lTreeHeightVariation.Location = new System.Drawing.Point( 133, 47 ); + this.lTreeHeightVariation.Name = "lTreeHeightVariation"; + this.lTreeHeightVariation.Size = new System.Drawing.Size( 21, 13 ); + this.lTreeHeightVariation.TabIndex = 65; + this.lTreeHeightVariation.Text = "+/-"; + // + // nTreeHeight + // + this.nTreeHeight.Location = new System.Drawing.Point( 83, 45 ); + this.nTreeHeight.Maximum = new decimal( new int[] { + 64, + 0, + 0, + 0} ); + this.nTreeHeight.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nTreeHeight.Name = "nTreeHeight"; + this.nTreeHeight.Size = new System.Drawing.Size( 44, 20 ); + this.nTreeHeight.TabIndex = 2; + this.nTreeHeight.Value = new decimal( new int[] { + 6, + 0, + 0, + 0} ); + // + // lTreeHeight + // + this.lTreeHeight.AutoSize = true; + this.lTreeHeight.Location = new System.Drawing.Point( 14, 47 ); + this.lTreeHeight.Name = "lTreeHeight"; + this.lTreeHeight.Size = new System.Drawing.Size( 61, 13 ); + this.lTreeHeight.TabIndex = 64; + this.lTreeHeight.Text = "Tree height"; + // + // lTreeSpacingUnits + // + this.lTreeSpacingUnits.AutoSize = true; + this.lTreeSpacingUnits.Location = new System.Drawing.Point( 211, 21 ); + this.lTreeSpacingUnits.Name = "lTreeSpacingUnits"; + this.lTreeSpacingUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lTreeSpacingUnits.TabIndex = 62; + this.lTreeSpacingUnits.Text = "blocks"; + // + // nTreeSpacingVariation + // + this.nTreeSpacingVariation.Location = new System.Drawing.Point( 160, 19 ); + this.nTreeSpacingVariation.Maximum = new decimal( new int[] { + 512, + 0, + 0, + 0} ); + this.nTreeSpacingVariation.Name = "nTreeSpacingVariation"; + this.nTreeSpacingVariation.Size = new System.Drawing.Size( 45, 20 ); + this.nTreeSpacingVariation.TabIndex = 1; + this.nTreeSpacingVariation.Value = new decimal( new int[] { + 3, + 0, + 0, + 0} ); + // + // lTreeSpacingVariation + // + this.lTreeSpacingVariation.AutoSize = true; + this.lTreeSpacingVariation.Location = new System.Drawing.Point( 133, 21 ); + this.lTreeSpacingVariation.Name = "lTreeSpacingVariation"; + this.lTreeSpacingVariation.Size = new System.Drawing.Size( 21, 13 ); + this.lTreeSpacingVariation.TabIndex = 60; + this.lTreeSpacingVariation.Text = "+/-"; + // + // nTreeSpacing + // + this.nTreeSpacing.Location = new System.Drawing.Point( 83, 19 ); + this.nTreeSpacing.Maximum = new decimal( new int[] { + 1024, + 0, + 0, + 0} ); + this.nTreeSpacing.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nTreeSpacing.Name = "nTreeSpacing"; + this.nTreeSpacing.Size = new System.Drawing.Size( 44, 20 ); + this.nTreeSpacing.TabIndex = 0; + this.nTreeSpacing.Value = new decimal( new int[] { + 8, + 0, + 0, + 0} ); + // + // lTreeSpacing + // + this.lTreeSpacing.AutoSize = true; + this.lTreeSpacing.Location = new System.Drawing.Point( 6, 21 ); + this.lTreeSpacing.Name = "lTreeSpacing"; + this.lTreeSpacing.Size = new System.Drawing.Size( 69, 13 ); + this.lTreeSpacing.TabIndex = 59; + this.lTreeSpacing.Text = "Tree spacing"; + // + // gSnow + // + this.gSnow.Controls.Add( this.lSnowTransitionUnits ); + this.gSnow.Controls.Add( this.lSnowTransition ); + this.gSnow.Controls.Add( this.lSnowAltitudeUnits ); + this.gSnow.Controls.Add( this.nSnowTransition ); + this.gSnow.Controls.Add( this.nSnowAltitude ); + this.gSnow.Controls.Add( this.lSnowAltitude ); + this.gSnow.Location = new System.Drawing.Point( 3, 811 ); + this.gSnow.Name = "gSnow"; + this.gSnow.Size = new System.Drawing.Size( 362, 45 ); + this.gSnow.TabIndex = 24; + this.gSnow.TabStop = false; + this.gSnow.Text = "Snowy Mountains"; + this.gSnow.Visible = false; + // + // lSnowTransitionUnits + // + this.lSnowTransitionUnits.AutoSize = true; + this.lSnowTransitionUnits.Location = new System.Drawing.Point( 318, 21 ); + this.lSnowTransitionUnits.Name = "lSnowTransitionUnits"; + this.lSnowTransitionUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lSnowTransitionUnits.TabIndex = 69; + this.lSnowTransitionUnits.Text = "blocks"; + // + // lSnowTransition + // + this.lSnowTransition.AutoSize = true; + this.lSnowTransition.Location = new System.Drawing.Point( 217, 21 ); + this.lSnowTransition.Name = "lSnowTransition"; + this.lSnowTransition.Size = new System.Drawing.Size( 35, 13 ); + this.lSnowTransition.TabIndex = 68; + this.lSnowTransition.Text = "Dither"; + // + // lSnowAltitudeUnits + // + this.lSnowAltitudeUnits.AutoSize = true; + this.lSnowAltitudeUnits.Location = new System.Drawing.Point( 143, 21 ); + this.lSnowAltitudeUnits.Name = "lSnowAltitudeUnits"; + this.lSnowAltitudeUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lSnowAltitudeUnits.TabIndex = 67; + this.lSnowAltitudeUnits.Text = "blocks"; + // + // nSnowTransition + // + this.nSnowTransition.Location = new System.Drawing.Point( 258, 19 ); + this.nSnowTransition.Maximum = new decimal( new int[] { + 1024, + 0, + 0, + 0} ); + this.nSnowTransition.Name = "nSnowTransition"; + this.nSnowTransition.Size = new System.Drawing.Size( 54, 20 ); + this.nSnowTransition.TabIndex = 64; + this.nSnowTransition.Value = new decimal( new int[] { + 5, + 0, + 0, + 0} ); + // + // nSnowAltitude + // + this.nSnowAltitude.Location = new System.Drawing.Point( 83, 19 ); + this.nSnowAltitude.Maximum = new decimal( new int[] { + 2032, + 0, + 0, + 0} ); + this.nSnowAltitude.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nSnowAltitude.Name = "nSnowAltitude"; + this.nSnowAltitude.Size = new System.Drawing.Size( 54, 20 ); + this.nSnowAltitude.TabIndex = 63; + this.nSnowAltitude.Value = new decimal( new int[] { + 40, + 0, + 0, + 0} ); + // + // lSnowAltitude + // + this.lSnowAltitude.AutoSize = true; + this.lSnowAltitude.Location = new System.Drawing.Point( 6, 21 ); + this.lSnowAltitude.Name = "lSnowAltitude"; + this.lSnowAltitude.Size = new System.Drawing.Size( 71, 13 ); + this.lSnowAltitude.TabIndex = 65; + this.lSnowAltitude.Text = "Snow altitude"; + // + // gCliffs + // + this.gCliffs.Controls.Add( this.xCliffSmoothing ); + this.gCliffs.Controls.Add( this.lCliffThresholdUnits ); + this.gCliffs.Controls.Add( this.sCliffThreshold ); + this.gCliffs.Controls.Add( this.lCliffThreshold ); + this.gCliffs.Location = new System.Drawing.Point( 3, 862 ); + this.gCliffs.Name = "gCliffs"; + this.gCliffs.Size = new System.Drawing.Size( 362, 59 ); + this.gCliffs.TabIndex = 23; + this.gCliffs.TabStop = false; + this.gCliffs.Text = "Cliffs"; + this.gCliffs.Visible = false; + // + // xCliffSmoothing + // + this.xCliffSmoothing.AutoSize = true; + this.xCliffSmoothing.Location = new System.Drawing.Point( 260, 23 ); + this.xCliffSmoothing.Name = "xCliffSmoothing"; + this.xCliffSmoothing.Size = new System.Drawing.Size( 74, 17 ); + this.xCliffSmoothing.TabIndex = 75; + this.xCliffSmoothing.Text = "Smoothed"; + this.xCliffSmoothing.UseVisualStyleBackColor = true; + // + // lCliffThresholdUnits + // + this.lCliffThresholdUnits.AutoSize = true; + this.lCliffThresholdUnits.Location = new System.Drawing.Point( 213, 23 ); + this.lCliffThresholdUnits.Name = "lCliffThresholdUnits"; + this.lCliffThresholdUnits.Size = new System.Drawing.Size( 33, 13 ); + this.lCliffThresholdUnits.TabIndex = 74; + this.lCliffThresholdUnits.Text = "100%"; + // + // sCliffThreshold + // + this.sCliffThreshold.AutoSize = false; + this.sCliffThreshold.LargeChange = 20; + this.sCliffThreshold.Location = new System.Drawing.Point( 103, 19 ); + this.sCliffThreshold.Maximum = 200; + this.sCliffThreshold.Minimum = 20; + this.sCliffThreshold.Name = "sCliffThreshold"; + this.sCliffThreshold.Size = new System.Drawing.Size( 110, 27 ); + this.sCliffThreshold.TabIndex = 73; + this.sCliffThreshold.TickFrequency = 10; + this.sCliffThreshold.Value = 100; + this.sCliffThreshold.ValueChanged += new System.EventHandler( this.sCliffThreshold_ValueChanged ); + // + // lCliffThreshold + // + this.lCliffThreshold.AutoSize = true; + this.lCliffThreshold.Location = new System.Drawing.Point( 43, 24 ); + this.lCliffThreshold.Name = "lCliffThreshold"; + this.lCliffThreshold.Size = new System.Drawing.Size( 54, 13 ); + this.lCliffThreshold.TabIndex = 72; + this.lCliffThreshold.Text = "Threshold"; + // + // gBeaches + // + this.gBeaches.Controls.Add( this.lBeachHeight ); + this.gBeaches.Controls.Add( this.lBeachExtentUnits ); + this.gBeaches.Controls.Add( this.lBeachHeightUnits ); + this.gBeaches.Controls.Add( this.nBeachHeight ); + this.gBeaches.Controls.Add( this.nBeachExtent ); + this.gBeaches.Controls.Add( this.lBeachExtent ); + this.gBeaches.Location = new System.Drawing.Point( 3, 927 ); + this.gBeaches.Name = "gBeaches"; + this.gBeaches.Size = new System.Drawing.Size( 362, 48 ); + this.gBeaches.TabIndex = 76; + this.gBeaches.TabStop = false; + this.gBeaches.Text = "Beaches"; + this.gBeaches.Visible = false; + // + // lBeachHeight + // + this.lBeachHeight.AutoSize = true; + this.lBeachHeight.Location = new System.Drawing.Point( 216, 23 ); + this.lBeachHeight.Name = "lBeachHeight"; + this.lBeachHeight.Size = new System.Drawing.Size( 38, 13 ); + this.lBeachHeight.TabIndex = 69; + this.lBeachHeight.Text = "Height"; + // + // lBeachExtentUnits + // + this.lBeachExtentUnits.AutoSize = true; + this.lBeachExtentUnits.Location = new System.Drawing.Point( 143, 23 ); + this.lBeachExtentUnits.Name = "lBeachExtentUnits"; + this.lBeachExtentUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lBeachExtentUnits.TabIndex = 68; + this.lBeachExtentUnits.Text = "blocks"; + // + // lBeachHeightUnits + // + this.lBeachHeightUnits.AutoSize = true; + this.lBeachHeightUnits.Location = new System.Drawing.Point( 320, 23 ); + this.lBeachHeightUnits.Name = "lBeachHeightUnits"; + this.lBeachHeightUnits.Size = new System.Drawing.Size( 38, 13 ); + this.lBeachHeightUnits.TabIndex = 67; + this.lBeachHeightUnits.Text = "blocks"; + // + // nBeachHeight + // + this.nBeachHeight.Location = new System.Drawing.Point( 260, 21 ); + this.nBeachHeight.Maximum = new decimal( new int[] { + 512, + 0, + 0, + 0} ); + this.nBeachHeight.Name = "nBeachHeight"; + this.nBeachHeight.Size = new System.Drawing.Size( 54, 20 ); + this.nBeachHeight.TabIndex = 64; + this.nBeachHeight.Value = new decimal( new int[] { + 2, + 0, + 0, + 0} ); + // + // nBeachExtent + // + this.nBeachExtent.Location = new System.Drawing.Point( 83, 19 ); + this.nBeachExtent.Maximum = new decimal( new int[] { + 1024, + 0, + 0, + 0} ); + this.nBeachExtent.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nBeachExtent.Name = "nBeachExtent"; + this.nBeachExtent.Size = new System.Drawing.Size( 54, 20 ); + this.nBeachExtent.TabIndex = 63; + this.nBeachExtent.Value = new decimal( new int[] { + 7, + 0, + 0, + 0} ); + // + // lBeachExtent + // + this.lBeachExtent.AutoSize = true; + this.lBeachExtent.Location = new System.Drawing.Point( 40, 23 ); + this.lBeachExtent.Name = "lBeachExtent"; + this.lBeachExtent.Size = new System.Drawing.Size( 37, 13 ); + this.lBeachExtent.TabIndex = 65; + this.lBeachExtent.Text = "Extent"; + // + // xSeed + // + this.xSeed.AutoSize = true; + this.xSeed.Location = new System.Drawing.Point( 224, 34 ); + this.xSeed.Name = "xSeed"; + this.xSeed.Size = new System.Drawing.Size( 51, 17 ); + this.xSeed.TabIndex = 6; + this.xSeed.Text = "Seed"; + this.xSeed.UseVisualStyleBackColor = true; + this.xSeed.CheckedChanged += new System.EventHandler( this.xSeed_CheckedChanged ); + // + // nSeed + // + this.nSeed.Enabled = false; + this.nSeed.Location = new System.Drawing.Point( 281, 33 ); + this.nSeed.Maximum = new decimal( new int[] { + 2147483647, + 0, + 0, + 0} ); + this.nSeed.Minimum = new decimal( new int[] { + -2147483648, + 0, + 0, + -2147483648} ); + this.nSeed.Name = "nSeed"; + this.nSeed.Size = new System.Drawing.Size( 87, 20 ); + this.nSeed.TabIndex = 7; + // + // xAdvanced + // + this.xAdvanced.AutoSize = true; + this.xAdvanced.Location = new System.Drawing.Point( 107, 22 ); + this.xAdvanced.Name = "xAdvanced"; + this.xAdvanced.Size = new System.Drawing.Size( 75, 17 ); + this.xAdvanced.TabIndex = 1; + this.xAdvanced.Text = "Advanced"; + this.xAdvanced.UseVisualStyleBackColor = true; + this.xAdvanced.CheckedChanged += new System.EventHandler( this.xAdvanced_CheckedChanged ); + // + // lMapFileOptions + // + this.lMapFileOptions.AutoSize = true; + this.lMapFileOptions.Location = new System.Drawing.Point( 12, 94 ); + this.lMapFileOptions.Name = "lMapFileOptions"; + this.lMapFileOptions.Size = new System.Drawing.Size( 47, 13 ); + this.lMapFileOptions.TabIndex = 10; + this.lMapFileOptions.Text = "Map file:"; + // + // lCreateMap + // + this.lCreateMap.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.lCreateMap.AutoSize = true; + this.lCreateMap.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lCreateMap.ForeColor = System.Drawing.Color.Red; + this.lCreateMap.Location = new System.Drawing.Point( 610, 539 ); + this.lCreateMap.Name = "lCreateMap"; + this.lCreateMap.Size = new System.Drawing.Size( 150, 13 ); + this.lCreateMap.TabIndex = 15; + this.lCreateMap.Text = "Create a map to continue"; + // + // folderBrowser + // + this.folderBrowser.Description = "Find the folder where your Myne / MyneCraft / Hydebuild / iCraft map is located."; + // + // xBlockDB + // + this.xBlockDB.AutoSize = true; + this.xBlockDB.Location = new System.Drawing.Point( 246, 67 ); + this.xBlockDB.Name = "xBlockDB"; + this.xBlockDB.Size = new System.Drawing.Size( 68, 17 ); + this.xBlockDB.TabIndex = 9; + this.xBlockDB.Text = "BlockDB"; + this.xBlockDB.ThreeState = true; + this.xBlockDB.UseVisualStyleBackColor = true; + this.xBlockDB.CheckStateChanged += new System.EventHandler( this.xBlockDB_CheckStateChanged ); + // + // AddWorldPopup + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 984, 583 ); + this.Controls.Add( this.xBlockDB ); + this.Controls.Add( this.lCreateMap ); + this.Controls.Add( this.lMapFileOptions ); + this.Controls.Add( this.tabs ); + this.Controls.Add( this.bSavePreview ); + this.Controls.Add( this.previewLayout ); + this.Controls.Add( this.statusStrip ); + this.Controls.Add( this.xHidden ); + this.Controls.Add( this.tName ); + this.Controls.Add( this.lBackup ); + this.Controls.Add( this.lBuild ); + this.Controls.Add( this.lAccess ); + this.Controls.Add( this.lName ); + this.Controls.Add( this.cBuild ); + this.Controls.Add( this.cAccess ); + this.Controls.Add( this.cBackup ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bOK ); + this.Name = "AddWorldPopup"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.Text = "Add World"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler( this.AddWorldPopup_FormClosing ); + ((System.ComponentModel.ISupportInitialize)(this.nMapWidth)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMapLength)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMapHeight)).EndInit(); + this.statusStrip.ResumeLayout( false ); + this.statusStrip.PerformLayout(); + this.previewLayout.ResumeLayout( false ); + this.previewLayout.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.preview)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sFeatureScale)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sRoughness)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sWaterCoverage)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sBias)).EndInit(); + this.tabs.ResumeLayout( false ); + this.tabExisting.ResumeLayout( false ); + this.tabExisting.PerformLayout(); + this.tabLoad.ResumeLayout( false ); + this.tabLoad.PerformLayout(); + this.tabCopy.ResumeLayout( false ); + this.tabCopy.PerformLayout(); + this.tabFlatgrass.ResumeLayout( false ); + this.tabFlatgrass.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimX)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimZ)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nFlatgrassDimY)).EndInit(); + this.tabTerrain.ResumeLayout( false ); + this.tabTerrain.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout( false ); + this.gGenOptions.ResumeLayout( false ); + this.gGenOptions.PerformLayout(); + this.gTemplates.ResumeLayout( false ); + this.gTemplates.PerformLayout(); + this.gMapSize.ResumeLayout( false ); + this.gMapSize.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxDepthVariation)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxHeightVariation)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nWaterLevel)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxDepth)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxHeight)).EndInit(); + this.gTerrainFeatures.ResumeLayout( false ); + this.gTerrainFeatures.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nLoweredCorners)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nRaisedCorners)).EndInit(); + this.gHeightmapCreation.ResumeLayout( false ); + this.gHeightmapCreation.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sAboveFunc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sBelowFunc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sDetailScale)).EndInit(); + this.gCaves.ResumeLayout( false ); + this.gCaves.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sCaveSize)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.sCaveDensity)).EndInit(); + this.gTrees.ResumeLayout( false ); + this.gTrees.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeHeightVariation)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeHeight)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeSpacingVariation)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nTreeSpacing)).EndInit(); + this.gSnow.ResumeLayout( false ); + this.gSnow.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nSnowTransition)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSnowAltitude)).EndInit(); + this.gCliffs.ResumeLayout( false ); + this.gCliffs.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sCliffThreshold)).EndInit(); + this.gBeaches.ResumeLayout( false ); + this.gBeaches.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nBeachHeight)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nBeachExtent)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSeed)).EndInit(); + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lDim; + private System.Windows.Forms.Label lX2; + private System.Windows.Forms.Label lX1; + private System.Windows.Forms.NumericUpDown nMapWidth; + private System.Windows.Forms.NumericUpDown nMapLength; + private System.Windows.Forms.NumericUpDown nMapHeight; + private System.Windows.Forms.Label lPreview; + private System.Windows.Forms.Button bGenerate; + private System.Windows.Forms.ComboBox cWorld; + private System.Windows.Forms.TextBox tFile; + private System.Windows.Forms.Button bBrowseFile; + private System.Windows.Forms.Button bOK; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.ComboBox cBackup; + private System.Windows.Forms.ComboBox cAccess; + private System.Windows.Forms.ComboBox cBuild; + private System.Windows.Forms.Label lName; + private System.Windows.Forms.Label lAccess; + private System.Windows.Forms.Label lBuild; + private System.Windows.Forms.Label lBackup; + private System.Windows.Forms.TextBox tName; + private System.Windows.Forms.ComboBox cTheme; + private System.Windows.Forms.Label lTheme; + private System.Windows.Forms.Button bPreviewPrev; + private System.Windows.Forms.Button bPreviewNext; + private System.Windows.Forms.CheckBox xFloodBarrier; + private System.Windows.Forms.CheckBox xHidden; + private System.Windows.Forms.OpenFileDialog fileBrowser; + private System.Windows.Forms.StatusStrip statusStrip; + private System.Windows.Forms.ToolStripProgressBar progressBar; + private System.Windows.Forms.ToolStripStatusLabel tStatus1; + private System.Windows.Forms.ToolStripStatusLabel tStatus2; + private System.Windows.Forms.TableLayoutPanel previewLayout; + private System.Windows.Forms.Button bShow; + private System.Windows.Forms.CheckBox xLayeredHeightmap; + private System.Windows.Forms.CheckBox xMarbledMode; + private System.Windows.Forms.TrackBar sRoughness; + private System.Windows.Forms.Label lRoughness; + private System.Windows.Forms.TrackBar sFeatureScale; + private System.Windows.Forms.Label lDetailSize; + private System.Windows.Forms.Label lRoughnessDisplay; + private System.Windows.Forms.Label lMatchWaterCoverageDisplay; + private System.Windows.Forms.TrackBar sWaterCoverage; + private System.Windows.Forms.CheckBox xMatchWaterCoverage; + private System.Windows.Forms.Label lMaxHeightUnits; + private System.Windows.Forms.Label lMaxHeight; + private System.Windows.Forms.Label lFeatureSizeDisplay; + private System.Windows.Forms.Label lMaxDepth; + private System.Windows.Forms.TrackBar sBias; + private System.Windows.Forms.Label lBias; + private System.Windows.Forms.Label lMaxDepthUnits; + private System.Windows.Forms.CheckBox xAddTrees; + private System.Windows.Forms.Button bSavePreview; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.TabPage tabExisting; + private System.Windows.Forms.TabPage tabLoad; + private System.Windows.Forms.TabPage tabCopy; + private System.Windows.Forms.TabPage tabFlatgrass; + private System.Windows.Forms.TabPage tabTerrain; + private CustomPictureBox preview; + private System.Windows.Forms.Label lMapFileOptions; + private System.Windows.Forms.GroupBox gTrees; + private System.Windows.Forms.Label lTreeHeightUnits; + private System.Windows.Forms.NumericUpDown nTreeHeightVariation; + private System.Windows.Forms.Label lTreeHeightVariation; + private System.Windows.Forms.NumericUpDown nTreeHeight; + private System.Windows.Forms.Label lTreeHeight; + private System.Windows.Forms.Label lTreeSpacingUnits; + private System.Windows.Forms.NumericUpDown nTreeSpacingVariation; + private System.Windows.Forms.Label lTreeSpacingVariation; + private System.Windows.Forms.NumericUpDown nTreeSpacing; + private System.Windows.Forms.Label lTreeSpacing; + private System.Windows.Forms.GroupBox gHeightmapCreation; + private System.Windows.Forms.GroupBox gTerrainFeatures; + private System.Windows.Forms.GroupBox gMapSize; + private System.Windows.Forms.Label lBiasDisplay; + private System.Windows.Forms.CheckBox xAdvanced; + private System.Windows.Forms.TextBox tExistingMapInfo; + private System.Windows.Forms.TextBox tLoadFileInfo; + private System.Windows.Forms.Label lFile; + private System.Windows.Forms.Label lFormatList; + private System.Windows.Forms.TextBox tCopyInfo; + private System.Windows.Forms.Label lWorldToCopy; + private System.Windows.Forms.Button bFlatgrassGenerate; + private System.Windows.Forms.NumericUpDown nFlatgrassDimX; + private System.Windows.Forms.Label lFlatgrassX1; + private System.Windows.Forms.Label lFlatgrassDimensions; + private System.Windows.Forms.Label lFlatgrassX2; + private System.Windows.Forms.NumericUpDown nFlatgrassDimZ; + private System.Windows.Forms.NumericUpDown nFlatgrassDimY; + private System.Windows.Forms.NumericUpDown nMaxDepth; + private System.Windows.Forms.NumericUpDown nMaxHeight; + private System.Windows.Forms.NumericUpDown nSeed; + private System.Windows.Forms.CheckBox xSeed; + private System.Windows.Forms.Label lRaisedCorners; + private System.Windows.Forms.NumericUpDown nRaisedCorners; + private System.Windows.Forms.ComboBox cMidpoint; + private System.Windows.Forms.Label lMidpoint; + private System.Windows.Forms.TrackBar sDetailScale; + private System.Windows.Forms.Label lDetailSizeDisplay; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label lLoweredCorners; + private System.Windows.Forms.NumericUpDown nLoweredCorners; + private System.Windows.Forms.CheckBox xInvert; + private System.Windows.Forms.CheckBox xWater; + private System.Windows.Forms.GroupBox gTemplates; + private System.Windows.Forms.ComboBox cTemplates; + private System.Windows.Forms.Label lUseTemplate; + private System.Windows.Forms.Button bBrowseTemplate; + private System.Windows.Forms.Button bSaveTemplate; + private System.Windows.Forms.GroupBox gCaves; + private System.Windows.Forms.CheckBox xCaveLava; + private System.Windows.Forms.CheckBox xCaveWater; + private System.Windows.Forms.TrackBar sCaveSize; + private System.Windows.Forms.Label lCaveSize; + private System.Windows.Forms.TrackBar sCaveDensity; + private System.Windows.Forms.Label lCaveDensity; + private System.Windows.Forms.CheckBox xCaves; + private System.Windows.Forms.Label lCaveSizeDisplay; + private System.Windows.Forms.Label lCaveDensityDisplay; + private System.Windows.Forms.Label lCreateMap; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lFolder; + private System.Windows.Forms.TextBox tFolder; + private System.Windows.Forms.Button bBrowseFolder; + private System.Windows.Forms.FolderBrowserDialog folderBrowser; + private System.Windows.Forms.CheckBox xDelayBias; + private System.Windows.Forms.NumericUpDown nWaterLevel; + private System.Windows.Forms.Label lWaterLevelLabel; + private System.Windows.Forms.CheckBox xWaterLevel; + private System.Windows.Forms.CheckBox xAddSnow; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.CheckBox xAddBeaches; + private System.Windows.Forms.GroupBox gGenOptions; + private System.Windows.Forms.CheckBox xAddRuins; + private System.Windows.Forms.CheckBox xOre; + private System.Windows.Forms.CheckBox xAddCliffs; + private System.Windows.Forms.TrackBar sAboveFunc; + private System.Windows.Forms.TrackBar sBelowFunc; + private System.Windows.Forms.Label lBelowFuncUnits; + private System.Windows.Forms.Label lAboveFuncUnits; + private System.Windows.Forms.Label lBelowFunc; + private System.Windows.Forms.Label lAboveFunc; + private System.Windows.Forms.GroupBox gCliffs; + private System.Windows.Forms.GroupBox gSnow; + private System.Windows.Forms.Label lSnowTransitionUnits; + private System.Windows.Forms.Label lSnowTransition; + private System.Windows.Forms.Label lSnowAltitudeUnits; + private System.Windows.Forms.NumericUpDown nSnowTransition; + private System.Windows.Forms.NumericUpDown nSnowAltitude; + private System.Windows.Forms.Label lSnowAltitude; + private System.Windows.Forms.Label lCliffThresholdUnits; + private System.Windows.Forms.TrackBar sCliffThreshold; + private System.Windows.Forms.Label lCliffThreshold; + private System.Windows.Forms.CheckBox xCliffSmoothing; + private System.Windows.Forms.GroupBox gBeaches; + private System.Windows.Forms.Label lBeachHeight; + private System.Windows.Forms.Label lBeachExtentUnits; + private System.Windows.Forms.Label lBeachHeightUnits; + private System.Windows.Forms.NumericUpDown nBeachHeight; + private System.Windows.Forms.NumericUpDown nBeachExtent; + private System.Windows.Forms.Label lBeachExtent; + private System.Windows.Forms.NumericUpDown nMaxDepthVariation; + private System.Windows.Forms.NumericUpDown nMaxHeightVariation; + private System.Windows.Forms.Label lMaxHeightVariationUnits; + private System.Windows.Forms.Label lMaxDepthVariationUnits; + private System.Windows.Forms.CheckBox xBlockDB; + private System.Windows.Forms.CheckBox xGiantTrees; + } +} \ No newline at end of file diff --git a/ConfigGUI/AddWorldPopup.cs b/ConfigGUI/AddWorldPopup.cs new file mode 100644 index 0000000..cabd329 --- /dev/null +++ b/ConfigGUI/AddWorldPopup.cs @@ -0,0 +1,968 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Threading; +using System.Windows.Forms; +using fCraft.GUI; +using fCraft.MapConversion; + + +namespace fCraft.ConfigGUI { + sealed partial class AddWorldPopup : Form { + readonly BackgroundWorker bwLoader = new BackgroundWorker(), + bwGenerator = new BackgroundWorker(), + bwRenderer = new BackgroundWorker(); + + const string MapLoadFilter = "Minecraft Maps|*.fcm;*.lvl;*.dat;*.mclevel;*.gz;*.map;*.meta;*.mine;*.save"; + + readonly object redrawLock = new object(); + + Map map; + Map Map { + get { + return map; + } + set { + try { + bOK.Invoke( (MethodInvoker)delegate { + try { + bOK.Enabled = (value != null); + lCreateMap.Visible = !bOK.Enabled; + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + } ); + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + map = value; + } + } + + Stopwatch stopwatch; + int previewRotation; + Bitmap previewImage; + bool floodBarrier; + string originalWorldName; + readonly List copyOptionsList = new List(); + Tabs tab; + + + internal WorldListEntry World { get; private set; } + + + public AddWorldPopup( WorldListEntry world ) { + InitializeComponent(); + fileBrowser.Filter = MapLoadFilter; + + cBackup.Items.AddRange( WorldListEntry.BackupEnumNames ); + cTemplates.Items.AddRange( Enum.GetNames( typeof( MapGenTemplate ) ) ); + cTheme.Items.AddRange( Enum.GetNames( typeof( MapGenTheme ) ) ); + + bwLoader.DoWork += AsyncLoad; + bwLoader.RunWorkerCompleted += AsyncLoadCompleted; + + bwGenerator.DoWork += AsyncGen; + bwGenerator.WorkerReportsProgress = true; + bwGenerator.ProgressChanged += AsyncGenProgress; + bwGenerator.RunWorkerCompleted += AsyncGenCompleted; + + bwRenderer.WorkerReportsProgress = true; + bwRenderer.WorkerSupportsCancellation = true; + bwRenderer.DoWork += AsyncDraw; + bwRenderer.ProgressChanged += AsyncDrawProgress; + bwRenderer.RunWorkerCompleted += AsyncDrawCompleted; + + nMapWidth.Validating += MapDimensionValidating; + nMapLength.Validating += MapDimensionValidating; + nMapHeight.Validating += MapDimensionValidating; + + cAccess.Items.Add( "(everyone)" ); + cBuild.Items.Add( "(everyone)" ); + foreach( Rank rank in RankManager.Ranks ) { + cAccess.Items.Add( MainForm.ToComboBoxOption( rank ) ); + cBuild.Items.Add( MainForm.ToComboBoxOption( rank ) ); + } + + tStatus1.Text = ""; + tStatus2.Text = ""; + + World = world; + + savePreviewDialog.Filter = "PNG Image|*.png|TIFF Image|*.tif;*.tiff|Bitmap Image|*.bmp|JPEG Image|*.jpg;*.jpeg"; + savePreviewDialog.Title = "Saving preview image..."; + + browseTemplateDialog.Filter = "MapGenerator Template|*.ftpl"; + browseTemplateDialog.Title = "Opening a MapGenerator template..."; + + saveTemplateDialog.Filter = browseTemplateDialog.Filter; + saveTemplateDialog.Title = "Saving a MapGenerator template..."; + + Shown += LoadMap; + } + + + void LoadMap( object sender, EventArgs args ) { + // Fill in the "Copy existing world" combobox + foreach( WorldListEntry otherWorld in MainForm.Worlds ) { + if( otherWorld != World ) { + cWorld.Items.Add( otherWorld.Name + " (" + otherWorld.Description + ")" ); + copyOptionsList.Add( otherWorld ); + } + } + + if( World == null ) { + Text = "Adding a New World"; + + // keep trying "NewWorld#" until we find an unused number + int worldNameCounter = 1; + while( MainForm.IsWorldNameTaken( "NewWorld" + worldNameCounter ) ) { + worldNameCounter++; + } + + World = new WorldListEntry( "NewWorld" + worldNameCounter ); + + tName.Text = World.Name; + cAccess.SelectedIndex = 0; + cBuild.SelectedIndex = 0; + cBackup.SelectedIndex = 5; + xBlockDB.CheckState = CheckState.Indeterminate; + Map = null; + + } else { + // Editing a world + World = new WorldListEntry( World ); + Text = "Editing World \"" + World.Name + "\""; + originalWorldName = World.Name; + tName.Text = World.Name; + cAccess.SelectedItem = World.AccessPermission; + cBuild.SelectedItem = World.BuildPermission; + cBackup.SelectedItem = World.Backup; + xHidden.Checked = World.Hidden; + + switch( World.BlockDBEnabled ) { + case YesNoAuto.Auto: + xBlockDB.CheckState = CheckState.Indeterminate; + break; + case YesNoAuto.Yes: + xBlockDB.CheckState = CheckState.Checked; + break; + case YesNoAuto.No: + xBlockDB.CheckState = CheckState.Unchecked; + break; + } + } + + // Disable "copy" tab if there are no other worlds + if( cWorld.Items.Count > 0 ) { + cWorld.SelectedIndex = 0; + } else { + tabs.TabPages.Remove( tabCopy ); + } + + // Disable "existing map" tab if mapfile does not exist + fileToLoad = World.FullFileName; + if( File.Exists( fileToLoad ) ) { + ShowMapDetails( tExistingMapInfo, fileToLoad ); + StartLoadingMap(); + } else { + tabs.TabPages.Remove( tabExisting ); + tabs.SelectTab( tabLoad ); + } + + // Set Generator comboboxes to defaults + cTemplates.SelectedIndex = (int)MapGenTemplate.River; + + savePreviewDialog.FileName = World.Name; + } + + + #region Loading/Saving Map + + void StartLoadingMap() { + Map = null; + tStatus1.Text = "Loading " + new FileInfo( fileToLoad ).Name; + tStatus2.Text = ""; + progressBar.Visible = true; + progressBar.Style = ProgressBarStyle.Marquee; + bwLoader.RunWorkerAsync(); + } + + private void bBrowseFile_Click( object sender, EventArgs e ) { + fileBrowser.FileName = tFile.Text; + if( fileBrowser.ShowDialog() == DialogResult.OK && !String.IsNullOrEmpty( fileBrowser.FileName ) ) { + tFolder.Text = ""; + tFile.Text = fileBrowser.FileName; + tFile.SelectAll(); + + fileToLoad = fileBrowser.FileName; + ShowMapDetails( tLoadFileInfo, fileToLoad ); + StartLoadingMap(); + World.MapChangedBy = WorldListEntry.WorldInfoSignature; + World.MapChangedOn = DateTime.UtcNow; + } + } + + private void bBrowseFolder_Click( object sender, EventArgs e ) { + if( folderBrowser.ShowDialog() == DialogResult.OK && !String.IsNullOrEmpty( folderBrowser.SelectedPath ) ) { + tFile.Text = ""; + tFolder.Text = folderBrowser.SelectedPath; + tFolder.SelectAll(); + + fileToLoad = folderBrowser.SelectedPath; + ShowMapDetails( tLoadFileInfo, fileToLoad ); + StartLoadingMap(); + World.MapChangedBy = WorldListEntry.WorldInfoSignature; + World.MapChangedOn = DateTime.UtcNow; + } + } + + string fileToLoad; + void AsyncLoad( object sender, DoWorkEventArgs e ) { + stopwatch = Stopwatch.StartNew(); + try { + Map = MapUtility.Load( fileToLoad ); + Map.CalculateShadows(); + } catch( Exception ex ) { + MessageBox.Show( String.Format( "Could not load specified map: {0}: {1}", + ex.GetType().Name, ex.Message ) ); + } + } + + void AsyncLoadCompleted( object sender, RunWorkerCompletedEventArgs e ) { + stopwatch.Stop(); + if( Map == null ) { + tStatus1.Text = "Load failed!"; + } else { + tStatus1.Text = "Load successful (" + stopwatch.Elapsed.TotalSeconds.ToString( "0.000" ) + "s)"; + tStatus2.Text = ", drawing..."; + Redraw( true ); + } + if( tab == Tabs.CopyWorld ) { + bShow.Enabled = true; + } + } + + #endregion Loading + + + #region Map Preview + + IsoCat renderer; + + void Redraw( bool drawAgain ) { + lock( redrawLock ) { + progressBar.Visible = true; + progressBar.Style = ProgressBarStyle.Continuous; + if( bwRenderer.IsBusy ) { + bwRenderer.CancelAsync(); + while( bwRenderer.IsBusy ) { + Thread.Sleep( 1 ); + Application.DoEvents(); + } + } + if( drawAgain ) bwRenderer.RunWorkerAsync(); + } + } + + void AsyncDraw( object sender, DoWorkEventArgs e ) { + stopwatch = Stopwatch.StartNew(); + renderer = new IsoCat( Map, IsoCatMode.Normal, previewRotation ); + Rectangle cropRectangle; + if( bwRenderer.CancellationPending ) return; + Bitmap rawImage = renderer.Draw( out cropRectangle, bwRenderer ); + if( bwRenderer.CancellationPending ) return; + if( rawImage != null ) { + previewImage = rawImage.Clone( cropRectangle, rawImage.PixelFormat ); + } + renderer = null; + GC.Collect( GC.MaxGeneration, GCCollectionMode.Optimized ); + } + + void AsyncDrawProgress( object sender, ProgressChangedEventArgs e ) { + progressBar.Value = e.ProgressPercentage; + } + + void AsyncDrawCompleted( object sender, RunWorkerCompletedEventArgs e ) { + stopwatch.Stop(); + tStatus2.Text = String.Format( "drawn ({0:0.000}s)", stopwatch.Elapsed.TotalSeconds ); + if( previewImage != null && previewImage != preview.Image ) { + Image oldImage = preview.Image; + if( oldImage != null ) oldImage.Dispose(); + preview.Image = previewImage; + bSavePreview.Enabled = true; + } + progressBar.Visible = false; + } + + private void bPreviewPrev_Click( object sender, EventArgs e ) { + if( Map == null ) return; + if( previewRotation == 0 ) previewRotation = 3; + else previewRotation--; + tStatus2.Text = ", redrawing..."; + Redraw( true ); + } + + private void bPreviewNext_Click( object sender, EventArgs e ) { + if( Map == null ) return; + if( previewRotation == 3 ) previewRotation = 0; + else previewRotation++; + tStatus2.Text = ", redrawing..."; + Redraw( true ); + } + + #endregion + + + #region Map Generation + + MapGeneratorArgs generatorArgs = new MapGeneratorArgs(); + + private void bGenerate_Click( object sender, EventArgs e ) { + Map = null; + bGenerate.Enabled = false; + bFlatgrassGenerate.Enabled = false; + + if( tab == Tabs.Generator ) { + if( !xSeed.Checked ) { + nSeed.Value = GetRandomSeed(); + } + + SaveGeneratorArgs(); + } + + tStatus1.Text = "Generating..."; + tStatus2.Text = ""; + progressBar.Visible = true; + progressBar.Style = ProgressBarStyle.Continuous; + progressBar.Value = 0; + + Refresh(); + bwGenerator.RunWorkerAsync(); + World.MapChangedBy = WorldListEntry.WorldInfoSignature; + World.MapChangedOn = DateTime.UtcNow; + } + + void AsyncGen( object sender, DoWorkEventArgs e ) { + stopwatch = Stopwatch.StartNew(); + GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced ); + Map generatedMap; + if( tab == Tabs.Generator ) { + MapGenerator gen = new MapGenerator( generatorArgs ); + gen.ProgressChanged += + ( progressSender, progressArgs ) => + bwGenerator.ReportProgress( progressArgs.ProgressPercentage, progressArgs.UserState ); + generatedMap = gen.Generate(); + } else { + generatedMap = MapGenerator.GenerateFlatgrass( Convert.ToInt32( nFlatgrassDimX.Value ), + Convert.ToInt32( nFlatgrassDimY.Value ), + Convert.ToInt32( nFlatgrassDimZ.Value ) ); + } + + if( floodBarrier ) generatedMap.MakeFloodBarrier(); + generatedMap.CalculateShadows(); + Map = generatedMap; + GC.Collect( GC.MaxGeneration, GCCollectionMode.Forced ); + } + + void AsyncGenProgress( object sender, ProgressChangedEventArgs e ) { + progressBar.Value = e.ProgressPercentage; + tStatus1.Text = (string)e.UserState; + } + + void AsyncGenCompleted( object sender, RunWorkerCompletedEventArgs e ) { + stopwatch.Stop(); + if( Map == null ) { + tStatus1.Text = "Generation failed!"; + } else { + tStatus1.Text = "Generation successful (" + stopwatch.Elapsed.TotalSeconds.ToString( "0.000" ) + "s)"; + tStatus2.Text = ", drawing..."; + Redraw( true ); + } + bGenerate.Enabled = true; + bFlatgrassGenerate.Enabled = true; + } + + + readonly Random rand = new Random(); + int GetRandomSeed() { + return rand.Next() - rand.Next(); + } + + #endregion + + + #region Input Handlers + + void xFloodBarrier_CheckedChanged( object sender, EventArgs e ) { + floodBarrier = xFloodBarrier.Checked; + } + + + static void MapDimensionValidating( object sender, CancelEventArgs e ) { + ((NumericUpDown)sender).Value = Convert.ToInt32( ((NumericUpDown)sender).Value / 16 ) * 16; + } + + + void tName_Validating( object sender, CancelEventArgs e ) { + if( fCraft.World.IsValidName( tName.Text ) && + (!MainForm.IsWorldNameTaken( tName.Text ) || + (originalWorldName != null && tName.Text.ToLower() == originalWorldName.ToLower())) ) { + tName.ForeColor = SystemColors.ControlText; + } else { + tName.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + } + } + + + void tName_Validated( object sender, EventArgs e ) { + World.Name = tName.Text; + } + + + void cAccess_SelectedIndexChanged( object sender, EventArgs e ) { + World.AccessPermission = cAccess.SelectedItem.ToString(); + } + + + void cBuild_SelectedIndexChanged( object sender, EventArgs e ) { + World.BuildPermission = cBuild.SelectedItem.ToString(); + } + + + void cBackup_SelectedIndexChanged( object sender, EventArgs e ) { + World.Backup = cBackup.SelectedItem.ToString(); + } + + + void xHidden_CheckedChanged( object sender, EventArgs e ) { + World.Hidden = xHidden.Checked; + } + + + void bShow_Click( object sender, EventArgs e ) { + if( cWorld.SelectedIndex != -1 && File.Exists( copyOptionsList[cWorld.SelectedIndex].FullFileName ) ) { + bShow.Enabled = false; + fileToLoad = copyOptionsList[cWorld.SelectedIndex].FullFileName; + ShowMapDetails( tCopyInfo, fileToLoad ); + StartLoadingMap(); + } + } + + + void cWorld_SelectedIndexChanged( object sender, EventArgs e ) { + if( cWorld.SelectedIndex != -1 ) { + string fileName = copyOptionsList[cWorld.SelectedIndex].FullFileName; + bShow.Enabled = File.Exists( fileName ); + ShowMapDetails( tCopyInfo, fileName ); + } + } + + + void xAdvanced_CheckedChanged( object sender, EventArgs e ) { + gTerrainFeatures.Visible = xAdvanced.Checked; + gHeightmapCreation.Visible = xAdvanced.Checked; + gTrees.Visible = xAdvanced.Checked && xAddTrees.Checked; + gCaves.Visible = xCaves.Checked && xAdvanced.Checked; + gSnow.Visible = xAdvanced.Checked && xAddSnow.Checked; + gCliffs.Visible = xAdvanced.Checked && xAddCliffs.Checked; + gBeaches.Visible = xAdvanced.Checked && xAddBeaches.Checked; + } + + + void MapDimensionChanged( object sender, EventArgs e ) { + sFeatureScale.Maximum = (int)Math.Log( (double)Math.Max( nMapWidth.Value, nMapLength.Value ), 2 ); + int value = sDetailScale.Maximum - sDetailScale.Value; + sDetailScale.Maximum = sFeatureScale.Maximum; + sDetailScale.Value = sDetailScale.Maximum - value; + + int resolution = 1 << (sDetailScale.Maximum - sDetailScale.Value); + lDetailSizeDisplay.Text = resolution + "×" + resolution; + resolution = 1 << (sFeatureScale.Maximum - sFeatureScale.Value); + lFeatureSizeDisplay.Text = resolution + "×" + resolution; + } + + + void sFeatureSize_ValueChanged( object sender, EventArgs e ) { + int resolution = 1 << (sFeatureScale.Maximum - sFeatureScale.Value); + lFeatureSizeDisplay.Text = resolution + "×" + resolution; + if( sDetailScale.Value < sFeatureScale.Value ) { + sDetailScale.Value = sFeatureScale.Value; + } + } + + + void sDetailSize_ValueChanged( object sender, EventArgs e ) { + int resolution = 1 << (sDetailScale.Maximum - sDetailScale.Value); + lDetailSizeDisplay.Text = resolution + "×" + resolution; + if( sFeatureScale.Value > sDetailScale.Value ) { + sFeatureScale.Value = sDetailScale.Value; + } + } + + + void xMatchWaterCoverage_CheckedChanged( object sender, EventArgs e ) { + sWaterCoverage.Enabled = xMatchWaterCoverage.Checked; + } + + + void sWaterCoverage_ValueChanged( object sender, EventArgs e ) { + lMatchWaterCoverageDisplay.Text = sWaterCoverage.Value + "%"; + } + + + void sBias_ValueChanged( object sender, EventArgs e ) { + lBiasDisplay.Text = sBias.Value + "%"; + bool useBias = (sBias.Value != 0); + + nRaisedCorners.Enabled = useBias; + nLoweredCorners.Enabled = useBias; + cMidpoint.Enabled = useBias; + xDelayBias.Enabled = useBias; + } + + + void sRoughness_ValueChanged( object sender, EventArgs e ) { + lRoughnessDisplay.Text = sRoughness.Value + "%"; + } + + + void xSeed_CheckedChanged( object sender, EventArgs e ) { + nSeed.Enabled = xSeed.Checked; + } + + + void nRaisedCorners_ValueChanged( object sender, EventArgs e ) { + nLoweredCorners.Value = Math.Min( 4 - nRaisedCorners.Value, nLoweredCorners.Value ); + } + + + void nLoweredCorners_ValueChanged( object sender, EventArgs e ) { + nRaisedCorners.Value = Math.Min( 4 - nLoweredCorners.Value, nRaisedCorners.Value ); + } + + #endregion + + + #region Tabs + + private void tabs_SelectedIndexChanged( object sender, EventArgs e ) { + if( tabs.SelectedTab == tabExisting ) { + tab = Tabs.ExistingMap; + } else if( tabs.SelectedTab == tabLoad ) { + tab = Tabs.LoadFile; + } else if( tabs.SelectedTab == tabCopy ) { + tab = Tabs.CopyWorld; + } else if( tabs.SelectedTab == tabFlatgrass ) { + tab = Tabs.Flatgrass; + } else { + tab = Tabs.Generator; + } + + switch( tab ) { + case Tabs.ExistingMap: + fileToLoad = World.FullFileName; + ShowMapDetails( tExistingMapInfo, fileToLoad ); + StartLoadingMap(); + return; + case Tabs.LoadFile: + if( !String.IsNullOrEmpty( tFile.Text ) ) { + tFile.SelectAll(); + fileToLoad = tFile.Text; + ShowMapDetails( tLoadFileInfo, fileToLoad ); + StartLoadingMap(); + } + return; + case Tabs.CopyWorld: + if( cWorld.SelectedIndex != -1 ) { + bShow.Enabled = File.Exists( copyOptionsList[cWorld.SelectedIndex].FullFileName ); + } + return; + case Tabs.Flatgrass: + return; + case Tabs.Generator: + return; + } + } + + enum Tabs { + ExistingMap, + LoadFile, + CopyWorld, + Flatgrass, + Generator + } + + #endregion + + + static void ShowMapDetails( TextBox textBox, string fileName ) { + DateTime creationTime, modificationTime; + long fileSize; + + if( File.Exists( fileName ) ) { + FileInfo existingMapFileInfo = new FileInfo( fileName ); + creationTime = existingMapFileInfo.CreationTime; + modificationTime = existingMapFileInfo.LastWriteTime; + fileSize = existingMapFileInfo.Length; + + } else if( Directory.Exists( fileName ) ) { + DirectoryInfo dirInfo = new DirectoryInfo( fileName ); + creationTime = dirInfo.CreationTime; + modificationTime = dirInfo.LastWriteTime; + fileSize = dirInfo.GetFiles().Sum( finfo => finfo.Length ); + + } else { + textBox.Text = "File or directory \"" + fileName + "\" does not exist."; + return; + } + + MapFormat format = MapUtility.Identify( fileName, true ); + try { + Map loadedMap = MapUtility.LoadHeader( fileName ); + const string msgFormat = +@" Location: {0} + Format: {1} + Filesize: {2} KB + Created: {3} + Modified: {4} +Dimensions: {5}×{6}×{7} + Blocks: {8}"; + textBox.Text = String.Format( msgFormat, + fileName, + format, + (fileSize / 1024), + creationTime.ToLongDateString(), + modificationTime.ToLongDateString(), + loadedMap.Width, + loadedMap.Length, + loadedMap.Height, + loadedMap.Volume ); + + } catch( Exception ex ) { + const string msgFormat = +@" Location: {0} + Format: {1} + Filesize: {2} KB + Created: {3} + Modified: {4} + +Could not load more information: +{5}: {6}"; + textBox.Text = String.Format( msgFormat, + fileName, + format, + (fileSize / 1024), + creationTime.ToLongDateString(), + modificationTime.ToLongDateString(), + ex.GetType().Name, + ex.Message ); + } + } + + + private void AddWorldPopup_FormClosing( object sender, FormClosingEventArgs e ) { + Redraw( false ); + if( DialogResult == DialogResult.OK ) { + if( Map == null ) { + e.Cancel = true; + } else { + bwRenderer.CancelAsync(); + Enabled = false; + progressBar.Visible = true; + progressBar.Style = ProgressBarStyle.Marquee; + tStatus1.Text = "Saving map..."; + tStatus2.Text = ""; + Refresh(); + + string newFileName = World.FullFileName; + Map.Save( newFileName ); + string oldFileName = Path.Combine( Paths.MapPath, originalWorldName + ".fcm" ); + + if( originalWorldName != null && originalWorldName != World.Name && File.Exists( oldFileName ) ) { + try { + File.Delete( oldFileName ); + } catch( Exception ex ) { + string errorMessage = String.Format( "Renaming the map file failed. Please delete the old file ({0}.fcm) manually.{1}{2}", + originalWorldName, Environment.NewLine, ex ); + MessageBox.Show( errorMessage, "Error renaming the map file" ); + } + } + } + } + } + + + readonly SaveFileDialog savePreviewDialog = new SaveFileDialog(); + private void bSavePreview_Click( object sender, EventArgs e ) { + try { + using( Image img = (Image)preview.Image.Clone() ) { + if( savePreviewDialog.ShowDialog() == DialogResult.OK && !String.IsNullOrEmpty( savePreviewDialog.FileName ) ) { + switch( savePreviewDialog.FilterIndex ) { + case 1: + img.Save( savePreviewDialog.FileName, ImageFormat.Png ); break; + case 2: + img.Save( savePreviewDialog.FileName, ImageFormat.Tiff ); break; + case 3: + img.Save( savePreviewDialog.FileName, ImageFormat.Bmp ); break; + case 4: + img.Save( savePreviewDialog.FileName, ImageFormat.Jpeg ); break; + } + } + } + } catch( Exception ex ) { + MessageBox.Show( "Could not prepare image for saving: " + ex ); + } + } + + + readonly OpenFileDialog browseTemplateDialog = new OpenFileDialog(); + private void bBrowseTemplate_Click( object sender, EventArgs e ) { + if( browseTemplateDialog.ShowDialog() == DialogResult.OK && !String.IsNullOrEmpty( browseTemplateDialog.FileName ) ) { + try { + generatorArgs = new MapGeneratorArgs( browseTemplateDialog.FileName ); + LoadGeneratorArgs(); + bGenerate.PerformClick(); + } catch( Exception ex ) { + MessageBox.Show( "Could not open template file: " + ex ); + } + } + } + + void LoadGeneratorArgs() { + nMapHeight.Value = generatorArgs.MapHeight; + nMapWidth.Value = generatorArgs.MapWidth; + nMapLength.Value = generatorArgs.MapLength; + + cTheme.SelectedIndex = (int)generatorArgs.Theme; + + sDetailScale.Value = generatorArgs.DetailScale; + sFeatureScale.Value = generatorArgs.FeatureScale; + + xLayeredHeightmap.Checked = generatorArgs.LayeredHeightmap; + xMarbledMode.Checked = generatorArgs.MarbledHeightmap; + xMatchWaterCoverage.Checked = generatorArgs.MatchWaterCoverage; + xInvert.Checked = generatorArgs.InvertHeightmap; + + nMaxDepth.Value = generatorArgs.MaxDepth; + nMaxHeight.Value = generatorArgs.MaxHeight; + sRoughness.Value = (int)(generatorArgs.Roughness * 100); + nSeed.Value = generatorArgs.Seed; + xWater.Checked = generatorArgs.AddWater; + + if( generatorArgs.UseBias ) sBias.Value = (int)(generatorArgs.Bias * 100); + else sBias.Value = 0; + xDelayBias.Checked = generatorArgs.DelayBias; + + sWaterCoverage.Value = (int)(100 * generatorArgs.WaterCoverage); + cMidpoint.SelectedIndex = generatorArgs.MidPoint + 1; + nRaisedCorners.Value = generatorArgs.RaisedCorners; + nLoweredCorners.Value = generatorArgs.LoweredCorners; + + xAddTrees.Checked = generatorArgs.AddTrees; + xGiantTrees.Checked = generatorArgs.AddGiantTrees; + nTreeHeight.Value = (generatorArgs.TreeHeightMax + generatorArgs.TreeHeightMin) / 2m; + nTreeHeightVariation.Value = (generatorArgs.TreeHeightMax - generatorArgs.TreeHeightMin) / 2m; + nTreeSpacing.Value = (generatorArgs.TreeSpacingMax + generatorArgs.TreeSpacingMin) / 2m; + nTreeSpacingVariation.Value = (generatorArgs.TreeSpacingMax - generatorArgs.TreeSpacingMin) / 2m; + + xCaves.Checked = generatorArgs.AddCaves; + xCaveLava.Checked = generatorArgs.AddCaveLava; + xCaveWater.Checked = generatorArgs.AddCaveWater; + xOre.Checked = generatorArgs.AddOre; + sCaveDensity.Value = (int)(generatorArgs.CaveDensity * 100); + sCaveSize.Value = (int)(generatorArgs.CaveSize * 100); + + xWaterLevel.Checked = generatorArgs.CustomWaterLevel; + nWaterLevel.Maximum = generatorArgs.MapHeight; + nWaterLevel.Value = Math.Min( generatorArgs.WaterLevel, generatorArgs.MapHeight ); + + xAddSnow.Checked = generatorArgs.AddSnow; + + nSnowAltitude.Value = generatorArgs.SnowAltitude - (generatorArgs.CustomWaterLevel ? generatorArgs.WaterLevel : generatorArgs.MapHeight / 2); + nSnowTransition.Value = generatorArgs.SnowTransition; + + xAddCliffs.Checked = generatorArgs.AddCliffs; + sCliffThreshold.Value = (int)(generatorArgs.CliffThreshold * 100); + xCliffSmoothing.Checked = generatorArgs.CliffSmoothing; + + xAddBeaches.Checked = generatorArgs.AddBeaches; + nBeachExtent.Value = generatorArgs.BeachExtent; + nBeachHeight.Value = generatorArgs.BeachHeight; + + sAboveFunc.Value = ExponentToTrackBar( sAboveFunc, generatorArgs.AboveFuncExponent ); + sBelowFunc.Value = ExponentToTrackBar( sBelowFunc, generatorArgs.BelowFuncExponent ); + + nMaxHeightVariation.Value = generatorArgs.MaxHeightVariation; + nMaxDepthVariation.Value = generatorArgs.MaxDepthVariation; + } + + void SaveGeneratorArgs() { + generatorArgs = new MapGeneratorArgs { + DetailScale = sDetailScale.Value, + FeatureScale = sFeatureScale.Value, + MapHeight = (int)nMapHeight.Value, + MapWidth = (int)nMapWidth.Value, + MapLength = (int)nMapLength.Value, + LayeredHeightmap = xLayeredHeightmap.Checked, + MarbledHeightmap = xMarbledMode.Checked, + MatchWaterCoverage = xMatchWaterCoverage.Checked, + MaxDepth = (int)nMaxDepth.Value, + MaxHeight = (int)nMaxHeight.Value, + AddTrees = xAddTrees.Checked, + AddGiantTrees = xGiantTrees.Checked, + Roughness = sRoughness.Value / 100f, + Seed = (int)nSeed.Value, + Theme = (MapGenTheme)cTheme.SelectedIndex, + TreeHeightMax = (int)(nTreeHeight.Value + nTreeHeightVariation.Value), + TreeHeightMin = (int)(nTreeHeight.Value - nTreeHeightVariation.Value), + TreeSpacingMax = (int)(nTreeSpacing.Value + nTreeSpacingVariation.Value), + TreeSpacingMin = (int)(nTreeSpacing.Value - nTreeSpacingVariation.Value), + UseBias = (sBias.Value != 0), + DelayBias = xDelayBias.Checked, + WaterCoverage = sWaterCoverage.Value / 100f, + Bias = sBias.Value / 100f, + MidPoint = cMidpoint.SelectedIndex - 1, + RaisedCorners = (int)nRaisedCorners.Value, + LoweredCorners = (int)nLoweredCorners.Value, + InvertHeightmap = xInvert.Checked, + AddWater = xWater.Checked, + AddCaves = xCaves.Checked, + AddOre = xOre.Checked, + AddCaveLava = xCaveLava.Checked, + AddCaveWater = xCaveWater.Checked, + CaveDensity = sCaveDensity.Value / 100f, + CaveSize = sCaveSize.Value / 100f, + CustomWaterLevel = xWaterLevel.Checked, + WaterLevel = (int)(xWaterLevel.Checked ? nWaterLevel.Value : nMapHeight.Value / 2), + AddSnow = xAddSnow.Checked, + SnowTransition = (int)nSnowTransition.Value, + SnowAltitude = (int)(nSnowAltitude.Value + (xWaterLevel.Checked ? nWaterLevel.Value : nMapHeight.Value / 2)), + AddCliffs = xAddCliffs.Checked, + CliffThreshold = sCliffThreshold.Value / 100f, + CliffSmoothing = xCliffSmoothing.Checked, + AddBeaches = xAddBeaches.Checked, + BeachExtent = (int)nBeachExtent.Value, + BeachHeight = (int)nBeachHeight.Value, + AboveFuncExponent = TrackBarToExponent( sAboveFunc ), + BelowFuncExponent = TrackBarToExponent( sBelowFunc ), + MaxHeightVariation = (int)nMaxHeightVariation.Value, + MaxDepthVariation = (int)nMaxDepthVariation.Value + }; + } + + + readonly SaveFileDialog saveTemplateDialog = new SaveFileDialog(); + private void bSaveTemplate_Click( object sender, EventArgs e ) { + if( saveTemplateDialog.ShowDialog() == DialogResult.OK && !String.IsNullOrEmpty( saveTemplateDialog.FileName ) ) { + try { + SaveGeneratorArgs(); + generatorArgs.Save( saveTemplateDialog.FileName ); + } catch( Exception ex ) { + MessageBox.Show( "Could not open template file: " + ex ); + } + } + } + + private void cTemplates_SelectedIndexChanged( object sender, EventArgs e ) { + generatorArgs = MapGenerator.MakeTemplate( (MapGenTemplate)cTemplates.SelectedIndex ); + LoadGeneratorArgs(); + bGenerate.PerformClick(); + } + + private void xCaves_CheckedChanged( object sender, EventArgs e ) { + gCaves.Visible = xCaves.Checked && xAdvanced.Checked; + } + + private void sCaveDensity_ValueChanged( object sender, EventArgs e ) { + lCaveDensityDisplay.Text = sCaveDensity.Value + "%"; + } + + private void sCaveSize_ValueChanged( object sender, EventArgs e ) { + lCaveSizeDisplay.Text = sCaveSize.Value + "%"; + } + + private void xWaterLevel_CheckedChanged( object sender, EventArgs e ) { + nWaterLevel.Enabled = xWaterLevel.Checked; + } + + private void nHeight_ValueChanged( object sender, EventArgs e ) { + nWaterLevel.Value = Math.Min( nWaterLevel.Value, nMapHeight.Value ); + nWaterLevel.Maximum = nMapHeight.Value; + } + + private void xAddTrees_CheckedChanged( object sender, EventArgs e ) { + gTrees.Visible = xAddTrees.Checked; + } + + private void xWater_CheckedChanged( object sender, EventArgs e ) { + xAddBeaches.Enabled = xWater.Checked; + } + + private void sAboveFunc_ValueChanged( object sender, EventArgs e ) { + lAboveFuncUnits.Text = (1 / TrackBarToExponent( sAboveFunc )).ToString( "0.0%" ); + } + + private void sBelowFunc_ValueChanged( object sender, EventArgs e ) { + lBelowFuncUnits.Text = (1 / TrackBarToExponent( sBelowFunc )).ToString( "0.0%" ); + } + + static float TrackBarToExponent( TrackBar bar ) { + if( bar.Value >= bar.Maximum / 2 ) { + float normalized = (bar.Value - bar.Maximum / 2f) / (bar.Maximum / 2f); + return 1 + normalized * normalized * 3; + } else { + float normalized = (bar.Value / (bar.Maximum / 2f)); + return normalized * .75f + .25f; + } + } + + static int ExponentToTrackBar( TrackBar bar, float val ) { + if( val >= 1 ) { + float normalized = (float)Math.Sqrt( (val - 1) / 3f ); + return (int)(bar.Maximum / 2f + normalized * (bar.Maximum / 2f)); + } else { + float normalized = (val - .25f) / .75f; + return (int)(normalized * bar.Maximum / 2f); + } + } + + private void sCliffThreshold_ValueChanged( object sender, EventArgs e ) { + lCliffThresholdUnits.Text = sCliffThreshold.Value + "%"; + } + + private void xAddSnow_CheckedChanged( object sender, EventArgs e ) { + gSnow.Visible = xAdvanced.Checked && xAddSnow.Checked; + } + + private void xAddCliffs_CheckedChanged( object sender, EventArgs e ) { + gCliffs.Visible = xAdvanced.Checked && xAddCliffs.Checked; + } + + private void xAddBeaches_CheckedChanged( object sender, EventArgs e ) { + gBeaches.Visible = xAdvanced.Checked && xAddBeaches.Checked; + } + + private void xBlockDB_CheckStateChanged( object sender, EventArgs e ) { + switch( xBlockDB.CheckState ) { + case CheckState.Indeterminate: + World.BlockDBEnabled = YesNoAuto.Auto; + xBlockDB.Text = "BlockDB (Auto)"; + break; + case CheckState.Checked: + World.BlockDBEnabled = YesNoAuto.Yes; + xBlockDB.Text = "BlockDB (On)"; + break; + case CheckState.Unchecked: + World.BlockDBEnabled = YesNoAuto.No; + xBlockDB.Text = "BlockDB (Off)"; + break; + } + } + } +} \ No newline at end of file diff --git a/ConfigGUI/AddWorldPopup.resx b/ConfigGUI/AddWorldPopup.resx new file mode 100644 index 0000000..0286588 --- /dev/null +++ b/ConfigGUI/AddWorldPopup.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 128, 17 + + + 237, 17 + + + 35 + + \ No newline at end of file diff --git a/ConfigGUI/ChatBackground.png b/ConfigGUI/ChatBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..9c13d06e2b01212ba996438092b499cf7b06fd5f GIT binary patch literal 4199 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 eLRdSTLMYF)jhb^hsnYCw literal 0 HcmV?d00001 diff --git a/ConfigGUI/ChatPreview.Designer.cs b/ConfigGUI/ChatPreview.Designer.cs new file mode 100644 index 0000000..69f8819 --- /dev/null +++ b/ConfigGUI/ChatPreview.Designer.cs @@ -0,0 +1,40 @@ +namespace fCraft.ConfigGUI { + sealed partial class ChatPreview { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.SuspendLayout(); + // + // ChatPreview + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Name = "ChatPreview"; + this.Size = new System.Drawing.Size( 283, 92 ); + this.ResumeLayout( false ); + + } + + #endregion + } +} diff --git a/ConfigGUI/ChatPreview.cs b/ConfigGUI/ChatPreview.cs new file mode 100644 index 0000000..01a0a51 --- /dev/null +++ b/ConfigGUI/ChatPreview.cs @@ -0,0 +1,126 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using fCraft.ConfigGUI.Properties; + + +namespace fCraft.ConfigGUI { + sealed partial class ChatPreview : UserControl { + + struct ColorPair { + public ColorPair( int r, int g, int b, int sr, int sg, int sb ) { + Foreground = new SolidBrush( System.Drawing.Color.FromArgb( r, g, b ) ); + Shadow = new SolidBrush( System.Drawing.Color.FromArgb( sr, sg, sb ) ); + } + public readonly Brush Foreground, Shadow; + } + + static readonly PrivateFontCollection Fonts; + static readonly Font MinecraftFont; + static readonly ColorPair[] ColorPairs; + + unsafe static ChatPreview() { + Fonts = new PrivateFontCollection(); + fixed( byte* fontPointer = Resources.MinecraftFont ) { + Fonts.AddMemoryFont( (IntPtr)fontPointer, Resources.MinecraftFont.Length ); + } + MinecraftFont = new Font( Fonts.Families[0], 12, FontStyle.Regular ); + ColorPairs = new[]{ + new ColorPair(0,0,0,0,0,0), + new ColorPair(0,0,191,0,0,47), + new ColorPair(0,191,0,0,47,0), + new ColorPair(0,191,191,0,47,47), + new ColorPair(191,0,0,47,0,0), + new ColorPair(191,0,191,47,0,47), + new ColorPair(191,191,0,47,47,0), + new ColorPair(191,191,191,47,47,47), + + new ColorPair(64,64,64,16,16,16), + new ColorPair(64,64,255,16,16,63), + new ColorPair(64,255,64,16,63,16), + new ColorPair(64,255,255,16,63,63), + new ColorPair(255,64,64,63,16,16), + new ColorPair(255,64,255,63,16,63), + new ColorPair(255,255,64,63,63,16), + new ColorPair(255,255,255,63,63,63) + }; + } + + + public ChatPreview() { + InitializeComponent(); + DoubleBuffered = true; + } + + + sealed class TextSegment { + public string Text; + public ColorPair Color; + public int X, Y; + + public void Draw( Graphics g ) { + g.DrawString( Text, MinecraftFont, Color.Shadow, X + 2, Y + 2 ); + g.DrawString( Text, MinecraftFont, Color.Foreground, X, Y ); + } + } + + static readonly Regex SplitByColorRegex = new Regex( "(&[0-9a-zA-Z])", RegexOptions.Compiled ); + TextSegment[] segments; + + public void SetText( string[] lines ) { + List newSegments = new List(); + using( Bitmap b = new Bitmap( 1, 1 ) ) { + using( Graphics g = Graphics.FromImage( b ) ) { // graphics for string mesaurement + g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; + + int y = 5; + for( int i = 0; i < lines.Length; i++ ) { + if( lines[i] == null || lines[i].Length == 0 ) continue; + int x = 5; + string[] plainTextSegments = SplitByColorRegex.Split( lines[i] ); + + int color = Color.ParseToIndex( Color.White ); + + for( int j = 0; j < plainTextSegments.Length; j++ ) { + if( plainTextSegments[j].Length == 0 ) continue; + if( plainTextSegments[j][0] == '&' ) { + color = Color.ParseToIndex( plainTextSegments[j] ); + } else { + newSegments.Add( new TextSegment { + Color = ColorPairs[color], + Text = plainTextSegments[j], + X = x, + Y = y + } ); + x += (int)g.MeasureString( plainTextSegments[j], MinecraftFont ).Width; + } + } + y += 20; + } + + } + } + segments = newSegments.ToArray(); + Invalidate(); + } + + + protected override void OnPaint( PaintEventArgs e ) { + e.Graphics.DrawImageUnscaledAndClipped( Resources.ChatBackground, e.ClipRectangle ); + + e.Graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixel; + + if( segments != null && segments.Length > 0 ) { + for( int i = 0; i < segments.Length; i++ ) { + segments[i].Draw( e.Graphics ); + } + } + + base.OnPaint( e ); + } + } +} \ No newline at end of file diff --git a/ConfigGUI/ChatPreview.resx b/ConfigGUI/ChatPreview.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ConfigGUI/ChatPreview.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/ColorPicker.Designer.cs b/ConfigGUI/ColorPicker.Designer.cs new file mode 100644 index 0000000..1eb25ad --- /dev/null +++ b/ConfigGUI/ColorPicker.Designer.cs @@ -0,0 +1,305 @@ +namespace fCraft.ConfigGUI { + partial class ColorPicker { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof( ColorPicker ) ); + this.b0 = new System.Windows.Forms.Button(); + this.b8 = new System.Windows.Forms.Button(); + this.b1 = new System.Windows.Forms.Button(); + this.b9 = new System.Windows.Forms.Button(); + this.b2 = new System.Windows.Forms.Button(); + this.ba = new System.Windows.Forms.Button(); + this.b3 = new System.Windows.Forms.Button(); + this.bb = new System.Windows.Forms.Button(); + this.b4 = new System.Windows.Forms.Button(); + this.bc = new System.Windows.Forms.Button(); + this.b5 = new System.Windows.Forms.Button(); + this.bd = new System.Windows.Forms.Button(); + this.bf = new System.Windows.Forms.Button(); + this.b7 = new System.Windows.Forms.Button(); + this.be = new System.Windows.Forms.Button(); + this.b6 = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // b0 + // + this.b0.BackColor = System.Drawing.Color.Black; + this.b0.ForeColor = System.Drawing.Color.White; + this.b0.Location = new System.Drawing.Point( 9, 7 ); + this.b0.Margin = new System.Windows.Forms.Padding( 0 ); + this.b0.Name = "b0"; + this.b0.Size = new System.Drawing.Size( 130, 40 ); + this.b0.TabIndex = 0; + this.b0.Text = "Black - 0"; + this.b0.UseVisualStyleBackColor = false; + // + // b8 + // + this.b8.BackColor = System.Drawing.Color.DimGray; + this.b8.ForeColor = System.Drawing.Color.White; + this.b8.Location = new System.Drawing.Point( 139, 7 ); + this.b8.Margin = new System.Windows.Forms.Padding( 0 ); + this.b8.Name = "b8"; + this.b8.Size = new System.Drawing.Size( 130, 40 ); + this.b8.TabIndex = 8; + this.b8.Text = "Gray - 8"; + this.b8.UseVisualStyleBackColor = false; + // + // b1 + // + this.b1.BackColor = System.Drawing.Color.Navy; + this.b1.ForeColor = System.Drawing.Color.White; + this.b1.Location = new System.Drawing.Point( 9, 47 ); + this.b1.Margin = new System.Windows.Forms.Padding( 0 ); + this.b1.Name = "b1"; + this.b1.Size = new System.Drawing.Size( 130, 40 ); + this.b1.TabIndex = 1; + this.b1.Text = "Navy - 1"; + this.b1.UseVisualStyleBackColor = false; + // + // b9 + // + this.b9.BackColor = System.Drawing.Color.RoyalBlue; + this.b9.ForeColor = System.Drawing.Color.White; + this.b9.Location = new System.Drawing.Point( 139, 47 ); + this.b9.Margin = new System.Windows.Forms.Padding( 0 ); + this.b9.Name = "b9"; + this.b9.Size = new System.Drawing.Size( 130, 40 ); + this.b9.TabIndex = 9; + this.b9.Text = "Blue - 9"; + this.b9.UseVisualStyleBackColor = false; + // + // b2 + // + this.b2.BackColor = System.Drawing.Color.Green; + this.b2.ForeColor = System.Drawing.Color.White; + this.b2.Location = new System.Drawing.Point( 9, 87 ); + this.b2.Margin = new System.Windows.Forms.Padding( 0 ); + this.b2.Name = "b2"; + this.b2.Size = new System.Drawing.Size( 130, 40 ); + this.b2.TabIndex = 2; + this.b2.Text = "Green - 2"; + this.b2.UseVisualStyleBackColor = false; + // + // ba + // + this.ba.BackColor = System.Drawing.Color.Lime; + this.ba.ForeColor = System.Drawing.Color.Black; + this.ba.Location = new System.Drawing.Point( 139, 87 ); + this.ba.Margin = new System.Windows.Forms.Padding( 0 ); + this.ba.Name = "ba"; + this.ba.Size = new System.Drawing.Size( 130, 40 ); + this.ba.TabIndex = 10; + this.ba.Text = "Lime - a"; + this.ba.UseVisualStyleBackColor = false; + // + // b3 + // + this.b3.BackColor = System.Drawing.Color.Teal; + this.b3.ForeColor = System.Drawing.Color.White; + this.b3.Location = new System.Drawing.Point( 9, 127 ); + this.b3.Margin = new System.Windows.Forms.Padding( 0 ); + this.b3.Name = "b3"; + this.b3.Size = new System.Drawing.Size( 130, 40 ); + this.b3.TabIndex = 3; + this.b3.Text = "Teal - 3"; + this.b3.UseVisualStyleBackColor = false; + // + // bb + // + this.bb.BackColor = System.Drawing.Color.Aqua; + this.bb.ForeColor = System.Drawing.Color.Black; + this.bb.Location = new System.Drawing.Point( 139, 127 ); + this.bb.Margin = new System.Windows.Forms.Padding( 0 ); + this.bb.Name = "bb"; + this.bb.Size = new System.Drawing.Size( 130, 40 ); + this.bb.TabIndex = 11; + this.bb.Text = "Aqua - b"; + this.bb.UseVisualStyleBackColor = false; + // + // b4 + // + this.b4.BackColor = System.Drawing.Color.Maroon; + this.b4.ForeColor = System.Drawing.Color.White; + this.b4.Location = new System.Drawing.Point( 9, 167 ); + this.b4.Margin = new System.Windows.Forms.Padding( 0 ); + this.b4.Name = "b4"; + this.b4.Size = new System.Drawing.Size( 130, 40 ); + this.b4.TabIndex = 4; + this.b4.Text = "Maroon - 4"; + this.b4.UseVisualStyleBackColor = false; + // + // bc + // + this.bc.BackColor = System.Drawing.Color.Red; + this.bc.ForeColor = System.Drawing.Color.White; + this.bc.Location = new System.Drawing.Point( 139, 167 ); + this.bc.Margin = new System.Windows.Forms.Padding( 0 ); + this.bc.Name = "bc"; + this.bc.Size = new System.Drawing.Size( 130, 40 ); + this.bc.TabIndex = 12; + this.bc.Text = "Red - c"; + this.bc.UseVisualStyleBackColor = false; + // + // b5 + // + this.b5.BackColor = System.Drawing.Color.Purple; + this.b5.ForeColor = System.Drawing.Color.White; + this.b5.Location = new System.Drawing.Point( 9, 207 ); + this.b5.Margin = new System.Windows.Forms.Padding( 0 ); + this.b5.Name = "b5"; + this.b5.Size = new System.Drawing.Size( 130, 40 ); + this.b5.TabIndex = 5; + this.b5.Text = "Purple - 5"; + this.b5.UseVisualStyleBackColor = false; + // + // bd + // + this.bd.BackColor = System.Drawing.Color.Magenta; + this.bd.ForeColor = System.Drawing.Color.Black; + this.bd.Location = new System.Drawing.Point( 139, 207 ); + this.bd.Margin = new System.Windows.Forms.Padding( 0 ); + this.bd.Name = "bd"; + this.bd.Size = new System.Drawing.Size( 130, 40 ); + this.bd.TabIndex = 13; + this.bd.Text = "Magenta - d"; + this.bd.UseVisualStyleBackColor = false; + // + // bf + // + this.bf.BackColor = System.Drawing.Color.White; + this.bf.ForeColor = System.Drawing.Color.Black; + this.bf.Location = new System.Drawing.Point( 139, 287 ); + this.bf.Margin = new System.Windows.Forms.Padding( 0 ); + this.bf.Name = "bf"; + this.bf.Size = new System.Drawing.Size( 130, 40 ); + this.bf.TabIndex = 15; + this.bf.Text = "White - f"; + this.bf.UseVisualStyleBackColor = false; + // + // b7 + // + this.b7.BackColor = System.Drawing.Color.Silver; + this.b7.ForeColor = System.Drawing.Color.Black; + this.b7.Location = new System.Drawing.Point( 9, 287 ); + this.b7.Margin = new System.Windows.Forms.Padding( 0 ); + this.b7.Name = "b7"; + this.b7.Size = new System.Drawing.Size( 130, 40 ); + this.b7.TabIndex = 7; + this.b7.Text = "Silver - 7"; + this.b7.UseVisualStyleBackColor = false; + // + // be + // + this.be.BackColor = System.Drawing.Color.Yellow; + this.be.ForeColor = System.Drawing.Color.Black; + this.be.Location = new System.Drawing.Point( 139, 247 ); + this.be.Margin = new System.Windows.Forms.Padding( 0 ); + this.be.Name = "be"; + this.be.Size = new System.Drawing.Size( 130, 40 ); + this.be.TabIndex = 14; + this.be.Text = "Yellow - e"; + this.be.UseVisualStyleBackColor = false; + // + // b6 + // + this.b6.BackColor = System.Drawing.Color.Olive; + this.b6.ForeColor = System.Drawing.Color.White; + this.b6.Location = new System.Drawing.Point( 9, 247 ); + this.b6.Margin = new System.Windows.Forms.Padding( 0 ); + this.b6.Name = "b6"; + this.b6.Size = new System.Drawing.Size( 130, 40 ); + this.b6.TabIndex = 6; + this.b6.Text = "Olive - 6"; + this.b6.UseVisualStyleBackColor = false; + // + // bCancel + // + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bCancel.Location = new System.Drawing.Point( 88, 330 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 25 ); + this.bCancel.TabIndex = 16; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + // + // ColorPicker + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 8F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 278, 367 ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bf ); + this.Controls.Add( this.b7 ); + this.Controls.Add( this.be ); + this.Controls.Add( this.b6 ); + this.Controls.Add( this.bd ); + this.Controls.Add( this.b5 ); + this.Controls.Add( this.bc ); + this.Controls.Add( this.b4 ); + this.Controls.Add( this.bb ); + this.Controls.Add( this.b3 ); + this.Controls.Add( this.ba ); + this.Controls.Add( this.b2 ); + this.Controls.Add( this.b9 ); + this.Controls.Add( this.b1 ); + this.Controls.Add( this.b8 ); + this.Controls.Add( this.b0 ); + this.Font = new System.Drawing.Font( "Lucida Console", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject( "$this.Icon" ))); + this.Margin = new System.Windows.Forms.Padding( 4, 3, 4, 3 ); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ColorPicker"; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "ColorPicker"; + this.ResumeLayout( false ); + + } + + #endregion + + private System.Windows.Forms.Button b0; + private System.Windows.Forms.Button b8; + private System.Windows.Forms.Button b1; + private System.Windows.Forms.Button b9; + private System.Windows.Forms.Button b2; + private System.Windows.Forms.Button ba; + private System.Windows.Forms.Button b3; + private System.Windows.Forms.Button bb; + private System.Windows.Forms.Button b4; + private System.Windows.Forms.Button bc; + private System.Windows.Forms.Button b5; + private System.Windows.Forms.Button bd; + private System.Windows.Forms.Button bf; + private System.Windows.Forms.Button b7; + private System.Windows.Forms.Button be; + private System.Windows.Forms.Button b6; + private System.Windows.Forms.Button bCancel; + } +} \ No newline at end of file diff --git a/ConfigGUI/ColorPicker.cs b/ConfigGUI/ColorPicker.cs new file mode 100644 index 0000000..be19486 --- /dev/null +++ b/ConfigGUI/ColorPicker.cs @@ -0,0 +1,65 @@ +// Copyright 2009-2012 Matvei Stefarov +using System.Collections.Generic; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + internal sealed partial class ColorPicker : Form { + public static readonly Dictionary ColorPairs = new Dictionary(); + public int ColorIndex; + + + static ColorPicker() { + ColorPairs.Add( 0, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Black ) ); + ColorPairs.Add( 8, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.DimGray ) ); + ColorPairs.Add( 1, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Navy ) ); + ColorPairs.Add( 9, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.RoyalBlue ) ); + ColorPairs.Add( 2, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Green ) ); + ColorPairs.Add( 10, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.Lime ) ); + ColorPairs.Add( 3, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Teal ) ); + ColorPairs.Add( 11, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.Aqua ) ); + ColorPairs.Add( 4, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Maroon ) ); + ColorPairs.Add( 12, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Red ) ); + ColorPairs.Add( 5, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Purple ) ); + ColorPairs.Add( 13, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.Magenta ) ); + ColorPairs.Add( 6, new ColorPair( System.Drawing.Color.White, System.Drawing.Color.Olive ) ); + ColorPairs.Add( 14, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.Yellow ) ); + ColorPairs.Add( 7, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.Silver ) ); + ColorPairs.Add( 15, new ColorPair( System.Drawing.Color.Black, System.Drawing.Color.White ) ); + } + + + public ColorPicker( string title, int oldColorIndex ) { + InitializeComponent(); + Text = title; + ColorIndex = oldColorIndex; + StartPosition = FormStartPosition.CenterParent; + + b0.Click += delegate { ColorIndex = 0; DialogResult = DialogResult.OK; Close(); }; + b1.Click += delegate { ColorIndex = 1; DialogResult = DialogResult.OK; Close(); }; + b2.Click += delegate { ColorIndex = 2; DialogResult = DialogResult.OK; Close(); }; + b3.Click += delegate { ColorIndex = 3; DialogResult = DialogResult.OK; Close(); }; + b4.Click += delegate { ColorIndex = 4; DialogResult = DialogResult.OK; Close(); }; + b5.Click += delegate { ColorIndex = 5; DialogResult = DialogResult.OK; Close(); }; + b6.Click += delegate { ColorIndex = 6; DialogResult = DialogResult.OK; Close(); }; + b7.Click += delegate { ColorIndex = 7; DialogResult = DialogResult.OK; Close(); }; + b8.Click += delegate { ColorIndex = 8; DialogResult = DialogResult.OK; Close(); }; + b9.Click += delegate { ColorIndex = 9; DialogResult = DialogResult.OK; Close(); }; + ba.Click += delegate { ColorIndex = 10; DialogResult = DialogResult.OK; Close(); }; + bb.Click += delegate { ColorIndex = 11; DialogResult = DialogResult.OK; Close(); }; + bc.Click += delegate { ColorIndex = 12; DialogResult = DialogResult.OK; Close(); }; + bd.Click += delegate { ColorIndex = 13; DialogResult = DialogResult.OK; Close(); }; + be.Click += delegate { ColorIndex = 14; DialogResult = DialogResult.OK; Close(); }; + bf.Click += delegate { ColorIndex = 15; DialogResult = DialogResult.OK; Close(); }; + } + + + internal struct ColorPair { + public ColorPair( System.Drawing.Color foreground, System.Drawing.Color background ) { + Foreground = foreground; + Background = background; + } + public System.Drawing.Color Foreground; + public System.Drawing.Color Background; + } + } +} \ No newline at end of file diff --git a/ConfigGUI/ColorPicker.resx b/ConfigGUI/ColorPicker.resx new file mode 100644 index 0000000..d96dd41 --- /dev/null +++ b/ConfigGUI/ColorPicker.resx @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////A+ + Afnv3//7////99///+///z/f/++fn///zz///+Z//+///////P//9/9///uHv/wAP8////////f////7 + //////////////v///////n/3H///+H8+///fgf/79///5/////+////8b///+/////9/////////ygA + AAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv8/QEAAAAAAAAAAMzQ + 5gXO1OYSzdPlDQAAAAAAAAAAzNXtAgAAAAAAAAAAAAAAAAAAAADM0+YFzNPmBs3T5gbN0+YGzdPmBs3T + 5gbN0+YGzdPlBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKS1 + 4yMAHaOyABqe4gAbn+4AGZnpABaN0QAWiGoAAAAAAB2mAQAmwgMAAAAAACrAeQAboeYAGZnjABia5AAY + muQAGJvkABea5AAYm+EAF5jkABePjwAAAAAAAAAAAAAAAAAkwwIAAAAAAB6pnAAViZ8AGJUJ+fv9AgAA + AACmt+gkACa54wU0xv8IPM//GEnT/xhI0v8LPMn/ASyr/wMolJEAAAAAAjG5BAAAAAAqV9iaCj3R/wU5 + 0P8XR9P/HkzS/xtK0v8bStL/HEvS/xdH0v8DM73FAAAAAAAAAAADNckDAAAAAAM0w3wDNcf/AjK6/wIu + rTz9/f4CAAAAAAAuyrICNs//Cj/U8lt+4IF5leY6gpznZUlw3tcRRNT+ACus/wAol1EAAAAAAAAAACpY + 134MQdH8AjTG+RxM0YY8Z95kRGzcZ0Vt3GVFbdxlQWrcZh5P1yoAAAAAADTHBAAAAAAAMr9WADTG/QM5 + 0P8HPtOvBDrPBwAAAACUq+suADLQ9QMxyvw3YdR2AAAAAAAAAAAAAAAAW3/gGita2eUGPMr9ACufxgAq + pgUAAAAAN2TbUB1Q2P4AMLn9ACmgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU90gIAAAAAADXEPAA1 + xvcBOdD/BT3S0Ak/0g4AAAAAAAAAAERu3mQPRtb/ACnE/HmU2xz+/v4D9/j8BPz8/gUAAAAALFvahxFH + 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 + AAAAAAAAAAAAAAB6eXnPzs53d3cAAAATExOqqanb2dnq6elYWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCgouLi4FBQUAAAABAQFubm7l4+Pd3d00 + NDQAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABBQUEAAAASExM7OzsyMjIFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA/Af8AB/7AAD7Df/AH/UAAPb2////6wAA//v9f//X + AAD7/X///78AAP///r//bwAA5/7/3//fAAD//v9v/r8AAP///7f9fwAA////2/r/AAD//3/t9/8AAP// + v/b9/wAA//9/+/v/AAD//9//X/8AAP//f/2//wAA///f/9//AAD//7/76/8AAP//7+b1/wAA/4DQHft/ + AAD/gHgD/r8AAP//j///PwAA///f////AAD///////8AAP//3////wAA///3////AAD//+////8AAP// + 9////wAA///v////AAD///v///8AAP5/6/8//wAA//P/////AADvj/X+//8AANH//v+//wAA/h/7ff// + AAD17/2Df/8AAP/9fgD//wAA2/T/////AAC7//////8AAM3r/////wAA9t//////AAD/J/////8AAPz9 + /////wAA8///////AAD/n/////8AAP+//////wAA////////AAAoAAAAMAAAAGAAAAABACAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8CAAAAAAAAAAAAAAAA8vP5AvLz + +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 + AAAAAAAAQUFBAgAAAAASExMqOzs7aTIyMlwFBQUVAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + AAD///////8AAPwH/AAf+wAA+w3/wB/1AAD29v///+sAAP/7/X//1wAA+/1///+/AAD///6//28AAOf+ + /9//3wAA//7/b/6/AAD///+3/X8AAP///9v6/wAA//9/7ff/AAD//7/2/f8AAP//f/v7/wAA///f/1// + AAD//3/9v/8AAP//3//f/wAA//+/++v/AAD//+/m9f8AAP+A0B37fwAA/4B4A/6/AAD//4///z8AAP// + 3////wAA////////AAD//9////8AAP//9////wAA///v////AAD///f///8AAP//7////wAA///7//// + AAD+f+v/P/8AAP/z/////wAA74/1/v//AADR//7/v/8AAP4f+33//wAA9e/9g3//AAD//X4A//8AANv0 + /////wAAu///////AADN6/////8AAPbf/////wAA/yf/////AAD8/f////8AAPP//////wAA/5////// + AAD/v/////8AAP///////wAAKAAAAEAAAACAAAAAAQAEAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAA + AADQyMgAAFDoAAA4yAAAMKAAQEhYADBo4ACYmJgA+Pj4AABg+AAYWOAAeHh4APDw8AA4gPAAKCgoAAhI + yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABERERVVAAAAAAABERERERERERFVAAA + AAAABEQAAAAABERERERFVAAAAAAzREREREREREREAAAAAAA0RUAAAABEQzMzNEREQAAAAP8zMzMzMzMz + M0QAAAAAAzNERAAAAzMzP///M0REAAAA//MzP///////MwAAAAADMzNEAAAzMz/2Zmb/NERAAAD/8z/2 + ZmZmZmbzAAAAADMzMzMAAzMz//ZmZmbzREAAAP/zM/ZmZmZmZm8AAAADMzM/MAADMzM/AAAAZm80RAAA + ZvM0QAAAAAAAAAAAADMzM/MAAAMzMzAAAAAGbzNEAAAGbzREAAAAAAAAAAADMzM/MAAA//MzMAAAAAZm + 80RAAAZvM0QAAAAAAAAAADMzMzMAAACq8zMwAAAAAGbzNEAABmrzREAAAAAAAAAAMzMzMwAAAKaqMwAA + AAAAZqM0QAAAZq80RAAAAAAAAAMzM//wAAAAZmajAAAAAABmrzRAAABmajNEQAAAAAAAMzM//wAAAAAG + ZmAAAAAAAAZiNEAAAAZmozREAAAAAAMzM/LwAAAAAAAAAAAAAAAABmozRAAAAGZqM0RAAAAAMzM/8gAA + AAAAAAAAAAAAAAAGaiNEAAAABmajNEQAAAMzMiIgAAAAAAAAAAAAAAAAAAqqI0QAAAAA1mojREAAAzMi + IiAAAAAAAAAAAAAAAAAABmojRAAAAAANZqI0RAAz8iIiAAAAAAAAAAAAAAAAAAAAaiNEAAAAAADWavNE + Az8iIiAAAAAAAAAAAAAAAAAAAABmrzRAAAAAAAZqI0Qz8iIiAAAAAAAAAAAAAAAAAAAAAGaiNEAAAAAA + AGaiNDMiIiAAAAAAAAAAAAAAAAAAAAAAaqLzQAAAAAAAZqLzPyLzAAAAAAAAAAAAAAAAAAAAAABmovRA + AAAAAACqoiKqIjRAAAAAAAAAAAAAAAAAAAAAAAai9EAAAAAAACIiImaq9EQAAAAAAAAAAAAAAAAAAAAA + CqIjQAAAAABD8iIirdYvREAAAAAAAAAAAAAAA0RERERPIi9EREREREPyIiIg3Wr0REAAAAAAAAAAAAAi + 9ERERD8iL/REREQ//yIiIgAN1qI0RAAAAAAAAAAAAKov////IiIiIv8iIiIiIqIiAADdai9EQAAAAAAA + AAAApqqqqqqqIiIiKqqqqqqqqqAAAA3dYvREAAAAAAAAAABt1mZmZmZqIiqmZmZmZmaqAAAAAN3WIv/w + AAAAAAAAAA3d3d3d3daiIqbd3d3W1gAAAAAADd1qIgAAAAAAAAAAAAAAAAAA1qL/IAAAAAAAAAAAAAAA + DdagAAAAAAAAAAAAAAAAAABqov/wAAAAAAAAAAAAAAAA1gAAAAAAAAAAAAAAAAAAAGaiL0AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAADWIvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANai9AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZqL/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqoi + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGaiL/AAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAA + AADaIvQAAAAAAAAAAAAAAAAAAAAAAAAAAADrGwAAAAAAAN2i//AAAAAAAP9EQAAAAAAAAAAAAAAAAFiI + UAAOAAAA3ZIv8AAAAAACL/9AAAAAAAAAAAAAAAAAWIiwDlGwAADdmS9AAAAAAAmS//AAAAAAAAAAAAAO + 4AC4iMXnERsAAA3ZL08AAAAACZkiIAAAAAAAAAAAAFEb7hjBERERGwAADdmS9AAAAACZmSIgAAAAAAAA + AAAOcRjIiMERFxEVAAAN3ZL0QAAAACmZIiAAAAAAAAAAAA5xHIiIwREREbAAAADdmS9EAAD/IikiAAAA + AAAAAAAAAFHMHIjBERzBsAAAAN3ZkvRET/8imSIAAAAAAAAAAAAABREciBu3HIwXtVAADd2ZL///8imZ + IAAAAAAAAAAAAAAOERjHAA58iMyIEAAA3dmZIiKZmZmQAAAAAAAAAAAADufBGB4AALyIzMjAAAAN3dmZ + mZmZmQAAAAAAAAAAAADnyMEccAAAXIjMiBAAAADd3d3d3dmQAAAAAAAAAAAAAHzIwcgQAAC8iMgbUAAA + AADd3d3d0AAAAAAAAAAAAAAAfMiByIsADhiIhwAAAAAAAAAAAAAAAAAAAAAAAAAAAABREYzIgbVRyIiL + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlfIiMERHIiIHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADhyIwR + EcjIiB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwRERERHMiMGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + fBERd3ERscEVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXBEXtxERUOt1AAAAAAAAAAAAAAAAAAAAAAAAAA + AAAADnEbAFERHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7AA4RweAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAALEXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///////////wA/+AAA/+P+AA/wAAD/wfwAB/AAAP+A+AAD8AAA/4DwAAHwAAD/AOAA + AfAAAP4B4D8A8B///APgf4D4D//4B8B/gHgP//APwH/AeAf/8A/A/8B8A//gH8D/wHwB/8A/4f/gfgD/ + gH///+A/AH8A////4D+APgH////gP8AeAf///+A/4AwD////8D/wCAf////wH/gAD/////Af/AAf//// + 8B/8AD/////wH/wAH/////gf/AAP////+B/wAAf//+AAAAAEAf//wAAAAA4A///AAAAADwB//8AAAAAf + gD//wAAAAD/AH//gAAAA/+A////8B///+H////wH///8/////Af////////+B/////////4H//////// + /gP////////+A///////B/4D//////4D/wP//////gIPAf/B//9+AAcB/4H//0AABwH/gf//gAAHgP+B + //8AAAeA/wH//wAAB4B/Af//AAAHwDwD//8AAADAAAP//4AAAOAAB///AAAA8AAH//8AEAD4AA///wAY + APwAH///ABAA/wB///8AAAD//////wAAA///////AAAB//////+AAAH//////8AAAf//////wAAB//// + ///AAAH//////8AAA///////wAB////////wgH/////////A////////KAAAAEAAAACAAAAAAQAYAAAA + AAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq + pQAnmwAnmgAlkwAlkgAlkAAkjgAkjQAkjQAlkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAArqAAnmAAlkgAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAkjgAjiwAlkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqpwAmlgAmlQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAtsQArqQApogApogApowApowApogApoAAnmgAmlQAkjgAjiQAmlAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAU4zgAwvwArqgAongAongAongAongAongAongAongAongAongAongAongAongAongAo + ngAonQAnmgAmlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwuwAsrgAongAkjQAlkAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAvtQAvtgAuswAvuAAxvQAywQAywgAywgAxvwAvtwAusQAqogAnmAAlkQAl + kQAAAAAAAAAAAAAAAAAAAAAAAAAAACFP1BZG0gE2zgAxvgAxvwAxvwAxvwAxvwAxvwAxvwAxvwAxvwAx + vwAxvwAxvwAxvwAxvwAwuwAtrgAqowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzxwAywwAxvwAs + qwAonAAmlQApoAAAAAAAAAAAAAAAAAAAAAAxvQAwuwAxvwAzxwA1zgk80BdH0iRS1SRS1SFP1BdH0gQ4 + zwAxvQAtrwAonQAlkQAlkQAAAAAAAAAAAAAAAAAAAAAAAClV1iZT1RZG0gA1zgA1zgI3zgY6zxBC0RZG + 0hZG0hZG0hZG0hZG0hZG0hZG0hZG0hZG0hFC0QA0yQAvuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAywwAzxgA0yAAzxQAwugAtsAAsqwAAAAAAAAAAAAAAAAAzwwAzwgA0yAA1zAc80BRG0yJR1S9b2D1m + 2j5n20Fp2ztl2ilW1xhJ1AA1zQAvtgAqoQAnlgApnQAAAAAAAAAAAAAAAAAAACVT1iRS1hRG0wA2zwU6 + 0BBD0iVT1i9b2Ddh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2TBc2BpK1AA2zQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAywAAyvwA0xwA1zQY70AA2zwA1yQAzwgAAAAAAAAAAAAY80QA3zwE40AA2zQM50QxA + 0h9P1iVU1zBd2Ttl2z9o3Epx3k503kZu3TZh2hpL1QA2zQAvswAqnwAnlAAAAAAAAAAAAAAAAAAAACZV + 1yVU1xZI1AI50AA2zQA3zxRH1C9c2UFq3EJr3EJr3EJr3EJr3EJr3EJr3EJr3EJr3Dtl2y9c2RZI1AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAzwQAzwAA1yQA2zgU70QY80QtA0go/0gAAAAAAAAAAAAAAAAU80gU8 + 0gI60QE50QE50QE50RJG1AAAAAAAAAAAAAAAAAAAAAAAAElx3kVu3TBd2hZJ1QA0wQAtqgApmAApmgAA + AAAAAAAAAAAAACta2S5c2R1P1gY90gAzvwAvrwAvsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wQA0wQA2yAA3zQI60QQ70gpA0wk/0wAAAAAAAAAA + AAAAAAAAAAM70wQ80wA50AE60gA3ygA4zgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFr3TVi2x5Q1wI7 + 0gAzvQAtpQAolQAAAAAAAAAAAAAAAAAAAD1o3Sxb2hVJ1gA2yAAwsQArnQAtpwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1wgA1wgA2xgA3zAM70wY+0wtC1Ag/ + 0wAAAAAAAAAAAAAAAAAAABdL1hZK1g5E1AA50QA3ygA0vwA0wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ADZj3D1o3S1c2hVJ1gA2xwAwrwArnQAtpQAAAAAAAAAAAAAAADtn3DBe2hpN1wA50QAzugAsowAplgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ywA2xwA3ywA3ywA5 + 0gQ80wI70gM70wAAAAAAAAAAAAAAAAAAAAAAABtP2B5R2BRJ1gE70wA2xgA0vQA0vgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAD1p3jBf2xlN1wA50AA0vQAvrAAvqgAAAAAAAAAAAAAAADJh3Ddk3CRW2QxD + 1QA1wgAvqwAplwAplQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3 + xwA4ywA4zQA60wc/1AI80wM81AE70wAAAAAAAAAAAAAAAAAAAAAAACNW2ilb2yZY2h5S2QU/1QA4yAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtc2yha2xdN2AI91AA1wAAwqwAupQAAAAAAAAAAAAAA + AAAAAENu3zdl3R5S2QZA1QA1vwAupgAqlgAqlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAA2wwA2wwA5zAA60gU/1QhB1QpD1gtD1gAAAAAAAAAAAAAAAAAAAAAAAAAAADRk3j5r3zxq + 3zVl3hpQ2QA70gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxe3DFh3SBU2ghC1gA3wwAwrAAt + oAAAAAAAAAAAAAAAAAAAAD9s30Ju4DVl3htR2QA80wA1vAAupQAqlgAqlgAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4xQA3xAA6zQA70gQ/1gVA1gtE1wpE1wAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEJv4UBu4Dxr4Cte3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlo3ylc + 3Q9I2AA6zAAztAAuoQAAAAAAAAAAAAAAAAAAAAAAAEVx4URx4TJj3hhP2gA80QA2vAAvpQArlgArlgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4xgA4xQA7zgA80gM/1gRA1wpF2AlE1wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAADVm3ytf3hZP2gA90wA2vQAxqQAyrQAAAAAAAAAAAAAAAAAAAAAAAEd04kRx4jBi3xhQ2wA9 + 0wA3vgAwpgAslwAslwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5xgA5xgA7zAA80QE/1wJA1wNA1wdD2AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAC1i4Clf3xdR3ABA2AA6xQA0sgA0sQAAAAAAAAAAAAAAAAAAAAAAAAAA + AEl35EV04zJl4BlT3QA/1gA5wAAyqAAtmAAtmAAAAAAAAAAAAAAAAAAAAAA9zgA7yQA8ywA+0gBA2QhG + 2gJB2QBA2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACde4CZd4BdS3QJC2gA7xQA1sQAzrAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAEp45UV05DRo4hpU3gBA2AA6wQAyqQAumgAumgAAAAAAAAAAAAAAAAA+0AA+ + 0QA+0QBB2QZF2wlI2wlI2wND2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACph4S9l4h5Y3wZG3AA8yAA1 + sQAypwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEl45UV15TRp4hpV3wBB2AA6wQA0qwAwoAAypwAA + AAAAAAA9ywA/0QBA1gBC2gVG3AdH3AtK3QtK3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADds + 5CZf4Q5N3gA/zwA3tgAypAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEh45kV25TFn4xZT3wBA + 0wA6vwAzqQAwnQAAAAA9yQA+ywBB1ABC2QRG3QVH3QpK3QlK3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADds5Cxk4xZU4ABC1gA7vwA1qwA2sQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEZ35j9y5SVf4glL3gA/zAA4tQAypAA6vAA+ygBB0wBD2QRH3gVI3gtM3glL3gAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9n5Clj4xdW4QBE3AA+xwA4tQA4tQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAEN25zFp5BdW4QBE2wA9xAA2rgA6uQA/zABD2ABF3gBF3QBE2wVJ3wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChj5CZi5BZW4gFH3wBAywA6uAA5tgAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADRs5ixm5RdX4gBG3wBBzgA9wwA/ygBE1wBG3gBG3gBA + zAA9wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpm5i1o5hxc5AVM4gBB + zAA6twA4sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdZ5B1d5BRW4wBI4QJJ4QhO4hRW + 4xhZ5AZM4gBH3QA/xAA2qgA1pQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ADdw6CZk5g1S4wBDzwA7twA3qQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABH2wZN4wRM + 4gBJ4gNL4gpQ4yxo5zpy6Sdl5hJW5ABCzAA4rwAznwAynAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABda5hlc5gdP5ABH2AA+vwA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7 + tQA/wQBEzwBH2gBJ3wFL4wBJ3wBH2R9g5kJ56kZ86zBs6A1T5ABDzQA5rwA0nwAymgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vgA5 + rQA2owA1oQA1oQA1oQA1oQA1oQA1oQA7sgBEzgBK4ABL4wBK4ABBxgA3qAA0nQA1ogA2owA1oQA1oQA1 + oQA1ogA2pAA4qQA5rQA+vABF0wBJ3wBL4wNN5ABJ3QBI2w5V5QAAAEyB7EV86zRw6hJY5gBF0wA7swA1 + oAAzmwA2pQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAlT5gBJ2QBCxQA8sgA7sQA7sQA7sQA7sQA7sQA7sQA+uQBFzwBL3gBN5ABN5ABH0wBAvQA7sQA8 + swA8tAA8tAA8tAA8tAA9tQA+uABAvwBCxABG0QBL3gBN5ANP5QFO5QBM4wBM4QAAAAAAAAAAAEuB7UV9 + 7DZz6xhe5wBJ2QA+uAA4qAA1ngA2oQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAACBk6hde6QJP5wBI1gBI1ABI1ABI1ABI1ABI1ABI1ABK2gBM4gBO5gBO5wBO + 5wBO5gBL3wBJ2QBI1wBI1wBJ2ABJ2ABJ2ABJ2QBK2gBM4QBN5QBO5wJP5wlU6BRc6Q1X6ANQ5wBN5AAA + AAAAAAAAAAAAAEmB7kZ/7jh17B5j6gBL3wBBwQA6rAA2nwA3ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNn6yhr7CFm6xRd6hNc6hNc6hNc6hNc6hNc6hNc6hNc + 6hFb6gxX6QRS6ABP6ABP6ABP6AJQ6AlV6RFb6hNc6hNc6hNc6hNc6hNc6hRd6hdf6h1j6yJm6yBl6x5k + 6xxi6xBa6QAAAAAAAAAAAAAAAAAAAAAAAEZ/7kiB7j157ilr7AlV6QBFzAA9sgA4pAA4pAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFy7T577j167jZ17jR07TR07TR0 + 7TR07TR07TR07TR07TV07jBx7R5l7AlW6gBQ6QVT6RBb6idr7DJy7TR07TR07TR07TR07TR07TR07TNz + 7TZ17jd27ils7SJn7B5l7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEV/70mC7z977jBx7Q1Z6gBK2ABE + xQBExQBExwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmD8EuE + 8ER/8EJ+70J+70J+70J+70J+70J+70J+70iC8EmD8DZ27hdh7ABR6gBQ5wJS6iJo7Th370J+70J+70J+ + 70J+70J+70F97zp57zh37zl47y9x7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF970qD + 8EWA8DV17hdh7AVU6gJS6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD188TBz8Bhj7gBT7ABM1wBIzQBQ + 5QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAEyG8kWC8Stw7xRg7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClv8Cdu8Bhk + 7wJV7QBM1wBEwABGxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEOB8jR38QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAACxy8TF28SFr8Alb7wBO2QBFwABBtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAADd68ihx8g9g8ABR4ABGxABAswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADR58ypz8hVl8QBU5wBKzABCtwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC118ylz8xdn8gBX7gBO1wBG + wABHwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy9CVx + 9BZn8wBZ8gBR3ABJxgBIxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAACl19ix39hts9QVe9ABS3QBKxQBHvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dHRUVFQwLCwAAAAAAAAAAAAAAAAAA + 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 + FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGdmZsC/v83MzNnY2Pn5+ff19e/v7/7+/v///+Pi4n19fURERFdWVrCvr/Dw8Pn5+f// + //39/f///3BwbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8fH0JBQZaVlfLy8vr6+v////z8/O/v79bV1b69vcbF + xd/e3uzs7Pz8/P////z8/P39/d/f3ykpKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlJdnY2Pf39/r5+f39 + /e/w8ODf39nY2NrZ2ePj4/Lx8fj4+Pf39/n5+f7+/v///8LCwiAgHwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVE + RPb19eLh4dza2t7d3d3d3dXU1M/OztHQ0NfX19/e3unq6vPz8/////////Ly8t7d3X17ewAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAsKCq+vr/Tz89rZ2c7NzcPCwri3t62srKmoqK6trb68vNfW1rOysoiHh8LCwvLy8ubl5cHA + wExMTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAEFAQO3s7OPi4tPS0szLy56dnYB/f62srLe2tsTCwtvZ2c3MzEhIRwAA + ACMjInh4eJ+enmhoZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQjI62srNrZ2c3MzIaGhhEREQAAAGhnZ8bFxdDO + zuXk5MbFxTY1NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEREYODg3V1dQAAAAAA + AAAAAC4tLbm4uNfW1u7t7cXFxR4dHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAsLC4qJieLh4efm5pybmwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////wP/8AAH///8AP/gAAf/j/gAP/AAB/+P8Hgf8AAP/w/h/ + h/x///+H+P/D/D///w/w/+H8P//+H/H/4f4///w/8f/x/h///D/x//H+D//4f///8P8H//D////w/4P/ + 4f////D/wf/D////+P/g/4P////4//B/B/////h/+D8P////+H/8Ph/////4f/4cP/////x//hh///// + /H//AH/////8f/8Af/////w//wg//////D/+Hh/////8P/wfD///8AAAAD+H///wAAAAP8H///AAAAD/ + 4P//8AAAB//4f////h////z/////H/////////8f/////////w//////////D/////////8P//////// + /4//////////j/////////+P/////////4f/////////h///////j//H//////+P/8f/4////4cfw//D + ///ngB/j/8f//+AAP+H/x///4AA/8P+H///wAD/wfw////h8H/gAD///+H4D/AAf///w/gH+AH///8D/ + A//B////gP4P//////+A/h////////h8H///////+AAf///////8AA////////gAB///////+AHv//// + ///w4//////////j//////////P/////////9///////////////////KAAAAEAAAACAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vgMAOa1TADajbgA1 + oW4ANaFuADWhbgA1oW4ANaFuADWhbgA7sm4ARM7EAErg/wBL4/8ASuD/AEHG/wA3qPgANJ2MADWibgA2 + o4YANaGGADWhhgA1oYYANaKGADakjAA4qa8AOa3uAD68/gBF0/8ASd//AEvj/wNN5P8ASd3/AEjb8A5V + 5QUAAAAATIHsgEV86/00cOr/Eljm/wBF0/8AO7P/ADWg/QAzm4AANqUBAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlT + 5gEASdnEAELF/wA8sv8AO7H/ADux/wA7sf8AO7H/ADux/wA7sf8APrn/AEXP/wBL3v8ATeT/AE3k/wBH + 0/8AQL3/ADux/wA8s/8APLT/ADy0/wA8tP8APLT/AD21/wA+uP8AQL//AELE/wBG0f8AS97/AE3k/wNP + 5f8BTuX/AEzj/gBM4VoAAAAAAAAAAAAAAABLge1NRX3s+jZz6/8YXuf/AEnZ/wA+uP8AOKj+ADWeswA2 + oQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAgZOoCF17p6AJP5/8ASNb/AEjU/wBI1P8ASNT/AEjU/wBI1P8ASNT/AEra/wBM + 4v8ATub/AE7n/wBO5/8ATub/AEvf/wBJ2f8ASNf/AEjX/wBJ2P8ASdj/AEnY/wBJ2f8AStr/AEzh/wBN + 5f8ATuf/Ak/n/wlU6P8UXOn/DVfo/wNQ58QATeQBAAAAAAAAAAAAAAAAAAAAAEmB7ilGf+7zOHXs/x5j + 6v8AS9//AEHB/wA6rP8ANp/hADeiDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI2frAihr7OghZuv/FF3q/xNc6v8TXOr/E1zq/xNc + 6v8TXOr/E1zq/xNc6v8RW+r/DFfp/wRS6P8AT+j/AE/o/wBP6P8CUOj/CVXp/xFb6v8TXOr/E1zq/xNc + 6v8TXOr/E1zq/xRd6v8XX+r/HWPr/yJm6/8gZev/HmTr/hxi67oQWukCAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAARn/uD0iB7uA9ee7/KWvs/wlV6f8ARcz/AD2y/wA4pPQAOKQSAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFy7QE+e+7HPXru/zZ1 + 7v80dO3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/NXTu/zBx7f8eZez/CVbq/wBQ6f8FU+n/EFvq/ydr + 7P8ycu3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/M3Pt/zZ17v43du78KWzt5SJn7EYeZewBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFf+8DSYLvtz977v4wce3/DVnq/wBK2P8ARMX/AETFwQBE + xwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASYPwA0uE8GdEf/B6Qn7vekJ+73pCfu96Qn7vekJ+73pCfu96Qn7vekiC8HpJg/DKNnbu/xdh + 7P8AUer/AFDn/wJS6vciaO2XOHfvekJ+73pCfu96Qn7vekJ+73pCfu96QX3vejp572E4d+9GOXjvGi9x + 7gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF97wFKg/CGRYDw/TV1 + 7v8XYez/BVTq/QJS6k0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAPXzxGjBz8PwYY+7/AFPs/wBM1/8ASM33AFDlDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEyG8lpFgvH5K3Dv+xRg7XQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAClv8AcnbvD0GGTv/wJV7f8ATNf/AETA/QBGxCMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ4HyDzR38RYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAscvEBMXbx2SFr8P8JW+//AE7Z/wBF + wP8AQbdGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADd6 + 8pEocfL/D2Dw/wBR4P8ARsT/AECzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA0efNTKnPy/xVl8f8AVOf/AErM/wBCt9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALXXzKSlz8/4XZ/L/AFfu/wBO1/8ARsDzAEfDBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy9A8lcfT6Fmfz/wBZ + 8v8AUdz/AEnG/ABIxBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAAAABAAAAAYAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAApdfYDLHf27xts9f8FXvT/AFLd/wBKxf4AR706AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + ADVoZ2etxsXF/9DOzv/l5OT/xsXF9jY1NWMAAAAJAAAABgAAABIAAAAZAAAAEAAAAAQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAMERERM4OD + g5B1dXV/AAAAJgAAAAgAAAAJLi0tV7m4uPbX1tb/7u3t/8XFxfMeHR1GAAAABAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAYAAAAPAAAADgAAAAQAAAAAAAAAAgsLCyOKiYmy4uHh/+fm5vqcm5u7AAAAIQAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAHwAA + ADYAAAA2AAAAIAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////////////8D//AAB////AD/4AAH/4/4A + D/wAAf/j/B4H/AAD/8P4f4f8f///h/j/w/w///8P8P/h/D///h/x/+H+P//8P/H/8f4f//w/8f/x/g// + +H////D/B//w////8P+D/+H////w/8H/w/////j/4P+D////+P/wfwf////4f/g/D/////h//D4f//// + +H/+HD/////8f/4Yf/////x//wB//////H//AH/////8P/8IP/////w//h4f/////D/8Hw////AAAAA/ + h///8AAAAD/B///wAAAA/+D///AAAAf/+H////4f///8/////x//////////H/////////8P//////// + /w//////////D/////////+P/////////4//////////j/////////+H/////////4f//////4//x/// + ////j//H/+P///+HH8P/w///54Af4//H///gAD/h/8f//+AAP/D/h///8AA/8H8P///4fB/4AA////h+ + A/wAH///8P4B/gB////A/wP/wf///4D+D///////gP4f///////4fB////////gAH////////AAP//// + ///4AAf///////gB7///////8OP/////////4//////////z//////////f//////////////////w== + + + \ No newline at end of file diff --git a/ConfigGUI/ConfigGUI.csproj b/ConfigGUI/ConfigGUI.csproj new file mode 100644 index 0000000..d476a7a --- /dev/null +++ b/ConfigGUI/ConfigGUI.csproj @@ -0,0 +1,183 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3016BB07-87C4-4CA4-9DA7-492404D1FEE7} + WinExe + Properties + fCraft.ConfigGUI + ConfigGUI + v3.5 + 512 + fcraft_config.ico + Client + fCraft.ConfigGUI.Program + + + true + ..\bin\Debug\ + DEBUG;TRACE + full + AnyCPU + Auto + prompt + true + + + ..\bin\Release\ + TRACE + true + pdbonly + AnyCPU + Off + prompt + true + + + + + 3.5 + + + + + + + 3.5 + + + + + Form + + + ColorPicker.cs + + + Form + + + MainForm.cs + + + + + Form + + + TextEditorPopup.cs + + + ChatPreview.cs + + + ColorPicker.cs + + + KeywordPicker.cs + + + MainForm.cs + + + AddWorldPopup.cs + + + DeleteRankPopup.cs + + + PermissionLimitBox.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + TextEditorPopup.cs + + + UpdaterSettingsPopup.cs + + + + + {AFAEE6CC-8B4F-40CD-9623-7FFDC8E52222} + fCraftGUI + + + {7FBE7809-6F77-415C-ABEB-A3F627E817B0} + fCraft + + + + + + + + + + + UserControl + + + ChatPreview.cs + + + Form + + + KeywordPicker.cs + + + Form + + + Form + + + Form + + + AddWorldPopup.cs + + + Component + + + Form + + + DeleteRankPopup.cs + + + UserControl + + + PermissionLimitBox.cs + + + True + True + Resources.resx + + + + Form + + + UpdaterSettingsPopup.cs + + + + + + \ No newline at end of file diff --git a/ConfigGUI/CustomPictureBox.cs b/ConfigGUI/CustomPictureBox.cs new file mode 100644 index 0000000..a6bba2a --- /dev/null +++ b/ConfigGUI/CustomPictureBox.cs @@ -0,0 +1,20 @@ +// Copyright 2009-2012 Matvei Stefarov +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + sealed class CustomPictureBox : PictureBox { + protected override void OnPaint( PaintEventArgs pe ) { + if( Image != null ) { + pe.Graphics.SmoothingMode = SmoothingMode.HighQuality; + pe.Graphics.CompositingQuality = CompositingQuality.HighQuality; + if( Image.Height * 3 > Height || Image.Width * 3 > Width ) { + pe.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + } else { + pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + } + } + base.OnPaint( pe ); + } + } +} \ No newline at end of file diff --git a/ConfigGUI/DeleteRankPopup.Designer.cs b/ConfigGUI/DeleteRankPopup.Designer.cs new file mode 100644 index 0000000..f90e305 --- /dev/null +++ b/ConfigGUI/DeleteRankPopup.Designer.cs @@ -0,0 +1,115 @@ +namespace fCraft.ConfigGUI { + partial class DeleteRankPopup { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof( DeleteRankPopup ) ); + this.lWarning = new System.Windows.Forms.Label(); + this.lSubstitute = new System.Windows.Forms.Label(); + this.cSubstitute = new System.Windows.Forms.ComboBox(); + this.bDelete = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lWarning + // + this.lWarning.AutoSize = true; + this.lWarning.Location = new System.Drawing.Point( 12, 9 ); + this.lWarning.Name = "lWarning"; + this.lWarning.Size = new System.Drawing.Size( 393, 39 ); + this.lWarning.TabIndex = 0; + this.lWarning.Text = resources.GetString( "lWarning.Text" ); + // + // lSubstitute + // + this.lSubstitute.AutoSize = true; + this.lSubstitute.Location = new System.Drawing.Point( 12, 69 ); + this.lSubstitute.Name = "lSubstitute"; + this.lSubstitute.Size = new System.Drawing.Size( 81, 13 ); + this.lSubstitute.TabIndex = 1; + this.lSubstitute.Text = "Substitute rank:"; + // + // cSubstitute + // + this.cSubstitute.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cSubstitute.FormattingEnabled = true; + this.cSubstitute.Location = new System.Drawing.Point( 102, 66 ); + this.cSubstitute.Name = "cSubstitute"; + this.cSubstitute.Size = new System.Drawing.Size( 121, 21 ); + this.cSubstitute.TabIndex = 2; + this.cSubstitute.SelectedIndexChanged += new System.EventHandler( this.cSubstitute_SelectedIndexChanged ); + // + // bDelete + // + this.bDelete.DialogResult = System.Windows.Forms.DialogResult.OK; + this.bDelete.Enabled = false; + this.bDelete.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ( (byte)( 0 ) ) ); + this.bDelete.Location = new System.Drawing.Point( 203, 112 ); + this.bDelete.Name = "bDelete"; + this.bDelete.Size = new System.Drawing.Size( 100, 25 ); + this.bDelete.TabIndex = 3; + this.bDelete.Text = "Delete"; + this.bDelete.UseVisualStyleBackColor = true; + // + // bCancel + // + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Location = new System.Drawing.Point( 309, 112 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 25 ); + this.bCancel.TabIndex = 4; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + // + // DeleteRankPopup + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 421, 149 ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bDelete ); + this.Controls.Add( this.cSubstitute ); + this.Controls.Add( this.lSubstitute ); + this.Controls.Add( this.lWarning ); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DeleteRankPopup"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Deleting a Rank"; + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lWarning; + private System.Windows.Forms.Label lSubstitute; + private System.Windows.Forms.Button bDelete; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.ComboBox cSubstitute; + } +} \ No newline at end of file diff --git a/ConfigGUI/DeleteRankPopup.cs b/ConfigGUI/DeleteRankPopup.cs new file mode 100644 index 0000000..7ebba74 --- /dev/null +++ b/ConfigGUI/DeleteRankPopup.cs @@ -0,0 +1,31 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + public sealed partial class DeleteRankPopup : Form { + internal Rank SubstituteRank { get; private set; } + + public DeleteRankPopup( Rank deletedRank ) { + InitializeComponent(); + foreach( Rank rank in RankManager.Ranks ) { + if( rank != deletedRank ) { + cSubstitute.Items.Add( MainForm.ToComboBoxOption( rank ) ); + } + } + lWarning.Text = String.Format( lWarning.Text, deletedRank.Name ); + cSubstitute.SelectedIndex = cSubstitute.Items.Count - 1; + } + + + private void cSubstitute_SelectedIndexChanged( object sender, EventArgs e ) { + if( cSubstitute.SelectedIndex < 0 ) return; + foreach( Rank rank in RankManager.Ranks ) { + if( cSubstitute.SelectedItem.ToString() != MainForm.ToComboBoxOption( rank ) ) continue; + SubstituteRank = rank; + bDelete.Enabled = true; + break; + } + } + } +} diff --git a/ConfigGUI/DeleteRankPopup.resx b/ConfigGUI/DeleteRankPopup.resx new file mode 100644 index 0000000..9108c19 --- /dev/null +++ b/ConfigGUI/DeleteRankPopup.resx @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + To preserve compatibility with older files (player database, world/zone permissions, +etc) it is recommended that you specify a substitute rank. This will allow fCraft to +convert all references to the "{0}" rank to the specified still-existing rank. + + \ No newline at end of file diff --git a/ConfigGUI/KeywordPicker.Designer.cs b/ConfigGUI/KeywordPicker.Designer.cs new file mode 100644 index 0000000..ff3ca25 --- /dev/null +++ b/ConfigGUI/KeywordPicker.Designer.cs @@ -0,0 +1,71 @@ +namespace fCraft.ConfigGUI { + sealed partial class KeywordPicker { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.pFlow = new System.Windows.Forms.FlowLayoutPanel(); + this.bCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // pFlow + // + this.pFlow.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pFlow.Location = new System.Drawing.Point( 13, 13 ); + this.pFlow.Name = "pFlow"; + this.pFlow.Size = new System.Drawing.Size( 159, 318 ); + this.pFlow.TabIndex = 0; + // + // bCancel + // + this.bCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Location = new System.Drawing.Point( 62, 337 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 60, 23 ); + this.bCancel.TabIndex = 1; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + // + // KeywordPicker + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.bCancel; + this.ClientSize = new System.Drawing.Size( 184, 372 ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.pFlow ); + this.Name = "KeywordPicker"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "KeywordPicker"; + this.ResumeLayout( false ); + + } + + #endregion + + private System.Windows.Forms.FlowLayoutPanel pFlow; + private System.Windows.Forms.Button bCancel; + } +} \ No newline at end of file diff --git a/ConfigGUI/KeywordPicker.cs b/ConfigGUI/KeywordPicker.cs new file mode 100644 index 0000000..9cd183f --- /dev/null +++ b/ConfigGUI/KeywordPicker.cs @@ -0,0 +1,53 @@ +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + public sealed partial class KeywordPicker : Form { + public string Result; + + readonly ToolTip tips; + static readonly KeywordInfo[] Keywords = new[]{ + new KeywordInfo("{SERVER_NAME}", "Server name", "Name of your server, as specified in config." ), + new KeywordInfo("{RANK}", "Player's rank", "Player's rank, including prefix and colors (if applicable)." ), + new KeywordInfo("{PLAYER_NAME}", "Player's name", "Name of the player, including prefix and colors (if applicable)." ), + new KeywordInfo("{TIME}", "Time", "Current time (server clock)."), + new KeywordInfo("{WORLD}", "Main world name","Name of the main/starting world, including prefix and colors (if applicable)." ), + new KeywordInfo("{PLAYERS}", "Number of players online", "Note that hidden players will not be included in this number." ), + new KeywordInfo("{WORLDS}", "Number of worlds", "Number of worlds accessible by the player. Does not count hidden worlds." ), + new KeywordInfo("{MOTD}", "MOTD", "Message-of-the-day (server subtitle), as specified in config." ), + new KeywordInfo("{VERSION}", "fCraft version", "Version of fCraft that this server is running." ) + }; + + const int ButtonWidth = 150, + ButtonHeight = 28; + + public KeywordPicker() { + InitializeComponent(); + tips = new ToolTip(); + foreach( KeywordInfo keyword in Keywords ) { + Button newButton = new Button { + Text = keyword.LongName, + Tag = keyword.Keyword, + Width = ButtonWidth, + Height = ButtonHeight + }; + pFlow.Controls.Add( newButton ); + newButton.Click += delegate { + Result = (string)newButton.Tag; + DialogResult = DialogResult.OK; + Close(); + }; + tips.SetToolTip( newButton, keyword.Description ); + } + } + + + struct KeywordInfo { + public KeywordInfo( string keyword, string name, string description ) { + Keyword = keyword; + LongName = name; + Description = description; + } + public readonly string Keyword, LongName, Description; + } + } +} \ No newline at end of file diff --git a/ConfigGUI/KeywordPicker.resx b/ConfigGUI/KeywordPicker.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ConfigGUI/KeywordPicker.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/MainForm.Adapter.cs b/ConfigGUI/MainForm.Adapter.cs new file mode 100644 index 0000000..33ebf89 --- /dev/null +++ b/ConfigGUI/MainForm.Adapter.cs @@ -0,0 +1,665 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Windows.Forms; +using System.Xml.Linq; +using JetBrains.Annotations; + + +namespace fCraft.ConfigGUI { + // This section handles transfer of settings from Config to the specific UI controls, and vice versa. + // Effectively, it's an adapter between Config's and ConfigUI's representations of the settings + partial class MainForm { + #region Loading & Applying Config + + void LoadConfig() { + string missingFileMsg = null; + if( !File.Exists( Paths.WorldListFileName ) && !File.Exists( Paths.ConfigFileName ) ) { + missingFileMsg = String.Format( "Configuration ({0}) and world list ({1}) were not found. Using defaults.", + Paths.ConfigFileName, + Paths.WorldListFileName ); + } else if( !File.Exists( Paths.ConfigFileName ) ) { + missingFileMsg = String.Format( "Configuration ({0}) was not found. Using defaults.", + Paths.ConfigFileName ); + } else if( !File.Exists( Paths.WorldListFileName ) ) { + missingFileMsg = String.Format( "World list ({0}) was not found. Assuming 0 worlds.", + Paths.WorldListFileName ); + } + if( missingFileMsg != null ) { + MessageBox.Show( missingFileMsg ); + } + + using( LogRecorder loadLogger = new LogRecorder() ) { + if( Config.Load( false, false ) ) { + if( loadLogger.HasMessages ) { + MessageBox.Show( loadLogger.MessageString, "Config loading warnings" ); + } + } else { + MessageBox.Show( loadLogger.MessageString, "Error occured while trying to load config" ); + } + } + + ApplyTabGeneral(); + ApplyTabChat(); + ApplyTabWorlds(); // also reloads world list + ApplyTabRanks(); + ApplyTabSecurity(); + ApplyTabSavingAndBackup(); + ApplyTabLogging(); + ApplyTabIRC(); + ApplyTabAdvanced(); + + AddChangeHandler( tabs, SomethingChanged ); + AddChangeHandler( bResetTab, SomethingChanged ); + AddChangeHandler( bResetAll, SomethingChanged ); + dgvWorlds.CellValueChanged += delegate { + SomethingChanged( null, null ); + }; + + AddChangeHandler( tabChat, HandleTabChatChange ); + bApply.Enabled = false; + } + + + void LoadWorldList() { + if( Worlds.Count > 0 ) Worlds.Clear(); + if( !File.Exists( Paths.WorldListFileName ) ) return; + + try { + XDocument doc = XDocument.Load( Paths.WorldListFileName ); + XElement root = doc.Root; + if( root == null ) { + MessageBox.Show( "Worlds.xml is empty or corrupted." ); + return; + } + + string errorLog = ""; + using( LogRecorder logRecorder = new LogRecorder() ) { + foreach( XElement el in root.Elements( "World" ) ) { + try { + Worlds.Add( new WorldListEntry( el ) ); + } catch( Exception ex ) { + errorLog += ex + Environment.NewLine; + } + } + if( logRecorder.HasMessages ) { + MessageBox.Show( logRecorder.MessageString, "World list loading warnings." ); + } + } + if( errorLog.Length > 0 ) { + MessageBox.Show( "Some errors occured while loading the world list:" + Environment.NewLine + errorLog, "Warning" ); + } + + FillWorldList(); + XAttribute mainWorldAttr = root.Attribute( "main" ); + if( mainWorldAttr != null ) { + foreach( WorldListEntry world in Worlds ) { + if( world.Name.ToLower() == mainWorldAttr.Value.ToLower() ) { + cMainWorld.SelectedItem = world.Name; + break; + } + } + } + + } catch( Exception ex ) { + MessageBox.Show( "Error occured while loading the world list: " + Environment.NewLine + ex, "Warning" ); + } + + Worlds.ListChanged += SomethingChanged; + } + + + void ApplyTabGeneral() { + tServerName.Text = ConfigKey.ServerName.GetString(); + tMOTD.Text = ConfigKey.MOTD.GetString(); + + nMaxPlayers.Value = ConfigKey.MaxPlayers.GetInt(); + CheckMaxPlayersPerWorldValue(); + nMaxPlayersPerWorld.Value = ConfigKey.MaxPlayersPerWorld.GetInt(); + + FillRankList( cDefaultRank, "(lowest rank)" ); + if( ConfigKey.DefaultRank.IsBlank() ) { + cDefaultRank.SelectedIndex = 0; + } else { + RankManager.DefaultRank = Rank.Parse( ConfigKey.DefaultRank.GetString() ); + cDefaultRank.SelectedIndex = RankManager.GetIndex( RankManager.DefaultRank ); + } + + cPublic.SelectedIndex = ConfigKey.IsPublic.Enabled() ? 0 : 1; + nPort.Value = ConfigKey.Port.GetInt(); + nUploadBandwidth.Value = ConfigKey.UploadBandwidth.GetInt(); + + xAnnouncements.Checked = (ConfigKey.AnnouncementInterval.GetInt() > 0); + if( xAnnouncements.Checked ) { + nAnnouncements.Value = ConfigKey.AnnouncementInterval.GetInt(); + } else { + nAnnouncements.Value = 1; + } + + // UpdaterSettingsWindow + updaterWindow.BackupBeforeUpdate = ConfigKey.BackupBeforeUpdate.Enabled(); + updaterWindow.RunBeforeUpdate = ConfigKey.RunBeforeUpdate.GetString(); + updaterWindow.RunAfterUpdate = ConfigKey.RunAfterUpdate.GetString(); + updaterWindow.UpdaterMode = ConfigKey.UpdaterMode.GetEnum(); + } + + + void ApplyTabChat() { + xRankColorsInChat.Checked = ConfigKey.RankColorsInChat.Enabled(); + xRankPrefixesInChat.Checked = ConfigKey.RankPrefixesInChat.Enabled(); + xRankPrefixesInList.Checked = ConfigKey.RankPrefixesInList.Enabled(); + xRankColorsInWorldNames.Checked = ConfigKey.RankColorsInWorldNames.Enabled(); + xShowJoinedWorldMessages.Checked = ConfigKey.ShowJoinedWorldMessages.Enabled(); + xShowConnectionMessages.Checked = ConfigKey.ShowConnectionMessages.Enabled(); + + colorSys = Color.ParseToIndex( ConfigKey.SystemMessageColor.GetString() ); + ApplyColor( bColorSys, colorSys ); + Color.Sys = Color.Parse( colorSys ); + + colorHelp = Color.ParseToIndex( ConfigKey.HelpColor.GetString() ); + ApplyColor( bColorHelp, colorHelp ); + Color.Help = Color.Parse( colorHelp ); + + colorSay = Color.ParseToIndex( ConfigKey.SayColor.GetString() ); + ApplyColor( bColorSay, colorSay ); + Color.Say = Color.Parse( colorSay ); + + colorAnnouncement = Color.ParseToIndex( ConfigKey.AnnouncementColor.GetString() ); + ApplyColor( bColorAnnouncement, colorAnnouncement ); + Color.Announcement = Color.Parse( colorAnnouncement ); + + colorPM = Color.ParseToIndex( ConfigKey.PrivateMessageColor.GetString() ); + ApplyColor( bColorPM, colorPM ); + Color.PM = Color.Parse( colorPM ); + + colorWarning = Color.ParseToIndex( ConfigKey.WarningColor.GetString() ); + ApplyColor( bColorWarning, colorWarning ); + Color.Warning = Color.Parse( colorWarning ); + + colorMe = Color.ParseToIndex( ConfigKey.MeColor.GetString() ); + ApplyColor( bColorMe, colorMe ); + Color.Me = Color.Parse( colorMe ); + + UpdateChatPreview(); + } + + + void ApplyTabWorlds() { + if( rankNameList == null ) { + rankNameList = new BindingList { + WorldListEntry.DefaultRankOption + }; + foreach( Rank rank in RankManager.Ranks ) { + rankNameList.Add( MainForm.ToComboBoxOption(rank) ); + } + dgvcAccess.DataSource = rankNameList; + dgvcBuild.DataSource = rankNameList; + dgvcBackup.DataSource = WorldListEntry.BackupEnumNames; + + LoadWorldList(); + dgvWorlds.DataSource = Worlds; + + } else { + //dgvWorlds.DataSource = null; + rankNameList.Clear(); + rankNameList.Add( WorldListEntry.DefaultRankOption ); + foreach( Rank rank in RankManager.Ranks ) { + rankNameList.Add( MainForm.ToComboBoxOption(rank) ); + } + foreach( WorldListEntry world in Worlds ) { + world.ReparseRanks(); + } + Worlds.ResetBindings(); + //dgvWorlds.DataSource = worlds; + } + + FillRankList( cDefaultBuildRank, "(default rank)" ); + if( ConfigKey.DefaultBuildRank.IsBlank() ) { + cDefaultBuildRank.SelectedIndex = 0; + } else { + RankManager.DefaultBuildRank = Rank.Parse( ConfigKey.DefaultBuildRank.GetString() ); + cDefaultBuildRank.SelectedIndex = RankManager.GetIndex( RankManager.DefaultBuildRank ); + } + + if( Paths.IsDefaultMapPath( ConfigKey.MapPath.GetString() ) ) { + tMapPath.Text = Paths.MapPathDefault; + xMapPath.Checked = false; + } else { + tMapPath.Text = ConfigKey.MapPath.GetString(); + xMapPath.Checked = true; + } + + xWoMEnableEnvExtensions.Checked = ConfigKey.WoMEnableEnvExtensions.Enabled(); + } + + + void ApplyTabRanks() { + selectedRank = null; + RebuildRankList(); + DisableRankOptions(); + } + + + void ApplyTabSecurity() { + ApplyEnum( cVerifyNames, ConfigKey.VerifyNames, NameVerificationMode.Balanced ); + + nMaxConnectionsPerIP.Value = ConfigKey.MaxConnectionsPerIP.GetInt(); + xMaxConnectionsPerIP.Checked = (nMaxConnectionsPerIP.Value > 0); + xAllowUnverifiedLAN.Checked = ConfigKey.AllowUnverifiedLAN.Enabled(); + + nAntispamMessageCount.Value = ConfigKey.AntispamMessageCount.GetInt(); + nAntispamInterval.Value = ConfigKey.AntispamInterval.GetInt(); + nSpamMute.Value = ConfigKey.AntispamMuteDuration.GetInt(); + + xAntispamKicks.Checked = (ConfigKey.AntispamMaxWarnings.GetInt() > 0); + nAntispamMaxWarnings.Value = ConfigKey.AntispamMaxWarnings.GetInt(); + if( !xAntispamKicks.Checked ) nAntispamMaxWarnings.Enabled = false; + + xRequireKickReason.Checked = ConfigKey.RequireKickReason.Enabled(); + xRequireBanReason.Checked = ConfigKey.RequireBanReason.Enabled(); + xRequireRankChangeReason.Checked = ConfigKey.RequireRankChangeReason.Enabled(); + xAnnounceKickAndBanReasons.Checked = ConfigKey.AnnounceKickAndBanReasons.Enabled(); + xAnnounceRankChanges.Checked = ConfigKey.AnnounceRankChanges.Enabled(); + xAnnounceRankChangeReasons.Checked = ConfigKey.AnnounceRankChangeReasons.Enabled(); + xAnnounceRankChangeReasons.Enabled = xAnnounceRankChanges.Checked; + + FillRankList( cPatrolledRank, "(default rank)" ); + if( ConfigKey.PatrolledRank.IsBlank() ) { + cPatrolledRank.SelectedIndex = 0; + } else { + RankManager.PatrolledRank = Rank.Parse( ConfigKey.PatrolledRank.GetString() ); + cPatrolledRank.SelectedIndex = RankManager.GetIndex( RankManager.PatrolledRank ); + } + + xPaidPlayersOnly.Checked = ConfigKey.PaidPlayersOnly.Enabled(); + + + xBlockDBEnabled.Checked = ConfigKey.BlockDBEnabled.Enabled(); + xBlockDBAutoEnable.Checked = ConfigKey.BlockDBAutoEnable.Enabled(); + + FillRankList( cBlockDBAutoEnableRank, "(default rank)" ); + if( ConfigKey.BlockDBAutoEnableRank.IsBlank() ) { + cBlockDBAutoEnableRank.SelectedIndex = 0; + } else { + RankManager.BlockDBAutoEnableRank = Rank.Parse( ConfigKey.BlockDBAutoEnableRank.GetString() ); + cBlockDBAutoEnableRank.SelectedIndex = RankManager.GetIndex( RankManager.BlockDBAutoEnableRank ); + } + } + + + void ApplyTabSavingAndBackup() { + xSaveInterval.Checked = (ConfigKey.SaveInterval.GetInt() > 0); + nSaveInterval.Value = ConfigKey.SaveInterval.GetInt(); + if( !xSaveInterval.Checked ) nSaveInterval.Enabled = false; + + xBackupOnStartup.Checked = ConfigKey.BackupOnStartup.Enabled(); + xBackupOnJoin.Checked = ConfigKey.BackupOnJoin.Enabled(); + xBackupOnlyWhenChanged.Checked = ConfigKey.BackupOnlyWhenChanged.Enabled(); + + xBackupInterval.Checked = (ConfigKey.DefaultBackupInterval.GetInt() > 0); + nBackupInterval.Value = ConfigKey.DefaultBackupInterval.GetInt(); + if( !xBackupInterval.Checked ) nBackupInterval.Enabled = false; + + xMaxBackups.Checked = (ConfigKey.MaxBackups.GetInt() > 0); + nMaxBackups.Value = ConfigKey.MaxBackups.GetInt(); + if( !xMaxBackups.Checked ) nMaxBackups.Enabled = false; + + xMaxBackupSize.Checked = (ConfigKey.MaxBackupSize.GetInt() > 0); + nMaxBackupSize.Value = ConfigKey.MaxBackupSize.GetInt(); + if( !xMaxBackupSize.Checked ) nMaxBackupSize.Enabled = false; + + xBackupDataOnStartup.Checked = ConfigKey.BackupDataOnStartup.Enabled(); + } + + + void ApplyTabLogging() { + foreach( ListViewItem item in vConsoleOptions.Items ) { + item.Checked = Logger.ConsoleOptions[item.Index]; + } + foreach( ListViewItem item in vLogFileOptions.Items ) { + item.Checked = Logger.LogFileOptions[item.Index]; + } + + ApplyEnum( cLogMode, ConfigKey.LogMode, LogSplittingType.OneFile ); + + xLogLimit.Checked = (ConfigKey.MaxLogs.GetInt() > 0); + nLogLimit.Value = ConfigKey.MaxLogs.GetInt(); + if( !xLogLimit.Checked ) nLogLimit.Enabled = false; + } + + + void ApplyTabIRC() { + xIRCBotEnabled.Checked = ConfigKey.IRCBotEnabled.Enabled(); + gIRCNetwork.Enabled = xIRCBotEnabled.Checked; + gIRCOptions.Enabled = xIRCBotEnabled.Checked; + + tIRCBotNetwork.Text = ConfigKey.IRCBotNetwork.GetString(); + nIRCBotPort.Value = ConfigKey.IRCBotPort.GetInt(); + nIRCDelay.Value = ConfigKey.IRCDelay.GetInt(); + + tIRCBotChannels.Text = ConfigKey.IRCBotChannels.GetString(); + + tIRCBotNick.Text = ConfigKey.IRCBotNick.GetString(); + xIRCRegisteredNick.Checked = ConfigKey.IRCRegisteredNick.Enabled(); + + tIRCNickServ.Text = ConfigKey.IRCNickServ.GetString(); + tIRCNickServMessage.Text = ConfigKey.IRCNickServMessage.GetString(); + + xIRCBotAnnounceIRCJoins.Checked = ConfigKey.IRCBotAnnounceIRCJoins.Enabled(); + xIRCBotAnnounceServerJoins.Checked = ConfigKey.IRCBotAnnounceServerJoins.Enabled(); + xIRCBotForwardFromIRC.Checked = ConfigKey.IRCBotForwardFromIRC.Enabled(); + xIRCBotForwardFromServer.Checked = ConfigKey.IRCBotForwardFromServer.Enabled(); + + + colorIRC = Color.ParseToIndex( ConfigKey.IRCMessageColor.GetString() ); + ApplyColor( bColorIRC, colorIRC ); + Color.IRC = Color.Parse( colorIRC ); + + xIRCUseColor.Checked = ConfigKey.IRCUseColor.Enabled(); + xIRCBotAnnounceServerEvents.Checked = ConfigKey.IRCBotAnnounceServerEvents.Enabled(); + } + + + void ApplyTabAdvanced() { + xRelayAllBlockUpdates.Checked = ConfigKey.RelayAllBlockUpdates.Enabled(); + xNoPartialPositionUpdates.Checked = ConfigKey.NoPartialPositionUpdates.Enabled(); + nTickInterval.Value = ConfigKey.TickInterval.GetInt(); + + if( ConfigKey.ProcessPriority.IsBlank() ) { + cProcessPriority.SelectedIndex = 0; // Default + } else { + switch( ConfigKey.ProcessPriority.GetEnum() ) { + case ProcessPriorityClass.High: + cProcessPriority.SelectedIndex = 1; break; + case ProcessPriorityClass.AboveNormal: + cProcessPriority.SelectedIndex = 2; break; + case ProcessPriorityClass.Normal: + cProcessPriority.SelectedIndex = 3; break; + case ProcessPriorityClass.BelowNormal: + cProcessPriority.SelectedIndex = 4; break; + case ProcessPriorityClass.Idle: + cProcessPriority.SelectedIndex = 5; break; + } + } + + ApplyEnum( cUpdaterMode, ConfigKey.UpdaterMode, UpdaterMode.Prompt ); + + nThrottling.Value = ConfigKey.BlockUpdateThrottling.GetInt(); + xLowLatencyMode.Checked = ConfigKey.LowLatencyMode.Enabled(); + xSubmitCrashReports.Checked = ConfigKey.SubmitCrashReports.Enabled(); + + if( ConfigKey.MaxUndo.GetInt() > 0 ) { + xMaxUndo.Checked = true; + nMaxUndo.Value = ConfigKey.MaxUndo.GetInt(); + } else { + xMaxUndo.Checked = false; + nMaxUndo.Value = (int)ConfigKey.MaxUndo.GetDefault(); + } + nMaxUndoStates.Value = ConfigKey.MaxUndoStates.GetInt(); + + tConsoleName.Text = ConfigKey.ConsoleName.GetString(); + + tIP.Text = ConfigKey.IP.GetString(); + if( ConfigKey.IP.IsBlank() || ConfigKey.IP.IsDefault() ) { + tIP.Enabled = false; + xIP.Checked = false; + } else { + tIP.Enabled = true; + xIP.Checked = true; + } + + xHeartbeatToWoMDirect.Checked = ConfigKey.HeartbeatToWoMDirect.Enabled(); + } + + + static void ApplyEnum( [NotNull] ComboBox box, ConfigKey key, TEnum def ) where TEnum : struct { + if( box == null ) throw new ArgumentNullException( "box" ); + if( !typeof( TEnum ).IsEnum ) throw new ArgumentException( "Enum type required" ); + try { + if( key.IsBlank() ) { + box.SelectedIndex = (int)(object)def; + } else { + box.SelectedIndex = (int)Enum.Parse( typeof( TEnum ), key.GetString(), true ); + } + } catch( ArgumentException ) { + box.SelectedIndex = (int)(object)def; + } + } + + #endregion + + + #region Saving Config + + void SaveConfig() { + // General + ConfigKey.ServerName.TrySetValue( tServerName.Text ); + ConfigKey.MOTD.TrySetValue( tMOTD.Text ); + ConfigKey.MaxPlayers.TrySetValue( nMaxPlayers.Value ); + ConfigKey.MaxPlayersPerWorld.TrySetValue( nMaxPlayersPerWorld.Value ); + if( cDefaultRank.SelectedIndex == 0 ) { + ConfigKey.DefaultRank.TrySetValue( "" ); + } else { + ConfigKey.DefaultRank.TrySetValue( RankManager.DefaultRank.FullName ); + } + ConfigKey.IsPublic.TrySetValue( cPublic.SelectedIndex == 0 ); + ConfigKey.Port.TrySetValue( nPort.Value ); + if( xIP.Checked ) { + ConfigKey.IP.TrySetValue( tIP.Text ); + } else { + ConfigKey.IP.ResetValue(); + } + + ConfigKey.UploadBandwidth.TrySetValue( nUploadBandwidth.Value ); + + if( xAnnouncements.Checked ) ConfigKey.AnnouncementInterval.TrySetValue( nAnnouncements.Value ); + else ConfigKey.AnnouncementInterval.TrySetValue( 0 ); + + // UpdaterSettingsWindow + ConfigKey.UpdaterMode.TrySetValue( updaterWindow.UpdaterMode ); + ConfigKey.BackupBeforeUpdate.TrySetValue( updaterWindow.BackupBeforeUpdate ); + ConfigKey.RunBeforeUpdate.TrySetValue( updaterWindow.RunBeforeUpdate ); + ConfigKey.RunAfterUpdate.TrySetValue( updaterWindow.RunAfterUpdate ); + + + // Chat + ConfigKey.SystemMessageColor.TrySetValue( Color.GetName( colorSys ) ); + ConfigKey.HelpColor.TrySetValue( Color.GetName( colorHelp ) ); + ConfigKey.SayColor.TrySetValue( Color.GetName( colorSay ) ); + ConfigKey.AnnouncementColor.TrySetValue( Color.GetName( colorAnnouncement ) ); + ConfigKey.PrivateMessageColor.TrySetValue( Color.GetName( colorPM ) ); + ConfigKey.WarningColor.TrySetValue( Color.GetName( colorWarning ) ); + ConfigKey.MeColor.TrySetValue( Color.GetName( colorMe ) ); + ConfigKey.ShowJoinedWorldMessages.TrySetValue( xShowJoinedWorldMessages.Checked ); + ConfigKey.RankColorsInWorldNames.TrySetValue( xRankColorsInWorldNames.Checked ); + ConfigKey.RankColorsInChat.TrySetValue( xRankColorsInChat.Checked ); + ConfigKey.RankPrefixesInChat.TrySetValue( xRankPrefixesInChat.Checked ); + ConfigKey.RankPrefixesInList.TrySetValue( xRankPrefixesInList.Checked ); + ConfigKey.ShowConnectionMessages.TrySetValue( xShowConnectionMessages.Checked ); + + + // Worlds + if( cDefaultBuildRank.SelectedIndex == 0 ) { + ConfigKey.DefaultBuildRank.TrySetValue( "" ); + } else { + ConfigKey.DefaultBuildRank.TrySetValue( RankManager.DefaultBuildRank.FullName ); + } + + if( xMapPath.Checked ) ConfigKey.MapPath.TrySetValue( tMapPath.Text ); + else ConfigKey.MapPath.TrySetValue( ConfigKey.MapPath.GetDefault() ); + + ConfigKey.WoMEnableEnvExtensions.TrySetValue( xWoMEnableEnvExtensions.Checked ); + + + // Security + WriteEnum( cVerifyNames, ConfigKey.VerifyNames ); + + if( xMaxConnectionsPerIP.Checked ) { + ConfigKey.MaxConnectionsPerIP.TrySetValue( nMaxConnectionsPerIP.Value ); + } else { + ConfigKey.MaxConnectionsPerIP.TrySetValue( 0 ); + } + ConfigKey.AllowUnverifiedLAN.TrySetValue( xAllowUnverifiedLAN.Checked ); + + ConfigKey.AntispamMessageCount.TrySetValue( nAntispamMessageCount.Value ); + ConfigKey.AntispamInterval.TrySetValue( nAntispamInterval.Value ); + ConfigKey.AntispamMuteDuration.TrySetValue( nSpamMute.Value ); + + if( xAntispamKicks.Checked ) ConfigKey.AntispamMaxWarnings.TrySetValue( nAntispamMaxWarnings.Value ); + else ConfigKey.AntispamMaxWarnings.TrySetValue( 0 ); + + ConfigKey.RequireKickReason.TrySetValue( xRequireKickReason.Checked ); + ConfigKey.RequireBanReason.TrySetValue( xRequireBanReason.Checked ); + ConfigKey.RequireRankChangeReason.TrySetValue( xRequireRankChangeReason.Checked ); + ConfigKey.AnnounceKickAndBanReasons.TrySetValue( xAnnounceKickAndBanReasons.Checked ); + ConfigKey.AnnounceRankChanges.TrySetValue( xAnnounceRankChanges.Checked ); + ConfigKey.AnnounceRankChangeReasons.TrySetValue( xAnnounceRankChangeReasons.Checked ); + + if( cPatrolledRank.SelectedIndex == 0 ) { + ConfigKey.PatrolledRank.TrySetValue( "" ); + } else { + ConfigKey.PatrolledRank.TrySetValue( RankManager.PatrolledRank.FullName ); + } + ConfigKey.PaidPlayersOnly.TrySetValue( xPaidPlayersOnly.Checked ); + + ConfigKey.BlockDBEnabled.TrySetValue( xBlockDBEnabled.Checked ); + ConfigKey.BlockDBAutoEnable.TrySetValue( xBlockDBAutoEnable.Checked ); + if( cBlockDBAutoEnableRank.SelectedIndex == 0 ) { + ConfigKey.BlockDBAutoEnableRank.TrySetValue( "" ); + } else { + ConfigKey.BlockDBAutoEnableRank.TrySetValue( RankManager.BlockDBAutoEnableRank.FullName ); + } + + + // Saving & Backups + if( xSaveInterval.Checked ) ConfigKey.SaveInterval.TrySetValue( nSaveInterval.Value ); + else ConfigKey.SaveInterval.TrySetValue( 0 ); + ConfigKey.BackupOnStartup.TrySetValue( xBackupOnStartup.Checked ); + ConfigKey.BackupOnJoin.TrySetValue( xBackupOnJoin.Checked ); + ConfigKey.BackupOnlyWhenChanged.TrySetValue( xBackupOnlyWhenChanged.Checked ); + + if( xBackupInterval.Checked ) ConfigKey.DefaultBackupInterval.TrySetValue( nBackupInterval.Value ); + else ConfigKey.DefaultBackupInterval.TrySetValue( 0 ); + if( xMaxBackups.Checked ) ConfigKey.MaxBackups.TrySetValue( nMaxBackups.Value ); + else ConfigKey.MaxBackups.TrySetValue( 0 ); + if( xMaxBackupSize.Checked ) ConfigKey.MaxBackupSize.TrySetValue( nMaxBackupSize.Value ); + else ConfigKey.MaxBackupSize.TrySetValue( 0 ); + + ConfigKey.BackupDataOnStartup.TrySetValue( xBackupDataOnStartup.Checked ); + + + // Logging + WriteEnum( cLogMode, ConfigKey.LogMode ); + if( xLogLimit.Checked ) ConfigKey.MaxLogs.TrySetValue( nLogLimit.Value ); + else ConfigKey.MaxLogs.TrySetValue( "0" ); + foreach( ListViewItem item in vConsoleOptions.Items ) { + Logger.ConsoleOptions[item.Index] = item.Checked; + } + foreach( ListViewItem item in vLogFileOptions.Items ) { + Logger.LogFileOptions[item.Index] = item.Checked; + } + + + // IRC + ConfigKey.IRCBotEnabled.TrySetValue( xIRCBotEnabled.Checked ); + + ConfigKey.IRCBotNetwork.TrySetValue( tIRCBotNetwork.Text ); + ConfigKey.IRCBotPort.TrySetValue( nIRCBotPort.Value ); + ConfigKey.IRCDelay.TrySetValue( nIRCDelay.Value ); + + ConfigKey.IRCBotChannels.TrySetValue( tIRCBotChannels.Text ); + + ConfigKey.IRCBotNick.TrySetValue( tIRCBotNick.Text ); + ConfigKey.IRCRegisteredNick.TrySetValue( xIRCRegisteredNick.Checked ); + ConfigKey.IRCNickServ.TrySetValue( tIRCNickServ.Text ); + ConfigKey.IRCNickServMessage.TrySetValue( tIRCNickServMessage.Text ); + + ConfigKey.IRCBotAnnounceIRCJoins.TrySetValue( xIRCBotAnnounceIRCJoins.Checked ); + ConfigKey.IRCBotAnnounceServerJoins.TrySetValue( xIRCBotAnnounceServerJoins.Checked ); + ConfigKey.IRCBotAnnounceServerEvents.TrySetValue( xIRCBotAnnounceServerEvents.Checked ); + ConfigKey.IRCBotForwardFromIRC.TrySetValue( xIRCBotForwardFromIRC.Checked ); + ConfigKey.IRCBotForwardFromServer.TrySetValue( xIRCBotForwardFromServer.Checked ); + + ConfigKey.IRCMessageColor.TrySetValue( Color.GetName( colorIRC ) ); + ConfigKey.IRCUseColor.TrySetValue( xIRCUseColor.Checked ); + + + // advanced + ConfigKey.SubmitCrashReports.TrySetValue( xSubmitCrashReports.Checked ); + + ConfigKey.RelayAllBlockUpdates.TrySetValue( xRelayAllBlockUpdates.Checked ); + ConfigKey.NoPartialPositionUpdates.TrySetValue( xNoPartialPositionUpdates.Checked ); + ConfigKey.TickInterval.TrySetValue( Convert.ToInt32( nTickInterval.Value ) ); + + switch( cProcessPriority.SelectedIndex ) { + case 0: + ConfigKey.ProcessPriority.ResetValue(); break; + case 1: + ConfigKey.ProcessPriority.TrySetValue( ProcessPriorityClass.High ); break; + case 2: + ConfigKey.ProcessPriority.TrySetValue( ProcessPriorityClass.AboveNormal ); break; + case 3: + ConfigKey.ProcessPriority.TrySetValue( ProcessPriorityClass.Normal ); break; + case 4: + ConfigKey.ProcessPriority.TrySetValue( ProcessPriorityClass.BelowNormal ); break; + case 5: + ConfigKey.ProcessPriority.TrySetValue( ProcessPriorityClass.Idle ); break; + } + + ConfigKey.BlockUpdateThrottling.TrySetValue( Convert.ToInt32( nThrottling.Value ) ); + + ConfigKey.LowLatencyMode.TrySetValue( xLowLatencyMode.Checked ); + + if( xMaxUndo.Checked ) ConfigKey.MaxUndo.TrySetValue( Convert.ToInt32( nMaxUndo.Value ) ); + else ConfigKey.MaxUndo.TrySetValue( 0 ); + ConfigKey.MaxUndoStates.TrySetValue( Convert.ToInt32( nMaxUndoStates.Value ) ); + + ConfigKey.ConsoleName.TrySetValue( tConsoleName.Text ); + + ConfigKey.HeartbeatToWoMDirect.TrySetValue( xHeartbeatToWoMDirect.Checked ); + + SaveWorldList(); + } + + + void SaveWorldList() { + const string worldListTempFileName = Paths.WorldListFileName + ".tmp"; + try { + XDocument doc = new XDocument(); + XElement root = new XElement( "fCraftWorldList" ); + foreach( WorldListEntry world in Worlds ) { + root.Add( world.Serialize() ); + } + if( cMainWorld.SelectedItem != null ) { + root.Add( new XAttribute( "main", cMainWorld.SelectedItem ) ); + } + doc.Add( root ); + doc.Save( worldListTempFileName ); + Paths.MoveOrReplace( worldListTempFileName, Paths.WorldListFileName ); + } catch( Exception ex ) { + MessageBox.Show( String.Format( "An error occured while trying to save world list ({0}): {1}{2}", + Paths.WorldListFileName, + Environment.NewLine, + ex ) ); + } + } + + + static void WriteEnum( [NotNull] ComboBox box, ConfigKey key ) where TEnum : struct { + if( box == null ) throw new ArgumentNullException( "box" ); + if( !typeof( TEnum ).IsEnum ) throw new ArgumentException( "Enum type required" ); + try { + TEnum val = (TEnum)Enum.Parse( typeof( TEnum ), box.SelectedIndex.ToString(), true ); + key.TrySetValue( val ); + } catch( ArgumentException ) { + Logger.Log( LogType.Error, + "ConfigUI.WriteEnum<{0}>: Could not parse value for {1}. Using default ({2}).", + typeof( TEnum ).Name, key, key.GetString() ); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/ConfigGUI/MainForm.Designer.cs b/ConfigGUI/MainForm.Designer.cs new file mode 100644 index 0000000..801cc1b --- /dev/null +++ b/ConfigGUI/MainForm.Designer.cs @@ -0,0 +1,3774 @@ +namespace fCraft.ConfigGUI { + partial class MainForm { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + bold.Dispose(); + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof( MainForm ) ); + this.tabs = new System.Windows.Forms.TabControl(); + this.tabGeneral = new System.Windows.Forms.TabPage(); + this.gUpdaterSettings = new System.Windows.Forms.GroupBox(); + this.bShowAdvancedUpdaterSettings = new System.Windows.Forms.Button(); + this.cUpdaterMode = new System.Windows.Forms.ComboBox(); + this.lUpdater = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.bChangelog = new System.Windows.Forms.Button(); + this.bCredits = new System.Windows.Forms.Button(); + this.bReadme = new System.Windows.Forms.Button(); + this.gHelpAndSupport = new System.Windows.Forms.GroupBox(); + this.bOpenWiki = new System.Windows.Forms.Button(); + this.bReportABug = new System.Windows.Forms.Button(); + this.gInformation = new System.Windows.Forms.GroupBox(); + this.bGreeting = new System.Windows.Forms.Button(); + this.lAnnouncementsUnits = new System.Windows.Forms.Label(); + this.nAnnouncements = new System.Windows.Forms.NumericUpDown(); + this.xAnnouncements = new System.Windows.Forms.CheckBox(); + this.bRules = new System.Windows.Forms.Button(); + this.bAnnouncements = new System.Windows.Forms.Button(); + this.gBasic = new System.Windows.Forms.GroupBox(); + this.nMaxPlayersPerWorld = new System.Windows.Forms.NumericUpDown(); + this.lMaxPlayersPerWorld = new System.Windows.Forms.Label(); + this.bPortCheck = new System.Windows.Forms.Button(); + this.lPort = new System.Windows.Forms.Label(); + this.nPort = new System.Windows.Forms.NumericUpDown(); + this.cDefaultRank = new System.Windows.Forms.ComboBox(); + this.lDefaultRank = new System.Windows.Forms.Label(); + this.lUploadBandwidth = new System.Windows.Forms.Label(); + this.bMeasure = new System.Windows.Forms.Button(); + this.tServerName = new System.Windows.Forms.TextBox(); + this.lUploadBandwidthUnits = new System.Windows.Forms.Label(); + this.lServerName = new System.Windows.Forms.Label(); + this.nUploadBandwidth = new System.Windows.Forms.NumericUpDown(); + this.tMOTD = new System.Windows.Forms.TextBox(); + this.lMOTD = new System.Windows.Forms.Label(); + this.cPublic = new System.Windows.Forms.ComboBox(); + this.nMaxPlayers = new System.Windows.Forms.NumericUpDown(); + this.lPublic = new System.Windows.Forms.Label(); + this.lMaxPlayers = new System.Windows.Forms.Label(); + this.tabChat = new System.Windows.Forms.TabPage(); + this.gChatColors = new System.Windows.Forms.GroupBox(); + this.lColorMe = new System.Windows.Forms.Label(); + this.bColorMe = new System.Windows.Forms.Button(); + this.lColorWarning = new System.Windows.Forms.Label(); + this.bColorWarning = new System.Windows.Forms.Button(); + this.bColorSys = new System.Windows.Forms.Button(); + this.lColorSys = new System.Windows.Forms.Label(); + this.bColorPM = new System.Windows.Forms.Button(); + this.lColorHelp = new System.Windows.Forms.Label(); + this.lColorPM = new System.Windows.Forms.Label(); + this.lColorSay = new System.Windows.Forms.Label(); + this.bColorAnnouncement = new System.Windows.Forms.Button(); + this.lColorAnnouncement = new System.Windows.Forms.Label(); + this.bColorHelp = new System.Windows.Forms.Button(); + this.bColorSay = new System.Windows.Forms.Button(); + this.gAppearence = new System.Windows.Forms.GroupBox(); + this.xShowConnectionMessages = new System.Windows.Forms.CheckBox(); + this.xShowJoinedWorldMessages = new System.Windows.Forms.CheckBox(); + this.xRankColorsInWorldNames = new System.Windows.Forms.CheckBox(); + this.xRankPrefixesInList = new System.Windows.Forms.CheckBox(); + this.xRankPrefixesInChat = new System.Windows.Forms.CheckBox(); + this.xRankColorsInChat = new System.Windows.Forms.CheckBox(); + this.chatPreview = new fCraft.ConfigGUI.ChatPreview(); + this.tabWorlds = new System.Windows.Forms.TabPage(); + this.xWoMEnableEnvExtensions = new System.Windows.Forms.CheckBox(); + this.bMapPath = new System.Windows.Forms.Button(); + this.xMapPath = new System.Windows.Forms.CheckBox(); + this.tMapPath = new System.Windows.Forms.TextBox(); + this.lDefaultBuildRank = new System.Windows.Forms.Label(); + this.cDefaultBuildRank = new System.Windows.Forms.ComboBox(); + this.cMainWorld = new System.Windows.Forms.ComboBox(); + this.lMainWorld = new System.Windows.Forms.Label(); + this.bWorldEdit = new System.Windows.Forms.Button(); + this.bAddWorld = new System.Windows.Forms.Button(); + this.bWorldDelete = new System.Windows.Forms.Button(); + this.dgvWorlds = new System.Windows.Forms.DataGridView(); + this.dgvcName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dgvcDescription = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dgvcAccess = new System.Windows.Forms.DataGridViewComboBoxColumn(); + this.dgvcBuild = new System.Windows.Forms.DataGridViewComboBoxColumn(); + this.dgvcBackup = new System.Windows.Forms.DataGridViewComboBoxColumn(); + this.dgvcHidden = new System.Windows.Forms.DataGridViewCheckBoxColumn(); + this.dgvcBlockDB = new System.Windows.Forms.DataGridViewCheckBoxColumn(); + this.tabRanks = new System.Windows.Forms.TabPage(); + this.gPermissionLimits = new System.Windows.Forms.GroupBox(); + this.permissionLimitBoxContainer = new System.Windows.Forms.FlowLayoutPanel(); + this.lRankList = new System.Windows.Forms.Label(); + this.bLowerRank = new System.Windows.Forms.Button(); + this.bRaiseRank = new System.Windows.Forms.Button(); + this.gRankOptions = new System.Windows.Forms.GroupBox(); + this.lFillLimitUnits = new System.Windows.Forms.Label(); + this.nFillLimit = new System.Windows.Forms.NumericUpDown(); + this.lFillLimit = new System.Windows.Forms.Label(); + this.nCopyPasteSlots = new System.Windows.Forms.NumericUpDown(); + this.lCopyPasteSlots = new System.Windows.Forms.Label(); + this.xAllowSecurityCircumvention = new System.Windows.Forms.CheckBox(); + this.lAntiGrief1 = new System.Windows.Forms.Label(); + this.lAntiGrief3 = new System.Windows.Forms.Label(); + this.nAntiGriefSeconds = new System.Windows.Forms.NumericUpDown(); + this.bColorRank = new System.Windows.Forms.Button(); + this.xDrawLimit = new System.Windows.Forms.CheckBox(); + this.lDrawLimitUnits = new System.Windows.Forms.Label(); + this.lKickIdleUnits = new System.Windows.Forms.Label(); + this.nDrawLimit = new System.Windows.Forms.NumericUpDown(); + this.nKickIdle = new System.Windows.Forms.NumericUpDown(); + this.xAntiGrief = new System.Windows.Forms.CheckBox(); + this.lAntiGrief2 = new System.Windows.Forms.Label(); + this.xKickIdle = new System.Windows.Forms.CheckBox(); + this.nAntiGriefBlocks = new System.Windows.Forms.NumericUpDown(); + this.xReserveSlot = new System.Windows.Forms.CheckBox(); + this.tPrefix = new System.Windows.Forms.TextBox(); + this.lPrefix = new System.Windows.Forms.Label(); + this.lRankColor = new System.Windows.Forms.Label(); + this.tRankName = new System.Windows.Forms.TextBox(); + this.lRankName = new System.Windows.Forms.Label(); + this.bDeleteRank = new System.Windows.Forms.Button(); + this.vPermissions = new System.Windows.Forms.ListView(); + this.chPermissions = new System.Windows.Forms.ColumnHeader(); + this.bAddRank = new System.Windows.Forms.Button(); + this.lPermissions = new System.Windows.Forms.Label(); + this.vRanks = new System.Windows.Forms.ListBox(); + this.tabSecurity = new System.Windows.Forms.TabPage(); + this.gBlockDB = new System.Windows.Forms.GroupBox(); + this.cBlockDBAutoEnableRank = new System.Windows.Forms.ComboBox(); + this.xBlockDBAutoEnable = new System.Windows.Forms.CheckBox(); + this.xBlockDBEnabled = new System.Windows.Forms.CheckBox(); + this.gSecurityMisc = new System.Windows.Forms.GroupBox(); + this.xAnnounceRankChangeReasons = new System.Windows.Forms.CheckBox(); + this.xRequireKickReason = new System.Windows.Forms.CheckBox(); + this.xPaidPlayersOnly = new System.Windows.Forms.CheckBox(); + this.lPatrolledRankAndBelow = new System.Windows.Forms.Label(); + this.cPatrolledRank = new System.Windows.Forms.ComboBox(); + this.lPatrolledRank = new System.Windows.Forms.Label(); + this.xAnnounceRankChanges = new System.Windows.Forms.CheckBox(); + this.xAnnounceKickAndBanReasons = new System.Windows.Forms.CheckBox(); + this.xRequireRankChangeReason = new System.Windows.Forms.CheckBox(); + this.xRequireBanReason = new System.Windows.Forms.CheckBox(); + this.gSpamChat = new System.Windows.Forms.GroupBox(); + this.lAntispamMaxWarnings = new System.Windows.Forms.Label(); + this.nAntispamMaxWarnings = new System.Windows.Forms.NumericUpDown(); + this.xAntispamKicks = new System.Windows.Forms.CheckBox(); + this.lSpamMuteSeconds = new System.Windows.Forms.Label(); + this.lAntispamInterval = new System.Windows.Forms.Label(); + this.nSpamMute = new System.Windows.Forms.NumericUpDown(); + this.lSpamMute = new System.Windows.Forms.Label(); + this.nAntispamInterval = new System.Windows.Forms.NumericUpDown(); + this.lAntispamMessageCount = new System.Windows.Forms.Label(); + this.nAntispamMessageCount = new System.Windows.Forms.NumericUpDown(); + this.lSpamChat = new System.Windows.Forms.Label(); + this.gVerify = new System.Windows.Forms.GroupBox(); + this.nMaxConnectionsPerIP = new System.Windows.Forms.NumericUpDown(); + this.xAllowUnverifiedLAN = new System.Windows.Forms.CheckBox(); + this.xMaxConnectionsPerIP = new System.Windows.Forms.CheckBox(); + this.lVerifyNames = new System.Windows.Forms.Label(); + this.cVerifyNames = new System.Windows.Forms.ComboBox(); + this.tabSavingAndBackup = new System.Windows.Forms.TabPage(); + this.gDataBackup = new System.Windows.Forms.GroupBox(); + this.xBackupDataOnStartup = new System.Windows.Forms.CheckBox(); + this.gSaving = new System.Windows.Forms.GroupBox(); + this.nSaveInterval = new System.Windows.Forms.NumericUpDown(); + this.lSaveIntervalUnits = new System.Windows.Forms.Label(); + this.xSaveInterval = new System.Windows.Forms.CheckBox(); + this.gBackups = new System.Windows.Forms.GroupBox(); + this.xBackupOnlyWhenChanged = new System.Windows.Forms.CheckBox(); + this.lMaxBackupSize = new System.Windows.Forms.Label(); + this.xMaxBackupSize = new System.Windows.Forms.CheckBox(); + this.nMaxBackupSize = new System.Windows.Forms.NumericUpDown(); + this.xMaxBackups = new System.Windows.Forms.CheckBox(); + this.xBackupOnStartup = new System.Windows.Forms.CheckBox(); + this.lMaxBackups = new System.Windows.Forms.Label(); + this.nMaxBackups = new System.Windows.Forms.NumericUpDown(); + this.nBackupInterval = new System.Windows.Forms.NumericUpDown(); + this.lBackupIntervalUnits = new System.Windows.Forms.Label(); + this.xBackupInterval = new System.Windows.Forms.CheckBox(); + this.xBackupOnJoin = new System.Windows.Forms.CheckBox(); + this.tabLogging = new System.Windows.Forms.TabPage(); + this.gLogFile = new System.Windows.Forms.GroupBox(); + this.lLogFileOptionsDescription = new System.Windows.Forms.Label(); + this.xLogLimit = new System.Windows.Forms.CheckBox(); + this.vLogFileOptions = new System.Windows.Forms.ListView(); + this.columnHeader2 = new System.Windows.Forms.ColumnHeader(); + this.lLogLimitUnits = new System.Windows.Forms.Label(); + this.nLogLimit = new System.Windows.Forms.NumericUpDown(); + this.cLogMode = new System.Windows.Forms.ComboBox(); + this.lLogMode = new System.Windows.Forms.Label(); + this.gConsole = new System.Windows.Forms.GroupBox(); + this.lLogConsoleOptionsDescription = new System.Windows.Forms.Label(); + this.vConsoleOptions = new System.Windows.Forms.ListView(); + this.columnHeader3 = new System.Windows.Forms.ColumnHeader(); + this.tabIRC = new System.Windows.Forms.TabPage(); + this.xIRCListShowNonEnglish = new System.Windows.Forms.CheckBox(); + this.gIRCOptions = new System.Windows.Forms.GroupBox(); + this.xIRCBotAnnounceServerEvents = new System.Windows.Forms.CheckBox(); + this.xIRCUseColor = new System.Windows.Forms.CheckBox(); + this.lIRCNoForwardingMessage = new System.Windows.Forms.Label(); + this.xIRCBotAnnounceIRCJoins = new System.Windows.Forms.CheckBox(); + this.bColorIRC = new System.Windows.Forms.Button(); + this.lColorIRC = new System.Windows.Forms.Label(); + this.xIRCBotForwardFromIRC = new System.Windows.Forms.CheckBox(); + this.xIRCBotAnnounceServerJoins = new System.Windows.Forms.CheckBox(); + this.xIRCBotForwardFromServer = new System.Windows.Forms.CheckBox(); + this.gIRCNetwork = new System.Windows.Forms.GroupBox(); + this.lIRCDelayUnits = new System.Windows.Forms.Label(); + this.xIRCRegisteredNick = new System.Windows.Forms.CheckBox(); + this.tIRCNickServMessage = new System.Windows.Forms.TextBox(); + this.lIRCNickServMessage = new System.Windows.Forms.Label(); + this.tIRCNickServ = new System.Windows.Forms.TextBox(); + this.lIRCNickServ = new System.Windows.Forms.Label(); + this.nIRCDelay = new System.Windows.Forms.NumericUpDown(); + this.lIRCDelay = new System.Windows.Forms.Label(); + this.lIRCBotChannels2 = new System.Windows.Forms.Label(); + this.lIRCBotChannels3 = new System.Windows.Forms.Label(); + this.tIRCBotChannels = new System.Windows.Forms.TextBox(); + this.lIRCBotChannels = new System.Windows.Forms.Label(); + this.nIRCBotPort = new System.Windows.Forms.NumericUpDown(); + this.lIRCBotPort = new System.Windows.Forms.Label(); + this.tIRCBotNetwork = new System.Windows.Forms.TextBox(); + this.lIRCBotNetwork = new System.Windows.Forms.Label(); + this.lIRCBotNick = new System.Windows.Forms.Label(); + this.tIRCBotNick = new System.Windows.Forms.TextBox(); + this.lIRCList = new System.Windows.Forms.Label(); + this.xIRCBotEnabled = new System.Windows.Forms.CheckBox(); + this.cIRCList = new System.Windows.Forms.ComboBox(); + this.tabAdvanced = new System.Windows.Forms.TabPage(); + this.gPerformance = new System.Windows.Forms.GroupBox(); + this.lAdvancedWarning = new System.Windows.Forms.Label(); + this.xLowLatencyMode = new System.Windows.Forms.CheckBox(); + this.lProcessPriority = new System.Windows.Forms.Label(); + this.cProcessPriority = new System.Windows.Forms.ComboBox(); + this.nTickInterval = new System.Windows.Forms.NumericUpDown(); + this.lTickIntervalUnits = new System.Windows.Forms.Label(); + this.lTickInterval = new System.Windows.Forms.Label(); + this.nThrottling = new System.Windows.Forms.NumericUpDown(); + this.lThrottling = new System.Windows.Forms.Label(); + this.lThrottlingUnits = new System.Windows.Forms.Label(); + this.gAdvancedMisc = new System.Windows.Forms.GroupBox(); + this.nMaxUndoStates = new System.Windows.Forms.NumericUpDown(); + this.lMaxUndoStates = new System.Windows.Forms.Label(); + this.xHeartbeatToWoMDirect = new System.Windows.Forms.CheckBox(); + this.lIPWarning = new System.Windows.Forms.Label(); + this.tIP = new System.Windows.Forms.TextBox(); + this.xIP = new System.Windows.Forms.CheckBox(); + this.lConsoleName = new System.Windows.Forms.Label(); + this.tConsoleName = new System.Windows.Forms.TextBox(); + this.nMaxUndo = new System.Windows.Forms.NumericUpDown(); + this.lMaxUndoUnits = new System.Windows.Forms.Label(); + this.xMaxUndo = new System.Windows.Forms.CheckBox(); + this.xRelayAllBlockUpdates = new System.Windows.Forms.CheckBox(); + this.xNoPartialPositionUpdates = new System.Windows.Forms.CheckBox(); + this.gCrashReport = new System.Windows.Forms.GroupBox(); + this.lCrashReportDisclaimer = new System.Windows.Forms.Label(); + this.xSubmitCrashReports = new System.Windows.Forms.CheckBox(); + this.bOK = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.bResetTab = new System.Windows.Forms.Button(); + this.bResetAll = new System.Windows.Forms.Button(); + this.bApply = new System.Windows.Forms.Button(); + this.toolTip = new System.Windows.Forms.ToolTip( this.components ); + this.tabs.SuspendLayout(); + this.tabGeneral.SuspendLayout(); + this.gUpdaterSettings.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.gHelpAndSupport.SuspendLayout(); + this.gInformation.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nAnnouncements)).BeginInit(); + this.gBasic.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxPlayersPerWorld)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nPort)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nUploadBandwidth)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxPlayers)).BeginInit(); + this.tabChat.SuspendLayout(); + this.gChatColors.SuspendLayout(); + this.gAppearence.SuspendLayout(); + this.tabWorlds.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvWorlds)).BeginInit(); + this.tabRanks.SuspendLayout(); + this.gPermissionLimits.SuspendLayout(); + this.gRankOptions.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nFillLimit)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nCopyPasteSlots)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntiGriefSeconds)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nDrawLimit)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nKickIdle)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntiGriefBlocks)).BeginInit(); + this.tabSecurity.SuspendLayout(); + this.gBlockDB.SuspendLayout(); + this.gSecurityMisc.SuspendLayout(); + this.gSpamChat.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamMaxWarnings)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSpamMute)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamInterval)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamMessageCount)).BeginInit(); + this.gVerify.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxConnectionsPerIP)).BeginInit(); + this.tabSavingAndBackup.SuspendLayout(); + this.gDataBackup.SuspendLayout(); + this.gSaving.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nSaveInterval)).BeginInit(); + this.gBackups.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxBackupSize)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxBackups)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nBackupInterval)).BeginInit(); + this.tabLogging.SuspendLayout(); + this.gLogFile.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nLogLimit)).BeginInit(); + this.gConsole.SuspendLayout(); + this.tabIRC.SuspendLayout(); + this.gIRCOptions.SuspendLayout(); + this.gIRCNetwork.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nIRCDelay)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nIRCBotPort)).BeginInit(); + this.tabAdvanced.SuspendLayout(); + this.gPerformance.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nTickInterval)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nThrottling)).BeginInit(); + this.gAdvancedMisc.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxUndoStates)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxUndo)).BeginInit(); + this.gCrashReport.SuspendLayout(); + this.SuspendLayout(); + // + // tabs + // + this.tabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabs.Controls.Add( this.tabGeneral ); + this.tabs.Controls.Add( this.tabChat ); + this.tabs.Controls.Add( this.tabWorlds ); + this.tabs.Controls.Add( this.tabRanks ); + this.tabs.Controls.Add( this.tabSecurity ); + this.tabs.Controls.Add( this.tabSavingAndBackup ); + this.tabs.Controls.Add( this.tabLogging ); + this.tabs.Controls.Add( this.tabIRC ); + this.tabs.Controls.Add( this.tabAdvanced ); + this.tabs.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tabs.Location = new System.Drawing.Point( 12, 12 ); + this.tabs.Name = "tabs"; + this.tabs.SelectedIndex = 0; + this.tabs.Size = new System.Drawing.Size( 660, 510 ); + this.tabs.TabIndex = 0; + // + // tabGeneral + // + this.tabGeneral.Controls.Add( this.gUpdaterSettings ); + this.tabGeneral.Controls.Add( this.groupBox2 ); + this.tabGeneral.Controls.Add( this.gHelpAndSupport ); + this.tabGeneral.Controls.Add( this.gInformation ); + this.tabGeneral.Controls.Add( this.gBasic ); + this.tabGeneral.Location = new System.Drawing.Point( 4, 24 ); + this.tabGeneral.Name = "tabGeneral"; + this.tabGeneral.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabGeneral.Size = new System.Drawing.Size( 652, 481 ); + this.tabGeneral.TabIndex = 0; + this.tabGeneral.Text = "General"; + this.tabGeneral.UseVisualStyleBackColor = true; + // + // gUpdaterSettings + // + this.gUpdaterSettings.Controls.Add( this.bShowAdvancedUpdaterSettings ); + this.gUpdaterSettings.Controls.Add( this.cUpdaterMode ); + this.gUpdaterSettings.Controls.Add( this.lUpdater ); + this.gUpdaterSettings.Location = new System.Drawing.Point( 8, 247 ); + this.gUpdaterSettings.Name = "gUpdaterSettings"; + this.gUpdaterSettings.Size = new System.Drawing.Size( 636, 54 ); + this.gUpdaterSettings.TabIndex = 2; + this.gUpdaterSettings.TabStop = false; + this.gUpdaterSettings.Text = "Updater Settings"; + // + // bShowAdvancedUpdaterSettings + // + this.bShowAdvancedUpdaterSettings.Location = new System.Drawing.Point( 318, 22 ); + this.bShowAdvancedUpdaterSettings.Name = "bShowAdvancedUpdaterSettings"; + this.bShowAdvancedUpdaterSettings.Size = new System.Drawing.Size( 75, 23 ); + this.bShowAdvancedUpdaterSettings.TabIndex = 2; + this.bShowAdvancedUpdaterSettings.Text = "Advanced"; + this.bShowAdvancedUpdaterSettings.UseVisualStyleBackColor = true; + this.bShowAdvancedUpdaterSettings.Click += new System.EventHandler( this.bShowAdvancedUpdaterSettings_Click ); + // + // cUpdaterMode + // + this.cUpdaterMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cUpdaterMode.FormattingEnabled = true; + this.cUpdaterMode.Items.AddRange( new object[] { + "Disabled", + "Notify about availability", + "Prompt to install", + "Fully automatic"} ); + this.cUpdaterMode.Location = new System.Drawing.Point( 123, 22 ); + this.cUpdaterMode.Name = "cUpdaterMode"; + this.cUpdaterMode.Size = new System.Drawing.Size( 189, 23 ); + this.cUpdaterMode.TabIndex = 1; + this.cUpdaterMode.SelectedIndexChanged += new System.EventHandler( this.cUpdaterMode_SelectedIndexChanged ); + // + // lUpdater + // + this.lUpdater.AutoSize = true; + this.lUpdater.Location = new System.Drawing.Point( 6, 25 ); + this.lUpdater.Name = "lUpdater"; + this.lUpdater.Size = new System.Drawing.Size( 111, 15 ); + this.lUpdater.TabIndex = 0; + this.lUpdater.Text = "fCraft update check"; + // + // groupBox2 + // + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.groupBox2.Controls.Add( this.bChangelog ); + this.groupBox2.Controls.Add( this.bCredits ); + this.groupBox2.Controls.Add( this.bReadme ); + this.groupBox2.Location = new System.Drawing.Point( 329, 413 ); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size( 315, 55 ); + this.groupBox2.TabIndex = 4; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "About fCraft"; + // + // bChangelog + // + this.bChangelog.Location = new System.Drawing.Point( 107, 20 ); + this.bChangelog.Name = "bChangelog"; + this.bChangelog.Size = new System.Drawing.Size( 95, 23 ); + this.bChangelog.TabIndex = 2; + this.bChangelog.Text = "Changelog"; + this.bChangelog.UseVisualStyleBackColor = true; + this.bChangelog.Click += new System.EventHandler( this.bChangelog_Click ); + // + // bCredits + // + this.bCredits.Location = new System.Drawing.Point( 208, 20 ); + this.bCredits.Name = "bCredits"; + this.bCredits.Size = new System.Drawing.Size( 95, 23 ); + this.bCredits.TabIndex = 1; + this.bCredits.Text = "Credits"; + this.bCredits.UseVisualStyleBackColor = true; + this.bCredits.Click += new System.EventHandler( this.bCredits_Click ); + // + // bReadme + // + this.bReadme.Enabled = false; + this.bReadme.Location = new System.Drawing.Point( 6, 20 ); + this.bReadme.Name = "bReadme"; + this.bReadme.Size = new System.Drawing.Size( 95, 23 ); + this.bReadme.TabIndex = 0; + this.bReadme.Text = "Readme"; + this.bReadme.UseVisualStyleBackColor = true; + this.bReadme.Click += new System.EventHandler( this.bReadme_Click ); + // + // gHelpAndSupport + // + this.gHelpAndSupport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.gHelpAndSupport.Controls.Add( this.bOpenWiki ); + this.gHelpAndSupport.Controls.Add( this.bReportABug ); + this.gHelpAndSupport.Location = new System.Drawing.Point( 8, 413 ); + this.gHelpAndSupport.Name = "gHelpAndSupport"; + this.gHelpAndSupport.Size = new System.Drawing.Size( 315, 55 ); + this.gHelpAndSupport.TabIndex = 3; + this.gHelpAndSupport.TabStop = false; + this.gHelpAndSupport.Text = "Help and Support"; + // + // bOpenWiki + // + this.bOpenWiki.Location = new System.Drawing.Point( 9, 20 ); + this.bOpenWiki.Name = "bOpenWiki"; + this.bOpenWiki.Size = new System.Drawing.Size( 140, 23 ); + this.bOpenWiki.TabIndex = 0; + this.bOpenWiki.Text = "Open fCraft Wiki"; + this.bOpenWiki.UseVisualStyleBackColor = true; + this.bOpenWiki.Click += new System.EventHandler( this.bOpenWiki_Click ); + // + // bReportABug + // + this.bReportABug.Location = new System.Drawing.Point( 155, 20 ); + this.bReportABug.Name = "bReportABug"; + this.bReportABug.Size = new System.Drawing.Size( 140, 23 ); + this.bReportABug.TabIndex = 1; + this.bReportABug.Text = "Report a Bug"; + this.bReportABug.UseVisualStyleBackColor = true; + this.bReportABug.Click += new System.EventHandler( this.bReportABug_Click ); + // + // gInformation + // + this.gInformation.Controls.Add( this.bGreeting ); + this.gInformation.Controls.Add( this.lAnnouncementsUnits ); + this.gInformation.Controls.Add( this.nAnnouncements ); + this.gInformation.Controls.Add( this.xAnnouncements ); + this.gInformation.Controls.Add( this.bRules ); + this.gInformation.Controls.Add( this.bAnnouncements ); + this.gInformation.Location = new System.Drawing.Point( 8, 184 ); + this.gInformation.Name = "gInformation"; + this.gInformation.Size = new System.Drawing.Size( 636, 57 ); + this.gInformation.TabIndex = 1; + this.gInformation.TabStop = false; + this.gInformation.Text = "Information"; + // + // bGreeting + // + this.bGreeting.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bGreeting.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bGreeting.Location = new System.Drawing.Point( 538, 20 ); + this.bGreeting.Name = "bGreeting"; + this.bGreeting.Size = new System.Drawing.Size( 92, 28 ); + this.bGreeting.TabIndex = 5; + this.bGreeting.Text = "Edit Greeting"; + this.bGreeting.UseVisualStyleBackColor = true; + this.bGreeting.Click += new System.EventHandler( this.bGreeting_Click ); + // + // lAnnouncementsUnits + // + this.lAnnouncementsUnits.AutoSize = true; + this.lAnnouncementsUnits.Location = new System.Drawing.Point( 266, 27 ); + this.lAnnouncementsUnits.Name = "lAnnouncementsUnits"; + this.lAnnouncementsUnits.Size = new System.Drawing.Size( 28, 15 ); + this.lAnnouncementsUnits.TabIndex = 2; + this.lAnnouncementsUnits.Text = "min"; + // + // nAnnouncements + // + this.nAnnouncements.Enabled = false; + this.nAnnouncements.Location = new System.Drawing.Point( 210, 25 ); + this.nAnnouncements.Maximum = new decimal( new int[] { + 60, + 0, + 0, + 0} ); + this.nAnnouncements.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nAnnouncements.Name = "nAnnouncements"; + this.nAnnouncements.Size = new System.Drawing.Size( 50, 21 ); + this.nAnnouncements.TabIndex = 1; + this.nAnnouncements.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // xAnnouncements + // + this.xAnnouncements.AutoSize = true; + this.xAnnouncements.Location = new System.Drawing.Point( 24, 26 ); + this.xAnnouncements.Name = "xAnnouncements"; + this.xAnnouncements.Size = new System.Drawing.Size( 180, 19 ); + this.xAnnouncements.TabIndex = 0; + this.xAnnouncements.Text = "Show announcements every"; + this.xAnnouncements.UseVisualStyleBackColor = true; + this.xAnnouncements.CheckedChanged += new System.EventHandler( this.xAnnouncements_CheckedChanged ); + // + // bRules + // + this.bRules.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bRules.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bRules.Location = new System.Drawing.Point( 445, 20 ); + this.bRules.Name = "bRules"; + this.bRules.Size = new System.Drawing.Size( 87, 28 ); + this.bRules.TabIndex = 4; + this.bRules.Text = "Edit Rules"; + this.bRules.UseVisualStyleBackColor = true; + this.bRules.Click += new System.EventHandler( this.bRules_Click ); + // + // bAnnouncements + // + this.bAnnouncements.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bAnnouncements.Enabled = false; + this.bAnnouncements.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bAnnouncements.Location = new System.Drawing.Point( 301, 20 ); + this.bAnnouncements.Name = "bAnnouncements"; + this.bAnnouncements.Size = new System.Drawing.Size( 138, 28 ); + this.bAnnouncements.TabIndex = 3; + this.bAnnouncements.Text = "Edit Announcements"; + this.bAnnouncements.UseVisualStyleBackColor = true; + this.bAnnouncements.Click += new System.EventHandler( this.bAnnouncements_Click ); + // + // gBasic + // + this.gBasic.Controls.Add( this.nMaxPlayersPerWorld ); + this.gBasic.Controls.Add( this.lMaxPlayersPerWorld ); + this.gBasic.Controls.Add( this.bPortCheck ); + this.gBasic.Controls.Add( this.lPort ); + this.gBasic.Controls.Add( this.nPort ); + this.gBasic.Controls.Add( this.cDefaultRank ); + this.gBasic.Controls.Add( this.lDefaultRank ); + this.gBasic.Controls.Add( this.lUploadBandwidth ); + this.gBasic.Controls.Add( this.bMeasure ); + this.gBasic.Controls.Add( this.tServerName ); + this.gBasic.Controls.Add( this.lUploadBandwidthUnits ); + this.gBasic.Controls.Add( this.lServerName ); + this.gBasic.Controls.Add( this.nUploadBandwidth ); + this.gBasic.Controls.Add( this.tMOTD ); + this.gBasic.Controls.Add( this.lMOTD ); + this.gBasic.Controls.Add( this.cPublic ); + this.gBasic.Controls.Add( this.nMaxPlayers ); + this.gBasic.Controls.Add( this.lPublic ); + this.gBasic.Controls.Add( this.lMaxPlayers ); + this.gBasic.Location = new System.Drawing.Point( 8, 13 ); + this.gBasic.Name = "gBasic"; + this.gBasic.Size = new System.Drawing.Size( 636, 165 ); + this.gBasic.TabIndex = 0; + this.gBasic.TabStop = false; + this.gBasic.Text = "Basic Settings"; + // + // nMaxPlayersPerWorld + // + this.nMaxPlayersPerWorld.Location = new System.Drawing.Point( 440, 74 ); + this.nMaxPlayersPerWorld.Maximum = new decimal( new int[] { + 127, + 0, + 0, + 0} ); + this.nMaxPlayersPerWorld.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxPlayersPerWorld.Name = "nMaxPlayersPerWorld"; + this.nMaxPlayersPerWorld.Size = new System.Drawing.Size( 75, 21 ); + this.nMaxPlayersPerWorld.TabIndex = 12; + this.nMaxPlayersPerWorld.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxPlayersPerWorld.Validating += new System.ComponentModel.CancelEventHandler( this.nMaxPlayerPerWorld_Validating ); + // + // lMaxPlayersPerWorld + // + this.lMaxPlayersPerWorld.AutoSize = true; + this.lMaxPlayersPerWorld.Location = new System.Drawing.Point( 299, 76 ); + this.lMaxPlayersPerWorld.Name = "lMaxPlayersPerWorld"; + this.lMaxPlayersPerWorld.Size = new System.Drawing.Size( 135, 15 ); + this.lMaxPlayersPerWorld.TabIndex = 11; + this.lMaxPlayersPerWorld.Text = "Max players (per world)"; + // + // bPortCheck + // + this.bPortCheck.Location = new System.Drawing.Point( 204, 99 ); + this.bPortCheck.Name = "bPortCheck"; + this.bPortCheck.Size = new System.Drawing.Size( 68, 23 ); + this.bPortCheck.TabIndex = 8; + this.bPortCheck.Text = "Check"; + this.bPortCheck.UseVisualStyleBackColor = true; + this.bPortCheck.Click += new System.EventHandler( this.bPortCheck_Click ); + // + // lPort + // + this.lPort.AutoSize = true; + this.lPort.Location = new System.Drawing.Point( 42, 103 ); + this.lPort.Name = "lPort"; + this.lPort.Size = new System.Drawing.Size( 75, 15 ); + this.lPort.TabIndex = 6; + this.lPort.Text = "Port number"; + // + // nPort + // + this.nPort.Location = new System.Drawing.Point( 123, 101 ); + this.nPort.Maximum = new decimal( new int[] { + 65535, + 0, + 0, + 0} ); + this.nPort.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nPort.Name = "nPort"; + this.nPort.Size = new System.Drawing.Size( 75, 21 ); + this.nPort.TabIndex = 7; + this.nPort.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // cDefaultRank + // + this.cDefaultRank.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cDefaultRank.FormattingEnabled = true; + this.cDefaultRank.Location = new System.Drawing.Point( 440, 128 ); + this.cDefaultRank.Name = "cDefaultRank"; + this.cDefaultRank.Size = new System.Drawing.Size( 170, 23 ); + this.cDefaultRank.TabIndex = 18; + this.cDefaultRank.SelectedIndexChanged += new System.EventHandler( this.cDefaultRank_SelectedIndexChanged ); + // + // lDefaultRank + // + this.lDefaultRank.AutoSize = true; + this.lDefaultRank.Location = new System.Drawing.Point( 361, 131 ); + this.lDefaultRank.Name = "lDefaultRank"; + this.lDefaultRank.Size = new System.Drawing.Size( 73, 15 ); + this.lDefaultRank.TabIndex = 17; + this.lDefaultRank.Text = "Default rank"; + // + // lUploadBandwidth + // + this.lUploadBandwidth.AutoSize = true; + this.lUploadBandwidth.Location = new System.Drawing.Point( 327, 103 ); + this.lUploadBandwidth.Name = "lUploadBandwidth"; + this.lUploadBandwidth.Size = new System.Drawing.Size( 107, 15 ); + this.lUploadBandwidth.TabIndex = 13; + this.lUploadBandwidth.Text = "Upload bandwidth"; + // + // bMeasure + // + this.bMeasure.Location = new System.Drawing.Point( 559, 99 ); + this.bMeasure.Name = "bMeasure"; + this.bMeasure.Size = new System.Drawing.Size( 71, 23 ); + this.bMeasure.TabIndex = 16; + this.bMeasure.Text = "Measure"; + this.bMeasure.UseVisualStyleBackColor = true; + this.bMeasure.Click += new System.EventHandler( this.bMeasure_Click ); + // + // tServerName + // + this.tServerName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tServerName.HideSelection = false; + this.tServerName.Location = new System.Drawing.Point( 123, 20 ); + this.tServerName.MaxLength = 64; + this.tServerName.Name = "tServerName"; + this.tServerName.Size = new System.Drawing.Size( 507, 21 ); + this.tServerName.TabIndex = 1; + // + // lUploadBandwidthUnits + // + this.lUploadBandwidthUnits.AutoSize = true; + this.lUploadBandwidthUnits.Location = new System.Drawing.Point( 521, 103 ); + this.lUploadBandwidthUnits.Name = "lUploadBandwidthUnits"; + this.lUploadBandwidthUnits.Size = new System.Drawing.Size( 32, 15 ); + this.lUploadBandwidthUnits.TabIndex = 15; + this.lUploadBandwidthUnits.Text = "KB/s"; + // + // lServerName + // + this.lServerName.AutoSize = true; + this.lServerName.Location = new System.Drawing.Point( 40, 23 ); + this.lServerName.Name = "lServerName"; + this.lServerName.Size = new System.Drawing.Size( 77, 15 ); + this.lServerName.TabIndex = 0; + this.lServerName.Text = "Server name"; + // + // nUploadBandwidth + // + this.nUploadBandwidth.Increment = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + this.nUploadBandwidth.Location = new System.Drawing.Point( 440, 101 ); + this.nUploadBandwidth.Maximum = new decimal( new int[] { + 10000, + 0, + 0, + 0} ); + this.nUploadBandwidth.Minimum = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + this.nUploadBandwidth.Name = "nUploadBandwidth"; + this.nUploadBandwidth.Size = new System.Drawing.Size( 75, 21 ); + this.nUploadBandwidth.TabIndex = 14; + this.nUploadBandwidth.Value = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + // + // tMOTD + // + this.tMOTD.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tMOTD.Location = new System.Drawing.Point( 123, 47 ); + this.tMOTD.MaxLength = 64; + this.tMOTD.Name = "tMOTD"; + this.tMOTD.Size = new System.Drawing.Size( 507, 21 ); + this.tMOTD.TabIndex = 3; + // + // lMOTD + // + this.lMOTD.AutoSize = true; + this.lMOTD.Location = new System.Drawing.Point( 74, 50 ); + this.lMOTD.Name = "lMOTD"; + this.lMOTD.Size = new System.Drawing.Size( 43, 15 ); + this.lMOTD.TabIndex = 2; + this.lMOTD.Text = "MOTD"; + // + // cPublic + // + this.cPublic.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cPublic.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.cPublic.FormattingEnabled = true; + this.cPublic.Items.AddRange( new object[] { + "Public", + "Private"} ); + this.cPublic.Location = new System.Drawing.Point( 123, 128 ); + this.cPublic.Name = "cPublic"; + this.cPublic.Size = new System.Drawing.Size( 75, 23 ); + this.cPublic.TabIndex = 10; + // + // nMaxPlayers + // + this.nMaxPlayers.Location = new System.Drawing.Point( 123, 74 ); + this.nMaxPlayers.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nMaxPlayers.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxPlayers.Name = "nMaxPlayers"; + this.nMaxPlayers.Size = new System.Drawing.Size( 75, 21 ); + this.nMaxPlayers.TabIndex = 5; + this.nMaxPlayers.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxPlayers.ValueChanged += new System.EventHandler( this.nMaxPlayers_ValueChanged ); + // + // lPublic + // + this.lPublic.AutoSize = true; + this.lPublic.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lPublic.Location = new System.Drawing.Point( 14, 131 ); + this.lPublic.Name = "lPublic"; + this.lPublic.Size = new System.Drawing.Size( 103, 15 ); + this.lPublic.TabIndex = 9; + this.lPublic.Text = "Server visibility"; + // + // lMaxPlayers + // + this.lMaxPlayers.AutoSize = true; + this.lMaxPlayers.Location = new System.Drawing.Point( 10, 76 ); + this.lMaxPlayers.Name = "lMaxPlayers"; + this.lMaxPlayers.Size = new System.Drawing.Size( 107, 15 ); + this.lMaxPlayers.TabIndex = 4; + this.lMaxPlayers.Text = "Max players (total)"; + // + // tabChat + // + this.tabChat.Controls.Add( this.gChatColors ); + this.tabChat.Controls.Add( this.gAppearence ); + this.tabChat.Controls.Add( this.chatPreview ); + this.tabChat.Location = new System.Drawing.Point( 4, 24 ); + this.tabChat.Name = "tabChat"; + this.tabChat.Padding = new System.Windows.Forms.Padding( 5, 5, 5, 10 ); + this.tabChat.Size = new System.Drawing.Size( 652, 481 ); + this.tabChat.TabIndex = 10; + this.tabChat.Text = "Chat"; + this.tabChat.UseVisualStyleBackColor = true; + // + // gChatColors + // + this.gChatColors.Controls.Add( this.lColorMe ); + this.gChatColors.Controls.Add( this.bColorMe ); + this.gChatColors.Controls.Add( this.lColorWarning ); + this.gChatColors.Controls.Add( this.bColorWarning ); + this.gChatColors.Controls.Add( this.bColorSys ); + this.gChatColors.Controls.Add( this.lColorSys ); + this.gChatColors.Controls.Add( this.bColorPM ); + this.gChatColors.Controls.Add( this.lColorHelp ); + this.gChatColors.Controls.Add( this.lColorPM ); + this.gChatColors.Controls.Add( this.lColorSay ); + this.gChatColors.Controls.Add( this.bColorAnnouncement ); + this.gChatColors.Controls.Add( this.lColorAnnouncement ); + this.gChatColors.Controls.Add( this.bColorHelp ); + this.gChatColors.Controls.Add( this.bColorSay ); + this.gChatColors.Location = new System.Drawing.Point( 8, 8 ); + this.gChatColors.Name = "gChatColors"; + this.gChatColors.Size = new System.Drawing.Size( 636, 139 ); + this.gChatColors.TabIndex = 0; + this.gChatColors.TabStop = false; + this.gChatColors.Text = "Colors"; + // + // lColorMe + // + this.lColorMe.AutoSize = true; + this.lColorMe.Location = new System.Drawing.Point( 402, 82 ); + this.lColorMe.Name = "lColorMe"; + this.lColorMe.Size = new System.Drawing.Size( 117, 15 ); + this.lColorMe.TabIndex = 12; + this.lColorMe.Text = "/Me command color"; + // + // bColorMe + // + this.bColorMe.BackColor = System.Drawing.Color.White; + this.bColorMe.Location = new System.Drawing.Point( 525, 78 ); + this.bColorMe.Name = "bColorMe"; + this.bColorMe.Size = new System.Drawing.Size( 100, 23 ); + this.bColorMe.TabIndex = 13; + this.bColorMe.UseVisualStyleBackColor = false; + this.bColorMe.Click += new System.EventHandler( this.bColorMe_Click ); + // + // lColorWarning + // + this.lColorWarning.AutoSize = true; + this.lColorWarning.Location = new System.Drawing.Point( 69, 53 ); + this.lColorWarning.Name = "lColorWarning"; + this.lColorWarning.Size = new System.Drawing.Size( 118, 15 ); + this.lColorWarning.TabIndex = 2; + this.lColorWarning.Text = "Warning / error color"; + // + // bColorWarning + // + this.bColorWarning.BackColor = System.Drawing.Color.White; + this.bColorWarning.Location = new System.Drawing.Point( 193, 49 ); + this.bColorWarning.Name = "bColorWarning"; + this.bColorWarning.Size = new System.Drawing.Size( 100, 23 ); + this.bColorWarning.TabIndex = 3; + this.bColorWarning.UseVisualStyleBackColor = false; + this.bColorWarning.Click += new System.EventHandler( this.bColorWarning_Click ); + // + // bColorSys + // + this.bColorSys.BackColor = System.Drawing.Color.White; + this.bColorSys.Location = new System.Drawing.Point( 193, 20 ); + this.bColorSys.Name = "bColorSys"; + this.bColorSys.Size = new System.Drawing.Size( 100, 23 ); + this.bColorSys.TabIndex = 1; + this.bColorSys.UseVisualStyleBackColor = false; + this.bColorSys.Click += new System.EventHandler( this.bColorSys_Click ); + // + // lColorSys + // + this.lColorSys.AutoSize = true; + this.lColorSys.Location = new System.Drawing.Point( 56, 24 ); + this.lColorSys.Name = "lColorSys"; + this.lColorSys.Size = new System.Drawing.Size( 131, 15 ); + this.lColorSys.TabIndex = 0; + this.lColorSys.Text = "System message color"; + // + // bColorPM + // + this.bColorPM.BackColor = System.Drawing.Color.White; + this.bColorPM.Location = new System.Drawing.Point( 193, 78 ); + this.bColorPM.Name = "bColorPM"; + this.bColorPM.Size = new System.Drawing.Size( 100, 23 ); + this.bColorPM.TabIndex = 5; + this.bColorPM.UseVisualStyleBackColor = false; + this.bColorPM.Click += new System.EventHandler( this.bColorPM_Click ); + // + // lColorHelp + // + this.lColorHelp.AutoSize = true; + this.lColorHelp.Location = new System.Drawing.Point( 70, 111 ); + this.lColorHelp.Name = "lColorHelp"; + this.lColorHelp.Size = new System.Drawing.Size( 117, 15 ); + this.lColorHelp.TabIndex = 6; + this.lColorHelp.Text = "Help message color"; + // + // lColorPM + // + this.lColorPM.AutoSize = true; + this.lColorPM.Location = new System.Drawing.Point( 26, 82 ); + this.lColorPM.Name = "lColorPM"; + this.lColorPM.Size = new System.Drawing.Size( 161, 15 ); + this.lColorPM.TabIndex = 4; + this.lColorPM.Text = "Private / rank message color"; + // + // lColorSay + // + this.lColorSay.AutoSize = true; + this.lColorSay.Location = new System.Drawing.Point( 407, 53 ); + this.lColorSay.Name = "lColorSay"; + this.lColorSay.Size = new System.Drawing.Size( 114, 15 ); + this.lColorSay.TabIndex = 10; + this.lColorSay.Text = "/Say message color"; + // + // bColorAnnouncement + // + this.bColorAnnouncement.BackColor = System.Drawing.Color.White; + this.bColorAnnouncement.Location = new System.Drawing.Point( 525, 20 ); + this.bColorAnnouncement.Name = "bColorAnnouncement"; + this.bColorAnnouncement.Size = new System.Drawing.Size( 100, 23 ); + this.bColorAnnouncement.TabIndex = 9; + this.bColorAnnouncement.UseVisualStyleBackColor = false; + this.bColorAnnouncement.Click += new System.EventHandler( this.bColorAnnouncement_Click ); + // + // lColorAnnouncement + // + this.lColorAnnouncement.AutoSize = true; + this.lColorAnnouncement.Location = new System.Drawing.Point( 342, 24 ); + this.lColorAnnouncement.Name = "lColorAnnouncement"; + this.lColorAnnouncement.Size = new System.Drawing.Size( 182, 15 ); + this.lColorAnnouncement.TabIndex = 8; + this.lColorAnnouncement.Text = "Announcement and /Rules color"; + // + // bColorHelp + // + this.bColorHelp.BackColor = System.Drawing.Color.White; + this.bColorHelp.Location = new System.Drawing.Point( 193, 107 ); + this.bColorHelp.Name = "bColorHelp"; + this.bColorHelp.Size = new System.Drawing.Size( 100, 23 ); + this.bColorHelp.TabIndex = 7; + this.bColorHelp.UseVisualStyleBackColor = false; + this.bColorHelp.Click += new System.EventHandler( this.bColorHelp_Click ); + // + // bColorSay + // + this.bColorSay.BackColor = System.Drawing.Color.White; + this.bColorSay.Location = new System.Drawing.Point( 525, 49 ); + this.bColorSay.Name = "bColorSay"; + this.bColorSay.Size = new System.Drawing.Size( 100, 23 ); + this.bColorSay.TabIndex = 11; + this.bColorSay.UseVisualStyleBackColor = false; + this.bColorSay.Click += new System.EventHandler( this.bColorSay_Click ); + // + // gAppearence + // + this.gAppearence.Controls.Add( this.xShowConnectionMessages ); + this.gAppearence.Controls.Add( this.xShowJoinedWorldMessages ); + this.gAppearence.Controls.Add( this.xRankColorsInWorldNames ); + this.gAppearence.Controls.Add( this.xRankPrefixesInList ); + this.gAppearence.Controls.Add( this.xRankPrefixesInChat ); + this.gAppearence.Controls.Add( this.xRankColorsInChat ); + this.gAppearence.Location = new System.Drawing.Point( 7, 153 ); + this.gAppearence.Name = "gAppearence"; + this.gAppearence.Size = new System.Drawing.Size( 637, 97 ); + this.gAppearence.TabIndex = 1; + this.gAppearence.TabStop = false; + this.gAppearence.Text = "Appearence Tweaks"; + // + // xShowConnectionMessages + // + this.xShowConnectionMessages.AutoSize = true; + this.xShowConnectionMessages.Location = new System.Drawing.Point( 325, 45 ); + this.xShowConnectionMessages.Name = "xShowConnectionMessages"; + this.xShowConnectionMessages.Size = new System.Drawing.Size( 306, 19 ); + this.xShowConnectionMessages.TabIndex = 4; + this.xShowConnectionMessages.Text = "Show a message when players join/leave SERVER."; + this.xShowConnectionMessages.UseVisualStyleBackColor = true; + // + // xShowJoinedWorldMessages + // + this.xShowJoinedWorldMessages.AutoSize = true; + this.xShowJoinedWorldMessages.Location = new System.Drawing.Point( 325, 20 ); + this.xShowJoinedWorldMessages.Name = "xShowJoinedWorldMessages"; + this.xShowJoinedWorldMessages.Size = new System.Drawing.Size( 261, 19 ); + this.xShowJoinedWorldMessages.TabIndex = 3; + this.xShowJoinedWorldMessages.Text = "Show a message when players join worlds."; + this.xShowJoinedWorldMessages.UseVisualStyleBackColor = true; + // + // xRankColorsInWorldNames + // + this.xRankColorsInWorldNames.AutoSize = true; + this.xRankColorsInWorldNames.Location = new System.Drawing.Point( 325, 70 ); + this.xRankColorsInWorldNames.Name = "xRankColorsInWorldNames"; + this.xRankColorsInWorldNames.Size = new System.Drawing.Size( 243, 19 ); + this.xRankColorsInWorldNames.TabIndex = 5; + this.xRankColorsInWorldNames.Text = "Color world names based on build rank."; + this.xRankColorsInWorldNames.UseVisualStyleBackColor = true; + // + // xRankPrefixesInList + // + this.xRankPrefixesInList.AutoSize = true; + this.xRankPrefixesInList.Location = new System.Drawing.Point( 44, 70 ); + this.xRankPrefixesInList.Name = "xRankPrefixesInList"; + this.xRankPrefixesInList.Size = new System.Drawing.Size( 219, 19 ); + this.xRankPrefixesInList.TabIndex = 2; + this.xRankPrefixesInList.Text = "Prefixes in player list (breaks skins)."; + this.xRankPrefixesInList.UseVisualStyleBackColor = true; + // + // xRankPrefixesInChat + // + this.xRankPrefixesInChat.AutoSize = true; + this.xRankPrefixesInChat.Location = new System.Drawing.Point( 25, 45 ); + this.xRankPrefixesInChat.Name = "xRankPrefixesInChat"; + this.xRankPrefixesInChat.Size = new System.Drawing.Size( 133, 19 ); + this.xRankPrefixesInChat.TabIndex = 1; + this.xRankPrefixesInChat.Text = "Show rank prefixes."; + this.xRankPrefixesInChat.UseVisualStyleBackColor = true; + this.xRankPrefixesInChat.CheckedChanged += new System.EventHandler( this.xRankPrefixesInChat_CheckedChanged ); + // + // xRankColorsInChat + // + this.xRankColorsInChat.AutoSize = true; + this.xRankColorsInChat.Location = new System.Drawing.Point( 25, 20 ); + this.xRankColorsInChat.Name = "xRankColorsInChat"; + this.xRankColorsInChat.Size = new System.Drawing.Size( 123, 19 ); + this.xRankColorsInChat.TabIndex = 0; + this.xRankColorsInChat.Text = "Show rank colors."; + this.xRankColorsInChat.UseVisualStyleBackColor = true; + // + // chatPreview + // + this.chatPreview.Location = new System.Drawing.Point( 7, 256 ); + this.chatPreview.Name = "chatPreview"; + this.chatPreview.Size = new System.Drawing.Size( 637, 241 ); + this.chatPreview.TabIndex = 2; + // + // tabWorlds + // + this.tabWorlds.Controls.Add( this.xWoMEnableEnvExtensions ); + this.tabWorlds.Controls.Add( this.bMapPath ); + this.tabWorlds.Controls.Add( this.xMapPath ); + this.tabWorlds.Controls.Add( this.tMapPath ); + this.tabWorlds.Controls.Add( this.lDefaultBuildRank ); + this.tabWorlds.Controls.Add( this.cDefaultBuildRank ); + this.tabWorlds.Controls.Add( this.cMainWorld ); + this.tabWorlds.Controls.Add( this.lMainWorld ); + this.tabWorlds.Controls.Add( this.bWorldEdit ); + this.tabWorlds.Controls.Add( this.bAddWorld ); + this.tabWorlds.Controls.Add( this.bWorldDelete ); + this.tabWorlds.Controls.Add( this.dgvWorlds ); + this.tabWorlds.Location = new System.Drawing.Point( 4, 24 ); + this.tabWorlds.Name = "tabWorlds"; + this.tabWorlds.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabWorlds.Size = new System.Drawing.Size( 652, 458 ); + this.tabWorlds.TabIndex = 9; + this.tabWorlds.Text = "Worlds"; + this.tabWorlds.UseVisualStyleBackColor = true; + // + // xWoMEnableEnvExtensions + // + this.xWoMEnableEnvExtensions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.xWoMEnableEnvExtensions.AutoSize = true; + this.xWoMEnableEnvExtensions.Location = new System.Drawing.Point( 8, 436 ); + this.xWoMEnableEnvExtensions.Name = "xWoMEnableEnvExtensions"; + this.xWoMEnableEnvExtensions.Size = new System.Drawing.Size( 267, 19 ); + this.xWoMEnableEnvExtensions.TabIndex = 22; + this.xWoMEnableEnvExtensions.Text = "Enable WoM environment extensions (/Env)."; + this.xWoMEnableEnvExtensions.UseVisualStyleBackColor = true; + // + // bMapPath + // + this.bMapPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bMapPath.Enabled = false; + this.bMapPath.Location = new System.Drawing.Point( 587, 409 ); + this.bMapPath.Name = "bMapPath"; + this.bMapPath.Size = new System.Drawing.Size( 57, 23 ); + this.bMapPath.TabIndex = 10; + this.bMapPath.Text = "Browse"; + this.bMapPath.UseVisualStyleBackColor = true; + this.bMapPath.Click += new System.EventHandler( this.bMapPath_Click ); + // + // xMapPath + // + this.xMapPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.xMapPath.AutoSize = true; + this.xMapPath.Location = new System.Drawing.Point( 8, 409 ); + this.xMapPath.Name = "xMapPath"; + this.xMapPath.Size = new System.Drawing.Size( 189, 19 ); + this.xMapPath.TabIndex = 8; + this.xMapPath.Text = "Custom path for storing maps:"; + this.xMapPath.UseVisualStyleBackColor = true; + this.xMapPath.CheckedChanged += new System.EventHandler( this.xMapPath_CheckedChanged ); + // + // tMapPath + // + this.tMapPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tMapPath.Enabled = false; + this.tMapPath.Font = new System.Drawing.Font( "Lucida Console", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tMapPath.Location = new System.Drawing.Point( 203, 411 ); + this.tMapPath.Name = "tMapPath"; + this.tMapPath.Size = new System.Drawing.Size( 378, 19 ); + this.tMapPath.TabIndex = 9; + // + // lDefaultBuildRank + // + this.lDefaultBuildRank.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lDefaultBuildRank.AutoSize = true; + this.lDefaultBuildRank.Location = new System.Drawing.Point( 24, 381 ); + this.lDefaultBuildRank.Name = "lDefaultBuildRank"; + this.lDefaultBuildRank.Size = new System.Drawing.Size( 342, 15 ); + this.lDefaultBuildRank.TabIndex = 6; + this.lDefaultBuildRank.Text = "Default rank requirement for building on newly-loaded worlds:"; + // + // cDefaultBuildRank + // + this.cDefaultBuildRank.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cDefaultBuildRank.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cDefaultBuildRank.FormattingEnabled = true; + this.cDefaultBuildRank.Location = new System.Drawing.Point( 372, 378 ); + this.cDefaultBuildRank.Name = "cDefaultBuildRank"; + this.cDefaultBuildRank.Size = new System.Drawing.Size( 121, 23 ); + this.cDefaultBuildRank.TabIndex = 7; + this.cDefaultBuildRank.SelectedIndexChanged += new System.EventHandler( this.cDefaultBuildRank_SelectedIndexChanged ); + // + // cMainWorld + // + this.cMainWorld.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.cMainWorld.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cMainWorld.Location = new System.Drawing.Point( 542, 17 ); + this.cMainWorld.Name = "cMainWorld"; + this.cMainWorld.Size = new System.Drawing.Size( 102, 23 ); + this.cMainWorld.TabIndex = 5; + // + // lMainWorld + // + this.lMainWorld.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.lMainWorld.AutoSize = true; + this.lMainWorld.Location = new System.Drawing.Point( 465, 20 ); + this.lMainWorld.Name = "lMainWorld"; + this.lMainWorld.Size = new System.Drawing.Size( 71, 15 ); + this.lMainWorld.TabIndex = 4; + this.lMainWorld.Text = "Main world:"; + // + // bWorldEdit + // + this.bWorldEdit.Enabled = false; + this.bWorldEdit.Location = new System.Drawing.Point( 114, 13 ); + this.bWorldEdit.Name = "bWorldEdit"; + this.bWorldEdit.Size = new System.Drawing.Size( 100, 28 ); + this.bWorldEdit.TabIndex = 2; + this.bWorldEdit.Text = "Edit"; + this.bWorldEdit.UseVisualStyleBackColor = true; + this.bWorldEdit.Click += new System.EventHandler( this.bWorldEdit_Click ); + // + // bAddWorld + // + this.bAddWorld.Location = new System.Drawing.Point( 8, 13 ); + this.bAddWorld.Name = "bAddWorld"; + this.bAddWorld.Size = new System.Drawing.Size( 100, 28 ); + this.bAddWorld.TabIndex = 1; + this.bAddWorld.Text = "Add World"; + this.bAddWorld.UseVisualStyleBackColor = true; + this.bAddWorld.Click += new System.EventHandler( this.bAddWorld_Click ); + // + // bWorldDelete + // + this.bWorldDelete.Enabled = false; + this.bWorldDelete.Location = new System.Drawing.Point( 220, 13 ); + this.bWorldDelete.Name = "bWorldDelete"; + this.bWorldDelete.Size = new System.Drawing.Size( 100, 28 ); + this.bWorldDelete.TabIndex = 3; + this.bWorldDelete.Text = "Delete World"; + this.bWorldDelete.UseVisualStyleBackColor = true; + this.bWorldDelete.Click += new System.EventHandler( this.bWorldDel_Click ); + // + // dgvWorlds + // + this.dgvWorlds.AllowUserToAddRows = false; + this.dgvWorlds.AllowUserToDeleteRows = false; + this.dgvWorlds.AllowUserToOrderColumns = true; + this.dgvWorlds.AllowUserToResizeRows = false; + this.dgvWorlds.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.dgvWorlds.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvWorlds.Columns.AddRange( new System.Windows.Forms.DataGridViewColumn[] { + this.dgvcName, + this.dgvcDescription, + this.dgvcAccess, + this.dgvcBuild, + this.dgvcBackup, + this.dgvcHidden, + this.dgvcBlockDB} ); + this.dgvWorlds.Location = new System.Drawing.Point( 8, 47 ); + this.dgvWorlds.MultiSelect = false; + this.dgvWorlds.Name = "dgvWorlds"; + this.dgvWorlds.RowHeadersVisible = false; + dataGridViewCellStyle2.Padding = new System.Windows.Forms.Padding( 0, 1, 0, 1 ); + this.dgvWorlds.RowsDefaultCellStyle = dataGridViewCellStyle2; + this.dgvWorlds.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dgvWorlds.Size = new System.Drawing.Size( 636, 325 ); + this.dgvWorlds.TabIndex = 0; + this.dgvWorlds.SelectionChanged += new System.EventHandler( this.dgvWorlds_Click ); + this.dgvWorlds.Click += new System.EventHandler( this.dgvWorlds_Click ); + // + // dgvcName + // + this.dgvcName.DataPropertyName = "Name"; + this.dgvcName.HeaderText = "World Name"; + this.dgvcName.Name = "dgvcName"; + this.dgvcName.Width = 110; + // + // dgvcDescription + // + this.dgvcDescription.DataPropertyName = "Description"; + this.dgvcDescription.HeaderText = ""; + this.dgvcDescription.Name = "dgvcDescription"; + this.dgvcDescription.ReadOnly = true; + this.dgvcDescription.Width = 130; + // + // dgvcAccess + // + this.dgvcAccess.DataPropertyName = "AccessPermission"; + this.dgvcAccess.DisplayStyle = System.Windows.Forms.DataGridViewComboBoxDisplayStyle.ComboBox; + this.dgvcAccess.HeaderText = "Access"; + this.dgvcAccess.Name = "dgvcAccess"; + this.dgvcAccess.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + // + // dgvcBuild + // + this.dgvcBuild.DataPropertyName = "BuildPermission"; + this.dgvcBuild.DisplayStyle = System.Windows.Forms.DataGridViewComboBoxDisplayStyle.ComboBox; + this.dgvcBuild.HeaderText = "Build"; + this.dgvcBuild.Name = "dgvcBuild"; + this.dgvcBuild.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + // + // dgvcBackup + // + this.dgvcBackup.DataPropertyName = "Backup"; + this.dgvcBackup.DisplayStyle = System.Windows.Forms.DataGridViewComboBoxDisplayStyle.ComboBox; + this.dgvcBackup.HeaderText = "Backup"; + this.dgvcBackup.Name = "dgvcBackup"; + this.dgvcBackup.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.dgvcBackup.Width = 90; + // + // dgvcHidden + // + this.dgvcHidden.DataPropertyName = "Hidden"; + this.dgvcHidden.HeaderText = "Hide"; + this.dgvcHidden.Name = "dgvcHidden"; + this.dgvcHidden.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.dgvcHidden.Width = 40; + // + // dgvcBlockDB + // + this.dgvcBlockDB.DataPropertyName = "BlockDBEnabled"; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.dgvcBlockDB.DefaultCellStyle = dataGridViewCellStyle1; + this.dgvcBlockDB.HeaderText = "BlockDB"; + this.dgvcBlockDB.Name = "dgvcBlockDB"; + this.dgvcBlockDB.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.dgvcBlockDB.ThreeState = true; + this.dgvcBlockDB.Width = 60; + // + // tabRanks + // + this.tabRanks.Controls.Add( this.gPermissionLimits ); + this.tabRanks.Controls.Add( this.lRankList ); + this.tabRanks.Controls.Add( this.bLowerRank ); + this.tabRanks.Controls.Add( this.bRaiseRank ); + this.tabRanks.Controls.Add( this.gRankOptions ); + this.tabRanks.Controls.Add( this.bDeleteRank ); + this.tabRanks.Controls.Add( this.vPermissions ); + this.tabRanks.Controls.Add( this.bAddRank ); + this.tabRanks.Controls.Add( this.lPermissions ); + this.tabRanks.Controls.Add( this.vRanks ); + this.tabRanks.Location = new System.Drawing.Point( 4, 24 ); + this.tabRanks.Name = "tabRanks"; + this.tabRanks.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabRanks.Size = new System.Drawing.Size( 652, 481 ); + this.tabRanks.TabIndex = 2; + this.tabRanks.Text = "Ranks"; + this.tabRanks.UseVisualStyleBackColor = true; + // + // gPermissionLimits + // + this.gPermissionLimits.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gPermissionLimits.Controls.Add( this.permissionLimitBoxContainer ); + this.gPermissionLimits.Location = new System.Drawing.Point( 160, 292 ); + this.gPermissionLimits.Name = "gPermissionLimits"; + this.gPermissionLimits.Size = new System.Drawing.Size( 307, 186 ); + this.gPermissionLimits.TabIndex = 7; + this.gPermissionLimits.TabStop = false; + this.gPermissionLimits.Text = "Permission Limits"; + // + // permissionLimitBoxContainer + // + this.permissionLimitBoxContainer.AutoScroll = true; + this.permissionLimitBoxContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.permissionLimitBoxContainer.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.permissionLimitBoxContainer.Location = new System.Drawing.Point( 3, 17 ); + this.permissionLimitBoxContainer.Margin = new System.Windows.Forms.Padding( 0 ); + this.permissionLimitBoxContainer.Name = "permissionLimitBoxContainer"; + this.permissionLimitBoxContainer.Size = new System.Drawing.Size( 301, 166 ); + this.permissionLimitBoxContainer.TabIndex = 0; + this.permissionLimitBoxContainer.WrapContents = false; + // + // lRankList + // + this.lRankList.AutoSize = true; + this.lRankList.Location = new System.Drawing.Point( 8, 10 ); + this.lRankList.Name = "lRankList"; + this.lRankList.Size = new System.Drawing.Size( 58, 15 ); + this.lRankList.TabIndex = 0; + this.lRankList.Text = "Rank List"; + // + // bLowerRank + // + this.bLowerRank.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bLowerRank.Location = new System.Drawing.Point( 84, 455 ); + this.bLowerRank.Name = "bLowerRank"; + this.bLowerRank.Size = new System.Drawing.Size( 70, 23 ); + this.bLowerRank.TabIndex = 5; + this.bLowerRank.Text = "▼ Lower"; + this.bLowerRank.UseVisualStyleBackColor = true; + this.bLowerRank.Click += new System.EventHandler( this.bLowerRank_Click ); + // + // bRaiseRank + // + this.bRaiseRank.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bRaiseRank.Location = new System.Drawing.Point( 8, 455 ); + this.bRaiseRank.Name = "bRaiseRank"; + this.bRaiseRank.Size = new System.Drawing.Size( 70, 23 ); + this.bRaiseRank.TabIndex = 4; + this.bRaiseRank.Text = "▲ Raise"; + this.bRaiseRank.UseVisualStyleBackColor = true; + this.bRaiseRank.Click += new System.EventHandler( this.bRaiseRank_Click ); + // + // gRankOptions + // + this.gRankOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gRankOptions.Controls.Add( this.lFillLimitUnits ); + this.gRankOptions.Controls.Add( this.nFillLimit ); + this.gRankOptions.Controls.Add( this.lFillLimit ); + this.gRankOptions.Controls.Add( this.nCopyPasteSlots ); + this.gRankOptions.Controls.Add( this.lCopyPasteSlots ); + this.gRankOptions.Controls.Add( this.xAllowSecurityCircumvention ); + this.gRankOptions.Controls.Add( this.lAntiGrief1 ); + this.gRankOptions.Controls.Add( this.lAntiGrief3 ); + this.gRankOptions.Controls.Add( this.nAntiGriefSeconds ); + this.gRankOptions.Controls.Add( this.bColorRank ); + this.gRankOptions.Controls.Add( this.xDrawLimit ); + this.gRankOptions.Controls.Add( this.lDrawLimitUnits ); + this.gRankOptions.Controls.Add( this.lKickIdleUnits ); + this.gRankOptions.Controls.Add( this.nDrawLimit ); + this.gRankOptions.Controls.Add( this.nKickIdle ); + this.gRankOptions.Controls.Add( this.xAntiGrief ); + this.gRankOptions.Controls.Add( this.lAntiGrief2 ); + this.gRankOptions.Controls.Add( this.xKickIdle ); + this.gRankOptions.Controls.Add( this.nAntiGriefBlocks ); + this.gRankOptions.Controls.Add( this.xReserveSlot ); + this.gRankOptions.Controls.Add( this.tPrefix ); + this.gRankOptions.Controls.Add( this.lPrefix ); + this.gRankOptions.Controls.Add( this.lRankColor ); + this.gRankOptions.Controls.Add( this.tRankName ); + this.gRankOptions.Controls.Add( this.lRankName ); + this.gRankOptions.Location = new System.Drawing.Point( 160, 13 ); + this.gRankOptions.Name = "gRankOptions"; + this.gRankOptions.Size = new System.Drawing.Size( 307, 273 ); + this.gRankOptions.TabIndex = 6; + this.gRankOptions.TabStop = false; + this.gRankOptions.Text = "Rank Options"; + // + // lFillLimitUnits + // + this.lFillLimitUnits.AutoSize = true; + this.lFillLimitUnits.Location = new System.Drawing.Point( 239, 245 ); + this.lFillLimitUnits.Name = "lFillLimitUnits"; + this.lFillLimitUnits.Size = new System.Drawing.Size( 42, 15 ); + this.lFillLimitUnits.TabIndex = 24; + this.lFillLimitUnits.Text = "blocks"; + // + // nFillLimit + // + this.nFillLimit.Location = new System.Drawing.Point( 174, 243 ); + this.nFillLimit.Maximum = new decimal( new int[] { + 2048, + 0, + 0, + 0} ); + this.nFillLimit.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nFillLimit.Name = "nFillLimit"; + this.nFillLimit.Size = new System.Drawing.Size( 59, 21 ); + this.nFillLimit.TabIndex = 23; + this.nFillLimit.Value = new decimal( new int[] { + 16, + 0, + 0, + 0} ); + this.nFillLimit.ValueChanged += new System.EventHandler( this.nFillLimit_ValueChanged ); + // + // lFillLimit + // + this.lFillLimit.AutoSize = true; + this.lFillLimit.Location = new System.Drawing.Point( 85, 245 ); + this.lFillLimit.Name = "lFillLimit"; + this.lFillLimit.Size = new System.Drawing.Size( 83, 15 ); + this.lFillLimit.TabIndex = 22; + this.lFillLimit.Text = "Flood-fill limit:"; + // + // nCopyPasteSlots + // + this.nCopyPasteSlots.Location = new System.Drawing.Point( 174, 216 ); + this.nCopyPasteSlots.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nCopyPasteSlots.Name = "nCopyPasteSlots"; + this.nCopyPasteSlots.Size = new System.Drawing.Size( 59, 21 ); + this.nCopyPasteSlots.TabIndex = 21; + this.nCopyPasteSlots.ValueChanged += new System.EventHandler( this.nCopyPasteSlots_ValueChanged ); + // + // lCopyPasteSlots + // + this.lCopyPasteSlots.AutoSize = true; + this.lCopyPasteSlots.Location = new System.Drawing.Point( 50, 218 ); + this.lCopyPasteSlots.Name = "lCopyPasteSlots"; + this.lCopyPasteSlots.Size = new System.Drawing.Size( 118, 15 ); + this.lCopyPasteSlots.TabIndex = 20; + this.lCopyPasteSlots.Text = "Copy/paste slot limit:"; + // + // xAllowSecurityCircumvention + // + this.xAllowSecurityCircumvention.AutoSize = true; + this.xAllowSecurityCircumvention.Location = new System.Drawing.Point( 12, 165 ); + this.xAllowSecurityCircumvention.Name = "xAllowSecurityCircumvention"; + this.xAllowSecurityCircumvention.Size = new System.Drawing.Size( 271, 19 ); + this.xAllowSecurityCircumvention.TabIndex = 16; + this.xAllowSecurityCircumvention.Text = "Allow removing own access/build restrictions."; + this.xAllowSecurityCircumvention.UseVisualStyleBackColor = true; + this.xAllowSecurityCircumvention.CheckedChanged += new System.EventHandler( this.xAllowSecurityCircumvention_CheckedChanged ); + // + // lAntiGrief1 + // + this.lAntiGrief1.AutoSize = true; + this.lAntiGrief1.Location = new System.Drawing.Point( 50, 135 ); + this.lAntiGrief1.Name = "lAntiGrief1"; + this.lAntiGrief1.Size = new System.Drawing.Size( 47, 15 ); + this.lAntiGrief1.TabIndex = 11; + this.lAntiGrief1.Text = "Kick on"; + // + // lAntiGrief3 + // + this.lAntiGrief3.AutoSize = true; + this.lAntiGrief3.Location = new System.Drawing.Point( 275, 135 ); + this.lAntiGrief3.Name = "lAntiGrief3"; + this.lAntiGrief3.Size = new System.Drawing.Size( 26, 15 ); + this.lAntiGrief3.TabIndex = 15; + this.lAntiGrief3.Text = "sec"; + // + // nAntiGriefSeconds + // + this.nAntiGriefSeconds.Location = new System.Drawing.Point( 229, 133 ); + this.nAntiGriefSeconds.Name = "nAntiGriefSeconds"; + this.nAntiGriefSeconds.Size = new System.Drawing.Size( 40, 21 ); + this.nAntiGriefSeconds.TabIndex = 14; + this.nAntiGriefSeconds.ValueChanged += new System.EventHandler( this.nAntiGriefSeconds_ValueChanged ); + // + // bColorRank + // + this.bColorRank.BackColor = System.Drawing.Color.White; + this.bColorRank.Location = new System.Drawing.Point( 201, 47 ); + this.bColorRank.Name = "bColorRank"; + this.bColorRank.Size = new System.Drawing.Size( 100, 24 ); + this.bColorRank.TabIndex = 6; + this.bColorRank.UseVisualStyleBackColor = false; + this.bColorRank.Click += new System.EventHandler( this.bColorRank_Click ); + // + // xDrawLimit + // + this.xDrawLimit.AutoSize = true; + this.xDrawLimit.Location = new System.Drawing.Point( 12, 190 ); + this.xDrawLimit.Name = "xDrawLimit"; + this.xDrawLimit.Size = new System.Drawing.Size( 81, 19 ); + this.xDrawLimit.TabIndex = 17; + this.xDrawLimit.Text = "Draw limit"; + this.xDrawLimit.UseVisualStyleBackColor = true; + this.xDrawLimit.CheckedChanged += new System.EventHandler( this.xDrawLimit_CheckedChanged ); + // + // lDrawLimitUnits + // + this.lDrawLimitUnits.AutoSize = true; + this.lDrawLimitUnits.Location = new System.Drawing.Point( 172, 191 ); + this.lDrawLimitUnits.Name = "lDrawLimitUnits"; + this.lDrawLimitUnits.Size = new System.Drawing.Size( 42, 15 ); + this.lDrawLimitUnits.TabIndex = 19; + this.lDrawLimitUnits.Text = "blocks"; + // + // lKickIdleUnits + // + this.lKickIdleUnits.AutoSize = true; + this.lKickIdleUnits.Location = new System.Drawing.Point( 181, 79 ); + this.lKickIdleUnits.Name = "lKickIdleUnits"; + this.lKickIdleUnits.Size = new System.Drawing.Size( 51, 15 ); + this.lKickIdleUnits.TabIndex = 9; + this.lKickIdleUnits.Text = "minutes"; + // + // nDrawLimit + // + this.nDrawLimit.Increment = new decimal( new int[] { + 32, + 0, + 0, + 0} ); + this.nDrawLimit.Location = new System.Drawing.Point( 99, 189 ); + this.nDrawLimit.Maximum = new decimal( new int[] { + 100000000, + 0, + 0, + 0} ); + this.nDrawLimit.Name = "nDrawLimit"; + this.nDrawLimit.Size = new System.Drawing.Size( 67, 21 ); + this.nDrawLimit.TabIndex = 18; + this.nDrawLimit.ValueChanged += new System.EventHandler( this.nDrawLimit_ValueChanged ); + // + // nKickIdle + // + this.nKickIdle.Location = new System.Drawing.Point( 116, 77 ); + this.nKickIdle.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nKickIdle.Name = "nKickIdle"; + this.nKickIdle.Size = new System.Drawing.Size( 59, 21 ); + this.nKickIdle.TabIndex = 8; + this.nKickIdle.ValueChanged += new System.EventHandler( this.nKickIdle_ValueChanged ); + // + // xAntiGrief + // + this.xAntiGrief.AutoSize = true; + this.xAntiGrief.Location = new System.Drawing.Point( 12, 108 ); + this.xAntiGrief.Name = "xAntiGrief"; + this.xAntiGrief.Size = new System.Drawing.Size( 213, 19 ); + this.xAntiGrief.TabIndex = 10; + this.xAntiGrief.Text = "Enable grief / autoclicker detection"; + this.xAntiGrief.UseVisualStyleBackColor = true; + this.xAntiGrief.CheckedChanged += new System.EventHandler( this.xAntiGrief_CheckedChanged ); + // + // lAntiGrief2 + // + this.lAntiGrief2.AutoSize = true; + this.lAntiGrief2.Location = new System.Drawing.Point( 168, 135 ); + this.lAntiGrief2.Name = "lAntiGrief2"; + this.lAntiGrief2.Size = new System.Drawing.Size( 55, 15 ); + this.lAntiGrief2.TabIndex = 13; + this.lAntiGrief2.Text = "blocks in"; + // + // xKickIdle + // + this.xKickIdle.AutoSize = true; + this.xKickIdle.Location = new System.Drawing.Point( 12, 78 ); + this.xKickIdle.Name = "xKickIdle"; + this.xKickIdle.Size = new System.Drawing.Size( 98, 19 ); + this.xKickIdle.TabIndex = 7; + this.xKickIdle.Text = "Kick if idle for"; + this.xKickIdle.UseVisualStyleBackColor = true; + this.xKickIdle.CheckedChanged += new System.EventHandler( this.xKickIdle_CheckedChanged ); + // + // nAntiGriefBlocks + // + this.nAntiGriefBlocks.Location = new System.Drawing.Point( 103, 133 ); + this.nAntiGriefBlocks.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nAntiGriefBlocks.Name = "nAntiGriefBlocks"; + this.nAntiGriefBlocks.Size = new System.Drawing.Size( 59, 21 ); + this.nAntiGriefBlocks.TabIndex = 12; + this.nAntiGriefBlocks.ValueChanged += new System.EventHandler( this.nAntiGriefBlocks_ValueChanged ); + // + // xReserveSlot + // + this.xReserveSlot.AutoSize = true; + this.xReserveSlot.Location = new System.Drawing.Point( 12, 51 ); + this.xReserveSlot.Name = "xReserveSlot"; + this.xReserveSlot.Size = new System.Drawing.Size( 129, 19 ); + this.xReserveSlot.TabIndex = 4; + this.xReserveSlot.Text = "Reserve player slot"; + this.xReserveSlot.UseVisualStyleBackColor = true; + this.xReserveSlot.CheckedChanged += new System.EventHandler( this.xReserveSlot_CheckedChanged ); + // + // tPrefix + // + this.tPrefix.Enabled = false; + this.tPrefix.Location = new System.Drawing.Point( 279, 20 ); + this.tPrefix.MaxLength = 1; + this.tPrefix.Name = "tPrefix"; + this.tPrefix.Size = new System.Drawing.Size( 22, 21 ); + this.tPrefix.TabIndex = 3; + this.tPrefix.Validating += new System.ComponentModel.CancelEventHandler( this.tPrefix_Validating ); + // + // lPrefix + // + this.lPrefix.AutoSize = true; + this.lPrefix.Enabled = false; + this.lPrefix.Location = new System.Drawing.Point( 235, 23 ); + this.lPrefix.Name = "lPrefix"; + this.lPrefix.Size = new System.Drawing.Size( 38, 15 ); + this.lPrefix.TabIndex = 2; + this.lPrefix.Text = "Prefix"; + // + // lRankColor + // + this.lRankColor.AutoSize = true; + this.lRankColor.Location = new System.Drawing.Point( 159, 52 ); + this.lRankColor.Name = "lRankColor"; + this.lRankColor.Size = new System.Drawing.Size( 36, 15 ); + this.lRankColor.TabIndex = 5; + this.lRankColor.Text = "Color"; + // + // tRankName + // + this.tRankName.Location = new System.Drawing.Point( 62, 20 ); + this.tRankName.MaxLength = 16; + this.tRankName.Name = "tRankName"; + this.tRankName.Size = new System.Drawing.Size( 143, 21 ); + this.tRankName.TabIndex = 1; + this.tRankName.Validating += new System.ComponentModel.CancelEventHandler( this.tRankName_Validating ); + // + // lRankName + // + this.lRankName.AutoSize = true; + this.lRankName.Location = new System.Drawing.Point( 15, 23 ); + this.lRankName.Name = "lRankName"; + this.lRankName.Size = new System.Drawing.Size( 41, 15 ); + this.lRankName.TabIndex = 0; + this.lRankName.Text = "Name"; + // + // bDeleteRank + // + this.bDeleteRank.Location = new System.Drawing.Point( 84, 28 ); + this.bDeleteRank.Name = "bDeleteRank"; + this.bDeleteRank.Size = new System.Drawing.Size( 70, 23 ); + this.bDeleteRank.TabIndex = 3; + this.bDeleteRank.Text = "Delete"; + this.bDeleteRank.UseVisualStyleBackColor = true; + this.bDeleteRank.Click += new System.EventHandler( this.bDeleteRank_Click ); + // + // vPermissions + // + this.vPermissions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.vPermissions.CheckBoxes = true; + this.vPermissions.Columns.AddRange( new System.Windows.Forms.ColumnHeader[] { + this.chPermissions} ); + this.vPermissions.GridLines = true; + this.vPermissions.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.vPermissions.Location = new System.Drawing.Point( 473, 28 ); + this.vPermissions.MultiSelect = false; + this.vPermissions.Name = "vPermissions"; + this.vPermissions.ShowGroups = false; + this.vPermissions.ShowItemToolTips = true; + this.vPermissions.Size = new System.Drawing.Size( 171, 450 ); + this.vPermissions.TabIndex = 9; + this.vPermissions.UseCompatibleStateImageBehavior = false; + this.vPermissions.View = System.Windows.Forms.View.Details; + this.vPermissions.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler( this.vPermissions_ItemChecked ); + // + // chPermissions + // + this.chPermissions.Width = 150; + // + // bAddRank + // + this.bAddRank.Location = new System.Drawing.Point( 8, 28 ); + this.bAddRank.Name = "bAddRank"; + this.bAddRank.Size = new System.Drawing.Size( 70, 23 ); + this.bAddRank.TabIndex = 2; + this.bAddRank.Text = "Add Rank"; + this.bAddRank.UseVisualStyleBackColor = true; + this.bAddRank.Click += new System.EventHandler( this.bAddRank_Click ); + // + // lPermissions + // + this.lPermissions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.lPermissions.AutoSize = true; + this.lPermissions.Location = new System.Drawing.Point( 473, 10 ); + this.lPermissions.Name = "lPermissions"; + this.lPermissions.Size = new System.Drawing.Size( 107, 15 ); + this.lPermissions.TabIndex = 8; + this.lPermissions.Text = "Rank Permissions"; + // + // vRanks + // + this.vRanks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.vRanks.Font = new System.Drawing.Font( "Lucida Console", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.vRanks.FormattingEnabled = true; + this.vRanks.IntegralHeight = false; + this.vRanks.ItemHeight = 15; + this.vRanks.Location = new System.Drawing.Point( 8, 57 ); + this.vRanks.Name = "vRanks"; + this.vRanks.Size = new System.Drawing.Size( 146, 392 ); + this.vRanks.TabIndex = 1; + this.vRanks.SelectedIndexChanged += new System.EventHandler( this.vRanks_SelectedIndexChanged ); + // + // tabSecurity + // + this.tabSecurity.Controls.Add( this.gBlockDB ); + this.tabSecurity.Controls.Add( this.gSecurityMisc ); + this.tabSecurity.Controls.Add( this.gSpamChat ); + this.tabSecurity.Controls.Add( this.gVerify ); + this.tabSecurity.Location = new System.Drawing.Point( 4, 24 ); + this.tabSecurity.Name = "tabSecurity"; + this.tabSecurity.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabSecurity.Size = new System.Drawing.Size( 652, 481 ); + this.tabSecurity.TabIndex = 7; + this.tabSecurity.Text = "Security"; + this.tabSecurity.UseVisualStyleBackColor = true; + // + // gBlockDB + // + this.gBlockDB.Controls.Add( this.cBlockDBAutoEnableRank ); + this.gBlockDB.Controls.Add( this.xBlockDBAutoEnable ); + this.gBlockDB.Controls.Add( this.xBlockDBEnabled ); + this.gBlockDB.Location = new System.Drawing.Point( 8, 100 ); + this.gBlockDB.Name = "gBlockDB"; + this.gBlockDB.Size = new System.Drawing.Size( 636, 88 ); + this.gBlockDB.TabIndex = 1; + this.gBlockDB.TabStop = false; + this.gBlockDB.Text = "BlockDB"; + // + // cBlockDBAutoEnableRank + // + this.cBlockDBAutoEnableRank.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cBlockDBAutoEnableRank.FormattingEnabled = true; + this.cBlockDBAutoEnableRank.Location = new System.Drawing.Point( 442, 53 ); + this.cBlockDBAutoEnableRank.Name = "cBlockDBAutoEnableRank"; + this.cBlockDBAutoEnableRank.Size = new System.Drawing.Size( 121, 23 ); + this.cBlockDBAutoEnableRank.TabIndex = 2; + this.cBlockDBAutoEnableRank.TabStop = false; + this.cBlockDBAutoEnableRank.SelectedIndexChanged += new System.EventHandler( this.cBlockDBAutoEnableRank_SelectedIndexChanged ); + // + // xBlockDBAutoEnable + // + this.xBlockDBAutoEnable.AutoSize = true; + this.xBlockDBAutoEnable.Enabled = false; + this.xBlockDBAutoEnable.Location = new System.Drawing.Point( 76, 55 ); + this.xBlockDBAutoEnable.Name = "xBlockDBAutoEnable"; + this.xBlockDBAutoEnable.Size = new System.Drawing.Size( 360, 19 ); + this.xBlockDBAutoEnable.TabIndex = 1; + this.xBlockDBAutoEnable.TabStop = false; + this.xBlockDBAutoEnable.Text = "Automatically enable BlockDB on worlds that can be edited by"; + this.xBlockDBAutoEnable.UseVisualStyleBackColor = true; + this.xBlockDBAutoEnable.CheckedChanged += new System.EventHandler( this.xBlockDBAutoEnable_CheckedChanged ); + // + // xBlockDBEnabled + // + this.xBlockDBEnabled.AutoSize = true; + this.xBlockDBEnabled.Location = new System.Drawing.Point( 42, 30 ); + this.xBlockDBEnabled.Name = "xBlockDBEnabled"; + this.xBlockDBEnabled.Size = new System.Drawing.Size( 249, 19 ); + this.xBlockDBEnabled.TabIndex = 0; + this.xBlockDBEnabled.Text = "Enable BlockDB (per-block edit tracking)."; + this.xBlockDBEnabled.UseVisualStyleBackColor = true; + this.xBlockDBEnabled.CheckedChanged += new System.EventHandler( this.xBlockDBEnabled_CheckedChanged ); + // + // gSecurityMisc + // + this.gSecurityMisc.Controls.Add( this.xAnnounceRankChangeReasons ); + this.gSecurityMisc.Controls.Add( this.xRequireKickReason ); + this.gSecurityMisc.Controls.Add( this.xPaidPlayersOnly ); + this.gSecurityMisc.Controls.Add( this.lPatrolledRankAndBelow ); + this.gSecurityMisc.Controls.Add( this.cPatrolledRank ); + this.gSecurityMisc.Controls.Add( this.lPatrolledRank ); + this.gSecurityMisc.Controls.Add( this.xAnnounceRankChanges ); + this.gSecurityMisc.Controls.Add( this.xAnnounceKickAndBanReasons ); + this.gSecurityMisc.Controls.Add( this.xRequireRankChangeReason ); + this.gSecurityMisc.Controls.Add( this.xRequireBanReason ); + this.gSecurityMisc.Location = new System.Drawing.Point( 8, 294 ); + this.gSecurityMisc.Name = "gSecurityMisc"; + this.gSecurityMisc.Size = new System.Drawing.Size( 636, 178 ); + this.gSecurityMisc.TabIndex = 3; + this.gSecurityMisc.TabStop = false; + this.gSecurityMisc.Text = "Misc"; + // + // xAnnounceRankChangeReasons + // + this.xAnnounceRankChangeReasons.AutoSize = true; + this.xAnnounceRankChangeReasons.Location = new System.Drawing.Point( 336, 109 ); + this.xAnnounceRankChangeReasons.Name = "xAnnounceRankChangeReasons"; + this.xAnnounceRankChangeReasons.Size = new System.Drawing.Size( 253, 19 ); + this.xAnnounceRankChangeReasons.TabIndex = 6; + this.xAnnounceRankChangeReasons.Text = "Announce promotion && demotion reasons"; + this.xAnnounceRankChangeReasons.UseVisualStyleBackColor = true; + // + // xRequireKickReason + // + this.xRequireKickReason.AutoSize = true; + this.xRequireKickReason.Location = new System.Drawing.Point( 42, 59 ); + this.xRequireKickReason.Name = "xRequireKickReason"; + this.xRequireKickReason.Size = new System.Drawing.Size( 135, 19 ); + this.xRequireKickReason.TabIndex = 1; + this.xRequireKickReason.Text = "Require kick reason"; + this.xRequireKickReason.UseVisualStyleBackColor = true; + // + // xPaidPlayersOnly + // + this.xPaidPlayersOnly.AutoSize = true; + this.xPaidPlayersOnly.Location = new System.Drawing.Point( 42, 20 ); + this.xPaidPlayersOnly.Name = "xPaidPlayersOnly"; + this.xPaidPlayersOnly.Size = new System.Drawing.Size( 489, 19 ); + this.xPaidPlayersOnly.TabIndex = 0; + this.xPaidPlayersOnly.Text = "Only allow players with paid Minecraft accounts to join the server (not recommend" + + "ed)."; + this.xPaidPlayersOnly.UseVisualStyleBackColor = true; + // + // lPatrolledRankAndBelow + // + this.lPatrolledRankAndBelow.AutoSize = true; + this.lPatrolledRankAndBelow.Location = new System.Drawing.Point( 282, 145 ); + this.lPatrolledRankAndBelow.Name = "lPatrolledRankAndBelow"; + this.lPatrolledRankAndBelow.Size = new System.Drawing.Size( 72, 15 ); + this.lPatrolledRankAndBelow.TabIndex = 9; + this.lPatrolledRankAndBelow.Text = "(and below)"; + // + // cPatrolledRank + // + this.cPatrolledRank.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cPatrolledRank.FormattingEnabled = true; + this.cPatrolledRank.Location = new System.Drawing.Point( 153, 142 ); + this.cPatrolledRank.Name = "cPatrolledRank"; + this.cPatrolledRank.Size = new System.Drawing.Size( 123, 23 ); + this.cPatrolledRank.TabIndex = 8; + this.cPatrolledRank.SelectedIndexChanged += new System.EventHandler( this.cPatrolledRank_SelectedIndexChanged ); + // + // lPatrolledRank + // + this.lPatrolledRank.AutoSize = true; + this.lPatrolledRank.Location = new System.Drawing.Point( 64, 145 ); + this.lPatrolledRank.Name = "lPatrolledRank"; + this.lPatrolledRank.Size = new System.Drawing.Size( 83, 15 ); + this.lPatrolledRank.TabIndex = 7; + this.lPatrolledRank.Text = "Patrolled rank"; + // + // xAnnounceRankChanges + // + this.xAnnounceRankChanges.AutoSize = true; + this.xAnnounceRankChanges.Location = new System.Drawing.Point( 304, 84 ); + this.xAnnounceRankChanges.Name = "xAnnounceRankChanges"; + this.xAnnounceRankChanges.Size = new System.Drawing.Size( 231, 19 ); + this.xAnnounceRankChanges.TabIndex = 5; + this.xAnnounceRankChanges.Text = "Announce promotions and demotions"; + this.xAnnounceRankChanges.UseVisualStyleBackColor = true; + this.xAnnounceRankChanges.CheckedChanged += new System.EventHandler( this.xAnnounceRankChanges_CheckedChanged ); + // + // xAnnounceKickAndBanReasons + // + this.xAnnounceKickAndBanReasons.AutoSize = true; + this.xAnnounceKickAndBanReasons.Location = new System.Drawing.Point( 304, 59 ); + this.xAnnounceKickAndBanReasons.Name = "xAnnounceKickAndBanReasons"; + this.xAnnounceKickAndBanReasons.Size = new System.Drawing.Size( 244, 19 ); + this.xAnnounceKickAndBanReasons.TabIndex = 4; + this.xAnnounceKickAndBanReasons.Text = "Announce kick, ban, and unban reasons"; + this.xAnnounceKickAndBanReasons.UseVisualStyleBackColor = true; + // + // xRequireRankChangeReason + // + this.xRequireRankChangeReason.AutoSize = true; + this.xRequireRankChangeReason.Location = new System.Drawing.Point( 42, 109 ); + this.xRequireRankChangeReason.Name = "xRequireRankChangeReason"; + this.xRequireRankChangeReason.Size = new System.Drawing.Size( 236, 19 ); + this.xRequireRankChangeReason.TabIndex = 3; + this.xRequireRankChangeReason.Text = "Require promotion && demotion reason"; + this.xRequireRankChangeReason.UseVisualStyleBackColor = true; + // + // xRequireBanReason + // + this.xRequireBanReason.AutoSize = true; + this.xRequireBanReason.Location = new System.Drawing.Point( 42, 84 ); + this.xRequireBanReason.Name = "xRequireBanReason"; + this.xRequireBanReason.Size = new System.Drawing.Size( 184, 19 ); + this.xRequireBanReason.TabIndex = 2; + this.xRequireBanReason.Text = "Require ban && unban reason"; + this.xRequireBanReason.UseVisualStyleBackColor = true; + // + // gSpamChat + // + this.gSpamChat.Controls.Add( this.lAntispamMaxWarnings ); + this.gSpamChat.Controls.Add( this.nAntispamMaxWarnings ); + this.gSpamChat.Controls.Add( this.xAntispamKicks ); + this.gSpamChat.Controls.Add( this.lSpamMuteSeconds ); + this.gSpamChat.Controls.Add( this.lAntispamInterval ); + this.gSpamChat.Controls.Add( this.nSpamMute ); + this.gSpamChat.Controls.Add( this.lSpamMute ); + this.gSpamChat.Controls.Add( this.nAntispamInterval ); + this.gSpamChat.Controls.Add( this.lAntispamMessageCount ); + this.gSpamChat.Controls.Add( this.nAntispamMessageCount ); + this.gSpamChat.Controls.Add( this.lSpamChat ); + this.gSpamChat.Location = new System.Drawing.Point( 8, 194 ); + this.gSpamChat.Name = "gSpamChat"; + this.gSpamChat.Size = new System.Drawing.Size( 636, 94 ); + this.gSpamChat.TabIndex = 2; + this.gSpamChat.TabStop = false; + this.gSpamChat.Text = "Chat Spam Prevention"; + // + // lAntispamMaxWarnings + // + this.lAntispamMaxWarnings.AutoSize = true; + this.lAntispamMaxWarnings.Location = new System.Drawing.Point( 454, 62 ); + this.lAntispamMaxWarnings.Name = "lAntispamMaxWarnings"; + this.lAntispamMaxWarnings.Size = new System.Drawing.Size( 57, 15 ); + this.lAntispamMaxWarnings.TabIndex = 10; + this.lAntispamMaxWarnings.Text = "warnings"; + // + // nAntispamMaxWarnings + // + this.nAntispamMaxWarnings.Location = new System.Drawing.Point( 386, 60 ); + this.nAntispamMaxWarnings.Name = "nAntispamMaxWarnings"; + this.nAntispamMaxWarnings.Size = new System.Drawing.Size( 62, 21 ); + this.nAntispamMaxWarnings.TabIndex = 9; + // + // xAntispamKicks + // + this.xAntispamKicks.AutoSize = true; + this.xAntispamKicks.Location = new System.Drawing.Point( 304, 61 ); + this.xAntispamKicks.Name = "xAntispamKicks"; + this.xAntispamKicks.Size = new System.Drawing.Size( 76, 19 ); + this.xAntispamKicks.TabIndex = 8; + this.xAntispamKicks.Text = "Kick after"; + this.xAntispamKicks.UseVisualStyleBackColor = true; + this.xAntispamKicks.CheckedChanged += new System.EventHandler( this.xSpamChatKick_CheckedChanged ); + // + // lSpamMuteSeconds + // + this.lSpamMuteSeconds.AutoSize = true; + this.lSpamMuteSeconds.Location = new System.Drawing.Point( 221, 62 ); + this.lSpamMuteSeconds.Name = "lSpamMuteSeconds"; + this.lSpamMuteSeconds.Size = new System.Drawing.Size( 53, 15 ); + this.lSpamMuteSeconds.TabIndex = 7; + this.lSpamMuteSeconds.Text = "seconds"; + // + // lAntispamInterval + // + this.lAntispamInterval.AutoSize = true; + this.lAntispamInterval.Location = new System.Drawing.Point( 372, 27 ); + this.lAntispamInterval.Name = "lAntispamInterval"; + this.lAntispamInterval.Size = new System.Drawing.Size( 53, 15 ); + this.lAntispamInterval.TabIndex = 4; + this.lAntispamInterval.Text = "seconds"; + // + // nSpamMute + // + this.nSpamMute.Location = new System.Drawing.Point( 153, 59 ); + this.nSpamMute.Name = "nSpamMute"; + this.nSpamMute.Size = new System.Drawing.Size( 62, 21 ); + this.nSpamMute.TabIndex = 6; + // + // lSpamMute + // + this.lSpamMute.AutoSize = true; + this.lSpamMute.Location = new System.Drawing.Point( 39, 62 ); + this.lSpamMute.Name = "lSpamMute"; + this.lSpamMute.Size = new System.Drawing.Size( 108, 15 ); + this.lSpamMute.TabIndex = 5; + this.lSpamMute.Text = "Mute spammer for"; + // + // nAntispamInterval + // + this.nAntispamInterval.Location = new System.Drawing.Point( 304, 25 ); + this.nAntispamInterval.Maximum = new decimal( new int[] { + 50, + 0, + 0, + 0} ); + this.nAntispamInterval.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nAntispamInterval.Name = "nAntispamInterval"; + this.nAntispamInterval.Size = new System.Drawing.Size( 62, 21 ); + this.nAntispamInterval.TabIndex = 3; + this.nAntispamInterval.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // lAntispamMessageCount + // + this.lAntispamMessageCount.AutoSize = true; + this.lAntispamMessageCount.Location = new System.Drawing.Point( 219, 27 ); + this.lAntispamMessageCount.Name = "lAntispamMessageCount"; + this.lAntispamMessageCount.Size = new System.Drawing.Size( 77, 15 ); + this.lAntispamMessageCount.TabIndex = 2; + this.lAntispamMessageCount.Text = "messages in"; + // + // nAntispamMessageCount + // + this.nAntispamMessageCount.Location = new System.Drawing.Point( 153, 25 ); + this.nAntispamMessageCount.Maximum = new decimal( new int[] { + 50, + 0, + 0, + 0} ); + this.nAntispamMessageCount.Minimum = new decimal( new int[] { + 2, + 0, + 0, + 0} ); + this.nAntispamMessageCount.Name = "nAntispamMessageCount"; + this.nAntispamMessageCount.Size = new System.Drawing.Size( 62, 21 ); + this.nAntispamMessageCount.TabIndex = 1; + this.nAntispamMessageCount.Value = new decimal( new int[] { + 2, + 0, + 0, + 0} ); + // + // lSpamChat + // + this.lSpamChat.AutoSize = true; + this.lSpamChat.Location = new System.Drawing.Point( 50, 27 ); + this.lSpamChat.Name = "lSpamChat"; + this.lSpamChat.Size = new System.Drawing.Size( 97, 15 ); + this.lSpamChat.TabIndex = 0; + this.lSpamChat.Text = "Limit chat rate to"; + // + // gVerify + // + this.gVerify.Controls.Add( this.nMaxConnectionsPerIP ); + this.gVerify.Controls.Add( this.xAllowUnverifiedLAN ); + this.gVerify.Controls.Add( this.xMaxConnectionsPerIP ); + this.gVerify.Controls.Add( this.lVerifyNames ); + this.gVerify.Controls.Add( this.cVerifyNames ); + this.gVerify.Location = new System.Drawing.Point( 8, 13 ); + this.gVerify.Name = "gVerify"; + this.gVerify.Size = new System.Drawing.Size( 636, 81 ); + this.gVerify.TabIndex = 0; + this.gVerify.TabStop = false; + this.gVerify.Text = "Connection"; + // + // nMaxConnectionsPerIP + // + this.nMaxConnectionsPerIP.Location = new System.Drawing.Point( 539, 21 ); + this.nMaxConnectionsPerIP.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nMaxConnectionsPerIP.Name = "nMaxConnectionsPerIP"; + this.nMaxConnectionsPerIP.Size = new System.Drawing.Size( 47, 21 ); + this.nMaxConnectionsPerIP.TabIndex = 4; + this.nMaxConnectionsPerIP.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // xAllowUnverifiedLAN + // + this.xAllowUnverifiedLAN.AutoSize = true; + this.xAllowUnverifiedLAN.Location = new System.Drawing.Point( 42, 49 ); + this.xAllowUnverifiedLAN.Name = "xAllowUnverifiedLAN"; + this.xAllowUnverifiedLAN.Size = new System.Drawing.Size( 490, 19 ); + this.xAllowUnverifiedLAN.TabIndex = 2; + this.xAllowUnverifiedLAN.Text = "Allow connections from LAN without name verification (192.168.0.0/16 and 10.0.0.0" + + "/8)"; + this.xAllowUnverifiedLAN.UseVisualStyleBackColor = true; + // + // xMaxConnectionsPerIP + // + this.xMaxConnectionsPerIP.AutoSize = true; + this.xMaxConnectionsPerIP.Location = new System.Drawing.Point( 304, 22 ); + this.xMaxConnectionsPerIP.Name = "xMaxConnectionsPerIP"; + this.xMaxConnectionsPerIP.Size = new System.Drawing.Size( 229, 19 ); + this.xMaxConnectionsPerIP.TabIndex = 3; + this.xMaxConnectionsPerIP.Text = "Limit number of connections per IP to"; + this.xMaxConnectionsPerIP.UseVisualStyleBackColor = true; + this.xMaxConnectionsPerIP.CheckedChanged += new System.EventHandler( this.xMaxConnectionsPerIP_CheckedChanged ); + // + // lVerifyNames + // + this.lVerifyNames.AutoSize = true; + this.lVerifyNames.Location = new System.Drawing.Point( 45, 23 ); + this.lVerifyNames.Name = "lVerifyNames"; + this.lVerifyNames.Size = new System.Drawing.Size( 102, 15 ); + this.lVerifyNames.TabIndex = 0; + this.lVerifyNames.Text = "Name verification"; + // + // cVerifyNames + // + this.cVerifyNames.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cVerifyNames.FormattingEnabled = true; + this.cVerifyNames.Items.AddRange( new object[] { + "None (Unsafe)", + "Normal", + "Strict"} ); + this.cVerifyNames.Location = new System.Drawing.Point( 153, 20 ); + this.cVerifyNames.Name = "cVerifyNames"; + this.cVerifyNames.Size = new System.Drawing.Size( 120, 23 ); + this.cVerifyNames.TabIndex = 1; + this.cVerifyNames.SelectedIndexChanged += new System.EventHandler( this.cVerifyNames_SelectedIndexChanged ); + // + // tabSavingAndBackup + // + this.tabSavingAndBackup.Controls.Add( this.gDataBackup ); + this.tabSavingAndBackup.Controls.Add( this.gSaving ); + this.tabSavingAndBackup.Controls.Add( this.gBackups ); + this.tabSavingAndBackup.Location = new System.Drawing.Point( 4, 24 ); + this.tabSavingAndBackup.Name = "tabSavingAndBackup"; + this.tabSavingAndBackup.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabSavingAndBackup.Size = new System.Drawing.Size( 652, 481 ); + this.tabSavingAndBackup.TabIndex = 4; + this.tabSavingAndBackup.Text = "Saving and Backup"; + this.tabSavingAndBackup.UseVisualStyleBackColor = true; + // + // gDataBackup + // + this.gDataBackup.Controls.Add( this.xBackupDataOnStartup ); + this.gDataBackup.Location = new System.Drawing.Point( 8, 235 ); + this.gDataBackup.Name = "gDataBackup"; + this.gDataBackup.Size = new System.Drawing.Size( 636, 52 ); + this.gDataBackup.TabIndex = 2; + this.gDataBackup.TabStop = false; + this.gDataBackup.Text = "Data Backup"; + // + // xBackupDataOnStartup + // + this.xBackupDataOnStartup.AutoSize = true; + this.xBackupDataOnStartup.Location = new System.Drawing.Point( 16, 20 ); + this.xBackupDataOnStartup.Name = "xBackupDataOnStartup"; + this.xBackupDataOnStartup.Size = new System.Drawing.Size( 261, 19 ); + this.xBackupDataOnStartup.TabIndex = 0; + this.xBackupDataOnStartup.Text = "Backup PlayerDB and IP ban list on startup."; + this.xBackupDataOnStartup.UseVisualStyleBackColor = true; + // + // gSaving + // + this.gSaving.Controls.Add( this.nSaveInterval ); + this.gSaving.Controls.Add( this.lSaveIntervalUnits ); + this.gSaving.Controls.Add( this.xSaveInterval ); + this.gSaving.Location = new System.Drawing.Point( 8, 13 ); + this.gSaving.Name = "gSaving"; + this.gSaving.Size = new System.Drawing.Size( 636, 52 ); + this.gSaving.TabIndex = 0; + this.gSaving.TabStop = false; + this.gSaving.Text = "Map Saving"; + // + // nSaveInterval + // + this.nSaveInterval.Location = new System.Drawing.Point( 136, 20 ); + this.nSaveInterval.Name = "nSaveInterval"; + this.nSaveInterval.Size = new System.Drawing.Size( 48, 21 ); + this.nSaveInterval.TabIndex = 1; + // + // lSaveIntervalUnits + // + this.lSaveIntervalUnits.AutoSize = true; + this.lSaveIntervalUnits.Location = new System.Drawing.Point( 190, 22 ); + this.lSaveIntervalUnits.Name = "lSaveIntervalUnits"; + this.lSaveIntervalUnits.Size = new System.Drawing.Size( 53, 15 ); + this.lSaveIntervalUnits.TabIndex = 2; + this.lSaveIntervalUnits.Text = "seconds"; + // + // xSaveInterval + // + this.xSaveInterval.AutoSize = true; + this.xSaveInterval.Location = new System.Drawing.Point( 12, 21 ); + this.xSaveInterval.Name = "xSaveInterval"; + this.xSaveInterval.Size = new System.Drawing.Size( 118, 19 ); + this.xSaveInterval.TabIndex = 0; + this.xSaveInterval.Text = "Save maps every"; + this.xSaveInterval.UseVisualStyleBackColor = true; + this.xSaveInterval.CheckedChanged += new System.EventHandler( this.xSaveAtInterval_CheckedChanged ); + // + // gBackups + // + this.gBackups.Controls.Add( this.xBackupOnlyWhenChanged ); + this.gBackups.Controls.Add( this.lMaxBackupSize ); + this.gBackups.Controls.Add( this.xMaxBackupSize ); + this.gBackups.Controls.Add( this.nMaxBackupSize ); + this.gBackups.Controls.Add( this.xMaxBackups ); + this.gBackups.Controls.Add( this.xBackupOnStartup ); + this.gBackups.Controls.Add( this.lMaxBackups ); + this.gBackups.Controls.Add( this.nMaxBackups ); + this.gBackups.Controls.Add( this.nBackupInterval ); + this.gBackups.Controls.Add( this.lBackupIntervalUnits ); + this.gBackups.Controls.Add( this.xBackupInterval ); + this.gBackups.Controls.Add( this.xBackupOnJoin ); + this.gBackups.Location = new System.Drawing.Point( 8, 71 ); + this.gBackups.Name = "gBackups"; + this.gBackups.Size = new System.Drawing.Size( 636, 158 ); + this.gBackups.TabIndex = 1; + this.gBackups.TabStop = false; + this.gBackups.Text = "Map Backups"; + // + // xBackupOnlyWhenChanged + // + this.xBackupOnlyWhenChanged.AutoSize = true; + this.xBackupOnlyWhenChanged.Location = new System.Drawing.Point( 369, 46 ); + this.xBackupOnlyWhenChanged.Name = "xBackupOnlyWhenChanged"; + this.xBackupOnlyWhenChanged.Size = new System.Drawing.Size( 260, 19 ); + this.xBackupOnlyWhenChanged.TabIndex = 4; + this.xBackupOnlyWhenChanged.Text = "Skip timed backups if map hasn\'t changed."; + this.xBackupOnlyWhenChanged.UseVisualStyleBackColor = true; + // + // lMaxBackupSize + // + this.lMaxBackupSize.AutoSize = true; + this.lMaxBackupSize.Location = new System.Drawing.Point( 418, 124 ); + this.lMaxBackupSize.Name = "lMaxBackupSize"; + this.lMaxBackupSize.Size = new System.Drawing.Size( 103, 15 ); + this.lMaxBackupSize.TabIndex = 11; + this.lMaxBackupSize.Text = "MB of disk space."; + // + // xMaxBackupSize + // + this.xMaxBackupSize.AutoSize = true; + this.xMaxBackupSize.Location = new System.Drawing.Point( 16, 123 ); + this.xMaxBackupSize.Name = "xMaxBackupSize"; + this.xMaxBackupSize.Size = new System.Drawing.Size( 317, 19 ); + this.xMaxBackupSize.TabIndex = 9; + this.xMaxBackupSize.Text = "Delete old backups if the directory takes up more than"; + this.xMaxBackupSize.UseVisualStyleBackColor = true; + this.xMaxBackupSize.CheckedChanged += new System.EventHandler( this.xMaxBackupSize_CheckedChanged ); + // + // nMaxBackupSize + // + this.nMaxBackupSize.Location = new System.Drawing.Point( 339, 122 ); + this.nMaxBackupSize.Maximum = new decimal( new int[] { + 1000000, + 0, + 0, + 0} ); + this.nMaxBackupSize.Name = "nMaxBackupSize"; + this.nMaxBackupSize.Size = new System.Drawing.Size( 73, 21 ); + this.nMaxBackupSize.TabIndex = 10; + // + // xMaxBackups + // + this.xMaxBackups.AutoSize = true; + this.xMaxBackups.Location = new System.Drawing.Point( 16, 98 ); + this.xMaxBackups.Name = "xMaxBackups"; + this.xMaxBackups.Size = new System.Drawing.Size( 251, 19 ); + this.xMaxBackups.TabIndex = 6; + this.xMaxBackups.Text = "Delete old backups if there are more than"; + this.xMaxBackups.UseVisualStyleBackColor = true; + this.xMaxBackups.CheckedChanged += new System.EventHandler( this.xMaxBackups_CheckedChanged ); + // + // xBackupOnStartup + // + this.xBackupOnStartup.AutoSize = true; + this.xBackupOnStartup.Enabled = false; + this.xBackupOnStartup.Location = new System.Drawing.Point( 16, 20 ); + this.xBackupOnStartup.Name = "xBackupOnStartup"; + this.xBackupOnStartup.Size = new System.Drawing.Size( 168, 19 ); + this.xBackupOnStartup.TabIndex = 0; + this.xBackupOnStartup.Text = "Create backups on startup"; + this.xBackupOnStartup.UseVisualStyleBackColor = true; + // + // lMaxBackups + // + this.lMaxBackups.AutoSize = true; + this.lMaxBackups.Location = new System.Drawing.Point( 336, 99 ); + this.lMaxBackups.Name = "lMaxBackups"; + this.lMaxBackups.Size = new System.Drawing.Size( 157, 15 ); + this.lMaxBackups.TabIndex = 8; + this.lMaxBackups.Text = "files in the backup directory."; + // + // nMaxBackups + // + this.nMaxBackups.Location = new System.Drawing.Point( 273, 97 ); + this.nMaxBackups.Maximum = new decimal( new int[] { + 100000, + 0, + 0, + 0} ); + this.nMaxBackups.Name = "nMaxBackups"; + this.nMaxBackups.Size = new System.Drawing.Size( 57, 21 ); + this.nMaxBackups.TabIndex = 7; + // + // nBackupInterval + // + this.nBackupInterval.Location = new System.Drawing.Point( 164, 45 ); + this.nBackupInterval.Name = "nBackupInterval"; + this.nBackupInterval.Size = new System.Drawing.Size( 48, 21 ); + this.nBackupInterval.TabIndex = 2; + // + // lBackupIntervalUnits + // + this.lBackupIntervalUnits.AutoSize = true; + this.lBackupIntervalUnits.Location = new System.Drawing.Point( 218, 47 ); + this.lBackupIntervalUnits.Name = "lBackupIntervalUnits"; + this.lBackupIntervalUnits.Size = new System.Drawing.Size( 51, 15 ); + this.lBackupIntervalUnits.TabIndex = 3; + this.lBackupIntervalUnits.Text = "minutes"; + // + // xBackupInterval + // + this.xBackupInterval.AutoSize = true; + this.xBackupInterval.Location = new System.Drawing.Point( 16, 46 ); + this.xBackupInterval.Name = "xBackupInterval"; + this.xBackupInterval.Size = new System.Drawing.Size( 142, 19 ); + this.xBackupInterval.TabIndex = 1; + this.xBackupInterval.Text = "Create backups every"; + this.xBackupInterval.UseVisualStyleBackColor = true; + this.xBackupInterval.CheckedChanged += new System.EventHandler( this.xBackupAtInterval_CheckedChanged ); + // + // xBackupOnJoin + // + this.xBackupOnJoin.AutoSize = true; + this.xBackupOnJoin.Location = new System.Drawing.Point( 16, 72 ); + this.xBackupOnJoin.Name = "xBackupOnJoin"; + this.xBackupOnJoin.Size = new System.Drawing.Size( 279, 19 ); + this.xBackupOnJoin.TabIndex = 5; + this.xBackupOnJoin.Text = "Create backup whenever a player joins a world"; + this.xBackupOnJoin.UseVisualStyleBackColor = true; + // + // tabLogging + // + this.tabLogging.Controls.Add( this.gLogFile ); + this.tabLogging.Controls.Add( this.gConsole ); + this.tabLogging.Location = new System.Drawing.Point( 4, 24 ); + this.tabLogging.Name = "tabLogging"; + this.tabLogging.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabLogging.Size = new System.Drawing.Size( 652, 481 ); + this.tabLogging.TabIndex = 5; + this.tabLogging.Text = "Logging"; + this.tabLogging.UseVisualStyleBackColor = true; + // + // gLogFile + // + this.gLogFile.Controls.Add( this.lLogFileOptionsDescription ); + this.gLogFile.Controls.Add( this.xLogLimit ); + this.gLogFile.Controls.Add( this.vLogFileOptions ); + this.gLogFile.Controls.Add( this.lLogLimitUnits ); + this.gLogFile.Controls.Add( this.nLogLimit ); + this.gLogFile.Controls.Add( this.cLogMode ); + this.gLogFile.Controls.Add( this.lLogMode ); + this.gLogFile.Location = new System.Drawing.Point( 329, 13 ); + this.gLogFile.Name = "gLogFile"; + this.gLogFile.Size = new System.Drawing.Size( 315, 423 ); + this.gLogFile.TabIndex = 1; + this.gLogFile.TabStop = false; + this.gLogFile.Text = "Log File"; + // + // lLogFileOptionsDescription + // + this.lLogFileOptionsDescription.AutoSize = true; + this.lLogFileOptionsDescription.Location = new System.Drawing.Point( 27, 22 ); + this.lLogFileOptionsDescription.Name = "lLogFileOptionsDescription"; + this.lLogFileOptionsDescription.Size = new System.Drawing.Size( 212, 30 ); + this.lLogFileOptionsDescription.TabIndex = 0; + this.lLogFileOptionsDescription.Text = "Types of messages that will be written\r\nto the log file on disk."; + // + // xLogLimit + // + this.xLogLimit.AutoSize = true; + this.xLogLimit.Enabled = false; + this.xLogLimit.Location = new System.Drawing.Point( 18, 390 ); + this.xLogLimit.Name = "xLogLimit"; + this.xLogLimit.Size = new System.Drawing.Size( 80, 19 ); + this.xLogLimit.TabIndex = 4; + this.xLogLimit.Text = "Only keep"; + this.xLogLimit.UseVisualStyleBackColor = true; + this.xLogLimit.CheckedChanged += new System.EventHandler( this.xLogLimit_CheckedChanged ); + // + // vLogFileOptions + // + this.vLogFileOptions.CheckBoxes = true; + this.vLogFileOptions.Columns.AddRange( new System.Windows.Forms.ColumnHeader[] { + this.columnHeader2} ); + this.vLogFileOptions.GridLines = true; + this.vLogFileOptions.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.vLogFileOptions.Location = new System.Drawing.Point( 78, 59 ); + this.vLogFileOptions.Name = "vLogFileOptions"; + this.vLogFileOptions.ShowItemToolTips = true; + this.vLogFileOptions.Size = new System.Drawing.Size( 161, 294 ); + this.vLogFileOptions.TabIndex = 1; + this.vLogFileOptions.UseCompatibleStateImageBehavior = false; + this.vLogFileOptions.View = System.Windows.Forms.View.Details; + this.vLogFileOptions.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler( this.vLogFileOptions_ItemChecked ); + // + // columnHeader2 + // + this.columnHeader2.Width = 157; + // + // lLogLimitUnits + // + this.lLogLimitUnits.AutoSize = true; + this.lLogLimitUnits.Location = new System.Drawing.Point( 166, 391 ); + this.lLogLimitUnits.Name = "lLogLimitUnits"; + this.lLogLimitUnits.Size = new System.Drawing.Size( 129, 15 ); + this.lLogLimitUnits.TabIndex = 6; + this.lLogLimitUnits.Text = "of most recent log files"; + // + // nLogLimit + // + this.nLogLimit.Enabled = false; + this.nLogLimit.Location = new System.Drawing.Point( 104, 389 ); + this.nLogLimit.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nLogLimit.Name = "nLogLimit"; + this.nLogLimit.Size = new System.Drawing.Size( 56, 21 ); + this.nLogLimit.TabIndex = 5; + // + // cLogMode + // + this.cLogMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cLogMode.FormattingEnabled = true; + this.cLogMode.Items.AddRange( new object[] { + "One long file", + "Multiple files, split by session", + "Multiple files, split by day"} ); + this.cLogMode.Location = new System.Drawing.Point( 104, 360 ); + this.cLogMode.Name = "cLogMode"; + this.cLogMode.Size = new System.Drawing.Size( 185, 23 ); + this.cLogMode.TabIndex = 3; + // + // lLogMode + // + this.lLogMode.AutoSize = true; + this.lLogMode.Location = new System.Drawing.Point( 35, 363 ); + this.lLogMode.Name = "lLogMode"; + this.lLogMode.Size = new System.Drawing.Size( 63, 15 ); + this.lLogMode.TabIndex = 2; + this.lLogMode.Text = "Log mode"; + // + // gConsole + // + this.gConsole.Controls.Add( this.lLogConsoleOptionsDescription ); + this.gConsole.Controls.Add( this.vConsoleOptions ); + this.gConsole.Location = new System.Drawing.Point( 8, 13 ); + this.gConsole.Name = "gConsole"; + this.gConsole.Size = new System.Drawing.Size( 315, 423 ); + this.gConsole.TabIndex = 0; + this.gConsole.TabStop = false; + this.gConsole.Text = "Console"; + // + // lLogConsoleOptionsDescription + // + this.lLogConsoleOptionsDescription.AutoSize = true; + this.lLogConsoleOptionsDescription.Location = new System.Drawing.Point( 9, 21 ); + this.lLogConsoleOptionsDescription.Name = "lLogConsoleOptionsDescription"; + this.lLogConsoleOptionsDescription.Size = new System.Drawing.Size( 212, 30 ); + this.lLogConsoleOptionsDescription.TabIndex = 0; + this.lLogConsoleOptionsDescription.Text = "Types of messages that will be written\r\ndirectly to console."; + // + // vConsoleOptions + // + this.vConsoleOptions.CheckBoxes = true; + this.vConsoleOptions.Columns.AddRange( new System.Windows.Forms.ColumnHeader[] { + this.columnHeader3} ); + this.vConsoleOptions.GridLines = true; + this.vConsoleOptions.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.vConsoleOptions.Location = new System.Drawing.Point( 76, 59 ); + this.vConsoleOptions.Name = "vConsoleOptions"; + this.vConsoleOptions.ShowItemToolTips = true; + this.vConsoleOptions.Size = new System.Drawing.Size( 161, 294 ); + this.vConsoleOptions.TabIndex = 1; + this.vConsoleOptions.UseCompatibleStateImageBehavior = false; + this.vConsoleOptions.View = System.Windows.Forms.View.Details; + this.vConsoleOptions.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler( this.vConsoleOptions_ItemChecked ); + // + // columnHeader3 + // + this.columnHeader3.Width = 157; + // + // tabIRC + // + this.tabIRC.Controls.Add( this.xIRCListShowNonEnglish ); + this.tabIRC.Controls.Add( this.gIRCOptions ); + this.tabIRC.Controls.Add( this.gIRCNetwork ); + this.tabIRC.Controls.Add( this.lIRCList ); + this.tabIRC.Controls.Add( this.xIRCBotEnabled ); + this.tabIRC.Controls.Add( this.cIRCList ); + this.tabIRC.Location = new System.Drawing.Point( 4, 24 ); + this.tabIRC.Name = "tabIRC"; + this.tabIRC.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabIRC.Size = new System.Drawing.Size( 652, 481 ); + this.tabIRC.TabIndex = 8; + this.tabIRC.Text = "IRC"; + this.tabIRC.UseVisualStyleBackColor = true; + // + // xIRCListShowNonEnglish + // + this.xIRCListShowNonEnglish.AutoSize = true; + this.xIRCListShowNonEnglish.Enabled = false; + this.xIRCListShowNonEnglish.Location = new System.Drawing.Point( 465, 13 ); + this.xIRCListShowNonEnglish.Name = "xIRCListShowNonEnglish"; + this.xIRCListShowNonEnglish.Size = new System.Drawing.Size( 178, 19 ); + this.xIRCListShowNonEnglish.TabIndex = 3; + this.xIRCListShowNonEnglish.Text = "Show non-English networks"; + this.xIRCListShowNonEnglish.UseVisualStyleBackColor = true; + this.xIRCListShowNonEnglish.CheckedChanged += new System.EventHandler( this.xIRCListShowNonEnglish_CheckedChanged ); + // + // gIRCOptions + // + this.gIRCOptions.Controls.Add( this.xIRCBotAnnounceServerEvents ); + this.gIRCOptions.Controls.Add( this.xIRCUseColor ); + this.gIRCOptions.Controls.Add( this.lIRCNoForwardingMessage ); + this.gIRCOptions.Controls.Add( this.xIRCBotAnnounceIRCJoins ); + this.gIRCOptions.Controls.Add( this.bColorIRC ); + this.gIRCOptions.Controls.Add( this.lColorIRC ); + this.gIRCOptions.Controls.Add( this.xIRCBotForwardFromIRC ); + this.gIRCOptions.Controls.Add( this.xIRCBotAnnounceServerJoins ); + this.gIRCOptions.Controls.Add( this.xIRCBotForwardFromServer ); + this.gIRCOptions.Location = new System.Drawing.Point( 8, 206 ); + this.gIRCOptions.Name = "gIRCOptions"; + this.gIRCOptions.Size = new System.Drawing.Size( 636, 162 ); + this.gIRCOptions.TabIndex = 5; + this.gIRCOptions.TabStop = false; + this.gIRCOptions.Text = "Options"; + // + // xIRCBotAnnounceServerEvents + // + this.xIRCBotAnnounceServerEvents.AutoSize = true; + this.xIRCBotAnnounceServerEvents.Location = new System.Drawing.Point( 38, 109 ); + this.xIRCBotAnnounceServerEvents.Name = "xIRCBotAnnounceServerEvents"; + this.xIRCBotAnnounceServerEvents.Size = new System.Drawing.Size( 417, 19 ); + this.xIRCBotAnnounceServerEvents.TabIndex = 7; + this.xIRCBotAnnounceServerEvents.Text = "Announce SERVER events (kicks, bans, promotions, demotions) on IRC."; + this.xIRCBotAnnounceServerEvents.UseVisualStyleBackColor = true; + // + // xIRCUseColor + // + this.xIRCUseColor.AutoSize = true; + this.xIRCUseColor.Location = new System.Drawing.Point( 325, 23 ); + this.xIRCUseColor.Name = "xIRCUseColor"; + this.xIRCUseColor.Size = new System.Drawing.Size( 149, 19 ); + this.xIRCUseColor.TabIndex = 2; + this.xIRCUseColor.Text = "Use text colors on IRC."; + this.xIRCUseColor.UseVisualStyleBackColor = true; + // + // lIRCNoForwardingMessage + // + this.lIRCNoForwardingMessage.AutoSize = true; + this.lIRCNoForwardingMessage.Location = new System.Drawing.Point( 35, 137 ); + this.lIRCNoForwardingMessage.Name = "lIRCNoForwardingMessage"; + this.lIRCNoForwardingMessage.Size = new System.Drawing.Size( 567, 15 ); + this.lIRCNoForwardingMessage.TabIndex = 8; + this.lIRCNoForwardingMessage.Text = "NOTE: If forwarding all messages is not enabled, only messages starting with a ha" + + "sh (#) will be relayed."; + // + // xIRCBotAnnounceIRCJoins + // + this.xIRCBotAnnounceIRCJoins.AutoSize = true; + this.xIRCBotAnnounceIRCJoins.Location = new System.Drawing.Point( 325, 84 ); + this.xIRCBotAnnounceIRCJoins.Name = "xIRCBotAnnounceIRCJoins"; + this.xIRCBotAnnounceIRCJoins.Size = new System.Drawing.Size( 303, 19 ); + this.xIRCBotAnnounceIRCJoins.TabIndex = 6; + this.xIRCBotAnnounceIRCJoins.Text = "Announce people joining/leaving the IRC channels."; + this.xIRCBotAnnounceIRCJoins.UseVisualStyleBackColor = true; + // + // bColorIRC + // + this.bColorIRC.BackColor = System.Drawing.Color.White; + this.bColorIRC.Location = new System.Drawing.Point( 152, 20 ); + this.bColorIRC.Name = "bColorIRC"; + this.bColorIRC.Size = new System.Drawing.Size( 100, 23 ); + this.bColorIRC.TabIndex = 1; + this.bColorIRC.UseVisualStyleBackColor = false; + this.bColorIRC.Click += new System.EventHandler( this.bColorIRC_Click ); + // + // lColorIRC + // + this.lColorIRC.AutoSize = true; + this.lColorIRC.Location = new System.Drawing.Point( 35, 24 ); + this.lColorIRC.Name = "lColorIRC"; + this.lColorIRC.Size = new System.Drawing.Size( 111, 15 ); + this.lColorIRC.TabIndex = 0; + this.lColorIRC.Text = "IRC message color"; + // + // xIRCBotForwardFromIRC + // + this.xIRCBotForwardFromIRC.AutoSize = true; + this.xIRCBotForwardFromIRC.Location = new System.Drawing.Point( 38, 84 ); + this.xIRCBotForwardFromIRC.Name = "xIRCBotForwardFromIRC"; + this.xIRCBotForwardFromIRC.Size = new System.Drawing.Size( 240, 19 ); + this.xIRCBotForwardFromIRC.TabIndex = 4; + this.xIRCBotForwardFromIRC.Text = "Forward ALL chat from IRC to SERVER."; + this.xIRCBotForwardFromIRC.UseVisualStyleBackColor = true; + // + // xIRCBotAnnounceServerJoins + // + this.xIRCBotAnnounceServerJoins.AutoSize = true; + this.xIRCBotAnnounceServerJoins.Location = new System.Drawing.Point( 325, 59 ); + this.xIRCBotAnnounceServerJoins.Name = "xIRCBotAnnounceServerJoins"; + this.xIRCBotAnnounceServerJoins.Size = new System.Drawing.Size( 279, 19 ); + this.xIRCBotAnnounceServerJoins.TabIndex = 5; + this.xIRCBotAnnounceServerJoins.Text = "Announce people joining/leaving the SERVER."; + this.xIRCBotAnnounceServerJoins.UseVisualStyleBackColor = true; + // + // xIRCBotForwardFromServer + // + this.xIRCBotForwardFromServer.AutoSize = true; + this.xIRCBotForwardFromServer.Location = new System.Drawing.Point( 38, 59 ); + this.xIRCBotForwardFromServer.Name = "xIRCBotForwardFromServer"; + this.xIRCBotForwardFromServer.Size = new System.Drawing.Size( 240, 19 ); + this.xIRCBotForwardFromServer.TabIndex = 3; + this.xIRCBotForwardFromServer.Text = "Forward ALL chat from SERVER to IRC."; + this.xIRCBotForwardFromServer.UseVisualStyleBackColor = true; + // + // gIRCNetwork + // + this.gIRCNetwork.Controls.Add( this.lIRCDelayUnits ); + this.gIRCNetwork.Controls.Add( this.xIRCRegisteredNick ); + this.gIRCNetwork.Controls.Add( this.tIRCNickServMessage ); + this.gIRCNetwork.Controls.Add( this.lIRCNickServMessage ); + this.gIRCNetwork.Controls.Add( this.tIRCNickServ ); + this.gIRCNetwork.Controls.Add( this.lIRCNickServ ); + this.gIRCNetwork.Controls.Add( this.nIRCDelay ); + this.gIRCNetwork.Controls.Add( this.lIRCDelay ); + this.gIRCNetwork.Controls.Add( this.lIRCBotChannels2 ); + this.gIRCNetwork.Controls.Add( this.lIRCBotChannels3 ); + this.gIRCNetwork.Controls.Add( this.tIRCBotChannels ); + this.gIRCNetwork.Controls.Add( this.lIRCBotChannels ); + this.gIRCNetwork.Controls.Add( this.nIRCBotPort ); + this.gIRCNetwork.Controls.Add( this.lIRCBotPort ); + this.gIRCNetwork.Controls.Add( this.tIRCBotNetwork ); + this.gIRCNetwork.Controls.Add( this.lIRCBotNetwork ); + this.gIRCNetwork.Controls.Add( this.lIRCBotNick ); + this.gIRCNetwork.Controls.Add( this.tIRCBotNick ); + this.gIRCNetwork.Location = new System.Drawing.Point( 8, 40 ); + this.gIRCNetwork.Name = "gIRCNetwork"; + this.gIRCNetwork.Size = new System.Drawing.Size( 636, 160 ); + this.gIRCNetwork.TabIndex = 4; + this.gIRCNetwork.TabStop = false; + this.gIRCNetwork.Text = "Network"; + // + // lIRCDelayUnits + // + this.lIRCDelayUnits.AutoSize = true; + this.lIRCDelayUnits.Location = new System.Drawing.Point( 598, 22 ); + this.lIRCDelayUnits.Name = "lIRCDelayUnits"; + this.lIRCDelayUnits.Size = new System.Drawing.Size( 24, 15 ); + this.lIRCDelayUnits.TabIndex = 6; + this.lIRCDelayUnits.Text = "ms"; + // + // xIRCRegisteredNick + // + this.xIRCRegisteredNick.AutoSize = true; + this.xIRCRegisteredNick.Location = new System.Drawing.Point( 265, 101 ); + this.xIRCRegisteredNick.Name = "xIRCRegisteredNick"; + this.xIRCRegisteredNick.Size = new System.Drawing.Size( 86, 19 ); + this.xIRCRegisteredNick.TabIndex = 13; + this.xIRCRegisteredNick.Text = "Registered"; + this.xIRCRegisteredNick.UseVisualStyleBackColor = true; + this.xIRCRegisteredNick.CheckedChanged += new System.EventHandler( this.xIRCRegisteredNick_CheckedChanged ); + // + // tIRCNickServMessage + // + this.tIRCNickServMessage.Enabled = false; + this.tIRCNickServMessage.Location = new System.Drawing.Point( 388, 126 ); + this.tIRCNickServMessage.Name = "tIRCNickServMessage"; + this.tIRCNickServMessage.Size = new System.Drawing.Size( 234, 21 ); + this.tIRCNickServMessage.TabIndex = 17; + // + // lIRCNickServMessage + // + this.lIRCNickServMessage.AutoSize = true; + this.lIRCNickServMessage.Enabled = false; + this.lIRCNickServMessage.Location = new System.Drawing.Point( 265, 129 ); + this.lIRCNickServMessage.Name = "lIRCNickServMessage"; + this.lIRCNickServMessage.Size = new System.Drawing.Size( 117, 15 ); + this.lIRCNickServMessage.TabIndex = 16; + this.lIRCNickServMessage.Text = "Authentication string"; + // + // tIRCNickServ + // + this.tIRCNickServ.Enabled = false; + this.tIRCNickServ.Location = new System.Drawing.Point( 121, 126 ); + this.tIRCNickServ.MaxLength = 32; + this.tIRCNickServ.Name = "tIRCNickServ"; + this.tIRCNickServ.Size = new System.Drawing.Size( 138, 21 ); + this.tIRCNickServ.TabIndex = 15; + // + // lIRCNickServ + // + this.lIRCNickServ.AutoSize = true; + this.lIRCNickServ.Enabled = false; + this.lIRCNickServ.Location = new System.Drawing.Point( 35, 129 ); + this.lIRCNickServ.Name = "lIRCNickServ"; + this.lIRCNickServ.Size = new System.Drawing.Size( 80, 15 ); + this.lIRCNickServ.TabIndex = 14; + this.lIRCNickServ.Text = "NickServ nick"; + // + // nIRCDelay + // + this.nIRCDelay.Increment = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + this.nIRCDelay.Location = new System.Drawing.Point( 536, 20 ); + this.nIRCDelay.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nIRCDelay.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nIRCDelay.Name = "nIRCDelay"; + this.nIRCDelay.Size = new System.Drawing.Size( 56, 21 ); + this.nIRCDelay.TabIndex = 5; + this.nIRCDelay.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // lIRCDelay + // + this.lIRCDelay.AutoSize = true; + this.lIRCDelay.Location = new System.Drawing.Point( 416, 22 ); + this.lIRCDelay.Name = "lIRCDelay"; + this.lIRCDelay.Size = new System.Drawing.Size( 114, 15 ); + this.lIRCDelay.TabIndex = 4; + this.lIRCDelay.Text = "Min message delay"; + // + // lIRCBotChannels2 + // + this.lIRCBotChannels2.AutoSize = true; + this.lIRCBotChannels2.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lIRCBotChannels2.Location = new System.Drawing.Point( 15, 65 ); + this.lIRCBotChannels2.Name = "lIRCBotChannels2"; + this.lIRCBotChannels2.Size = new System.Drawing.Size( 97, 13 ); + this.lIRCBotChannels2.TabIndex = 9; + this.lIRCBotChannels2.Text = "(comma seperated)"; + // + // lIRCBotChannels3 + // + this.lIRCBotChannels3.AutoSize = true; + this.lIRCBotChannels3.Location = new System.Drawing.Point( 118, 71 ); + this.lIRCBotChannels3.Name = "lIRCBotChannels3"; + this.lIRCBotChannels3.Size = new System.Drawing.Size( 340, 15 ); + this.lIRCBotChannels3.TabIndex = 10; + this.lIRCBotChannels3.Text = "NOTE: Channel names are case-sensitive on some networks!"; + // + // tIRCBotChannels + // + this.tIRCBotChannels.Location = new System.Drawing.Point( 121, 47 ); + this.tIRCBotChannels.MaxLength = 1000; + this.tIRCBotChannels.Name = "tIRCBotChannels"; + this.tIRCBotChannels.Size = new System.Drawing.Size( 501, 21 ); + this.tIRCBotChannels.TabIndex = 8; + // + // lIRCBotChannels + // + this.lIRCBotChannels.AutoSize = true; + this.lIRCBotChannels.Location = new System.Drawing.Point( 20, 50 ); + this.lIRCBotChannels.Name = "lIRCBotChannels"; + this.lIRCBotChannels.Size = new System.Drawing.Size( 95, 15 ); + this.lIRCBotChannels.TabIndex = 7; + this.lIRCBotChannels.Text = "Channels to join"; + // + // nIRCBotPort + // + this.nIRCBotPort.Location = new System.Drawing.Point( 300, 20 ); + this.nIRCBotPort.Maximum = new decimal( new int[] { + 65535, + 0, + 0, + 0} ); + this.nIRCBotPort.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nIRCBotPort.Name = "nIRCBotPort"; + this.nIRCBotPort.Size = new System.Drawing.Size( 64, 21 ); + this.nIRCBotPort.TabIndex = 3; + this.nIRCBotPort.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + // + // lIRCBotPort + // + this.lIRCBotPort.AutoSize = true; + this.lIRCBotPort.Location = new System.Drawing.Point( 265, 22 ); + this.lIRCBotPort.Name = "lIRCBotPort"; + this.lIRCBotPort.Size = new System.Drawing.Size( 29, 15 ); + this.lIRCBotPort.TabIndex = 2; + this.lIRCBotPort.Text = "Port"; + // + // tIRCBotNetwork + // + this.tIRCBotNetwork.Location = new System.Drawing.Point( 121, 19 ); + this.tIRCBotNetwork.MaxLength = 512; + this.tIRCBotNetwork.Name = "tIRCBotNetwork"; + this.tIRCBotNetwork.Size = new System.Drawing.Size( 138, 21 ); + this.tIRCBotNetwork.TabIndex = 1; + // + // lIRCBotNetwork + // + this.lIRCBotNetwork.AutoSize = true; + this.lIRCBotNetwork.Location = new System.Drawing.Point( 26, 22 ); + this.lIRCBotNetwork.Name = "lIRCBotNetwork"; + this.lIRCBotNetwork.Size = new System.Drawing.Size( 89, 15 ); + this.lIRCBotNetwork.TabIndex = 0; + this.lIRCBotNetwork.Text = "IRC server host"; + // + // lIRCBotNick + // + this.lIRCBotNick.AutoSize = true; + this.lIRCBotNick.Location = new System.Drawing.Point( 65, 102 ); + this.lIRCBotNick.Name = "lIRCBotNick"; + this.lIRCBotNick.Size = new System.Drawing.Size( 50, 15 ); + this.lIRCBotNick.TabIndex = 11; + this.lIRCBotNick.Text = "Bot nick"; + // + // tIRCBotNick + // + this.tIRCBotNick.Location = new System.Drawing.Point( 121, 99 ); + this.tIRCBotNick.MaxLength = 32; + this.tIRCBotNick.Name = "tIRCBotNick"; + this.tIRCBotNick.Size = new System.Drawing.Size( 138, 21 ); + this.tIRCBotNick.TabIndex = 12; + // + // lIRCList + // + this.lIRCList.AutoSize = true; + this.lIRCList.Enabled = false; + this.lIRCList.Location = new System.Drawing.Point( 213, 14 ); + this.lIRCList.Name = "lIRCList"; + this.lIRCList.Size = new System.Drawing.Size( 105, 15 ); + this.lIRCList.TabIndex = 1; + this.lIRCList.Text = "Popular networks:"; + // + // xIRCBotEnabled + // + this.xIRCBotEnabled.AutoSize = true; + this.xIRCBotEnabled.Location = new System.Drawing.Point( 14, 13 ); + this.xIRCBotEnabled.Name = "xIRCBotEnabled"; + this.xIRCBotEnabled.Size = new System.Drawing.Size( 149, 19 ); + this.xIRCBotEnabled.TabIndex = 0; + this.xIRCBotEnabled.Text = "Enable IRC integration"; + this.xIRCBotEnabled.UseVisualStyleBackColor = true; + this.xIRCBotEnabled.CheckedChanged += new System.EventHandler( this.xIRC_CheckedChanged ); + // + // cIRCList + // + this.cIRCList.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cIRCList.Enabled = false; + this.cIRCList.FormattingEnabled = true; + this.cIRCList.Location = new System.Drawing.Point( 321, 11 ); + this.cIRCList.Name = "cIRCList"; + this.cIRCList.Size = new System.Drawing.Size( 138, 23 ); + this.cIRCList.TabIndex = 2; + this.cIRCList.SelectedIndexChanged += new System.EventHandler( this.cIRCList_SelectedIndexChanged ); + // + // tabAdvanced + // + this.tabAdvanced.Controls.Add( this.gPerformance ); + this.tabAdvanced.Controls.Add( this.gAdvancedMisc ); + this.tabAdvanced.Controls.Add( this.gCrashReport ); + this.tabAdvanced.Location = new System.Drawing.Point( 4, 24 ); + this.tabAdvanced.Name = "tabAdvanced"; + this.tabAdvanced.Padding = new System.Windows.Forms.Padding( 5, 10, 5, 10 ); + this.tabAdvanced.Size = new System.Drawing.Size( 652, 482 ); + this.tabAdvanced.TabIndex = 6; + this.tabAdvanced.Text = "Advanced"; + this.tabAdvanced.UseVisualStyleBackColor = true; + // + // gPerformance + // + this.gPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gPerformance.Controls.Add( this.lAdvancedWarning ); + this.gPerformance.Controls.Add( this.xLowLatencyMode ); + this.gPerformance.Controls.Add( this.lProcessPriority ); + this.gPerformance.Controls.Add( this.cProcessPriority ); + this.gPerformance.Controls.Add( this.nTickInterval ); + this.gPerformance.Controls.Add( this.lTickIntervalUnits ); + this.gPerformance.Controls.Add( this.lTickInterval ); + this.gPerformance.Controls.Add( this.nThrottling ); + this.gPerformance.Controls.Add( this.lThrottling ); + this.gPerformance.Controls.Add( this.lThrottlingUnits ); + this.gPerformance.Location = new System.Drawing.Point( 8, 328 ); + this.gPerformance.Name = "gPerformance"; + this.gPerformance.Size = new System.Drawing.Size( 636, 151 ); + this.gPerformance.TabIndex = 2; + this.gPerformance.TabStop = false; + this.gPerformance.Text = "Performance"; + // + // lAdvancedWarning + // + this.lAdvancedWarning.AutoSize = true; + this.lAdvancedWarning.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lAdvancedWarning.Location = new System.Drawing.Point( 15, 21 ); + this.lAdvancedWarning.Name = "lAdvancedWarning"; + this.lAdvancedWarning.Size = new System.Drawing.Size( 558, 30 ); + this.lAdvancedWarning.TabIndex = 0; + this.lAdvancedWarning.Text = "Warning: Altering these settings may decrease your server\'s stability and perform" + + "ance.\r\nIf you\'re not sure what these settings do, you probably shouldn\'t change " + + "them..."; + // + // xLowLatencyMode + // + this.xLowLatencyMode.AutoSize = true; + this.xLowLatencyMode.Location = new System.Drawing.Point( 6, 64 ); + this.xLowLatencyMode.Name = "xLowLatencyMode"; + this.xLowLatencyMode.Size = new System.Drawing.Size( 544, 19 ); + this.xLowLatencyMode.TabIndex = 3; + this.xLowLatencyMode.Text = "Low-latency mode (disables Nagle\'s algorithm, reducing latency but increasing ban" + + "dwidth use)."; + this.xLowLatencyMode.UseVisualStyleBackColor = true; + // + // lProcessPriority + // + this.lProcessPriority.AutoSize = true; + this.lProcessPriority.Location = new System.Drawing.Point( 19, 94 ); + this.lProcessPriority.Name = "lProcessPriority"; + this.lProcessPriority.Size = new System.Drawing.Size( 90, 15 ); + this.lProcessPriority.TabIndex = 10; + this.lProcessPriority.Text = "Process priority"; + // + // cProcessPriority + // + this.cProcessPriority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cProcessPriority.Items.AddRange( new object[] { + "(system default)", + "High", + "Above Normal", + "Normal", + "Below Normal", + "Low"} ); + this.cProcessPriority.Location = new System.Drawing.Point( 115, 91 ); + this.cProcessPriority.Name = "cProcessPriority"; + this.cProcessPriority.Size = new System.Drawing.Size( 109, 23 ); + this.cProcessPriority.TabIndex = 11; + // + // nTickInterval + // + this.nTickInterval.Increment = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + this.nTickInterval.Location = new System.Drawing.Point( 429, 92 ); + this.nTickInterval.Maximum = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nTickInterval.Minimum = new decimal( new int[] { + 10, + 0, + 0, + 0} ); + this.nTickInterval.Name = "nTickInterval"; + this.nTickInterval.Size = new System.Drawing.Size( 70, 21 ); + this.nTickInterval.TabIndex = 13; + this.nTickInterval.Value = new decimal( new int[] { + 100, + 0, + 0, + 0} ); + // + // lTickIntervalUnits + // + this.lTickIntervalUnits.AutoSize = true; + this.lTickIntervalUnits.Location = new System.Drawing.Point( 505, 94 ); + this.lTickIntervalUnits.Name = "lTickIntervalUnits"; + this.lTickIntervalUnits.Size = new System.Drawing.Size( 24, 15 ); + this.lTickIntervalUnits.TabIndex = 14; + this.lTickIntervalUnits.Text = "ms"; + // + // lTickInterval + // + this.lTickInterval.AutoSize = true; + this.lTickInterval.Location = new System.Drawing.Point( 352, 94 ); + this.lTickInterval.Name = "lTickInterval"; + this.lTickInterval.Size = new System.Drawing.Size( 71, 15 ); + this.lTickInterval.TabIndex = 12; + this.lTickInterval.Text = "Tick interval"; + // + // nThrottling + // + this.nThrottling.Increment = new decimal( new int[] { + 100, + 0, + 0, + 0} ); + this.nThrottling.Location = new System.Drawing.Point( 115, 120 ); + this.nThrottling.Maximum = new decimal( new int[] { + 10000, + 0, + 0, + 0} ); + this.nThrottling.Minimum = new decimal( new int[] { + 100, + 0, + 0, + 0} ); + this.nThrottling.Name = "nThrottling"; + this.nThrottling.Size = new System.Drawing.Size( 70, 21 ); + this.nThrottling.TabIndex = 16; + this.nThrottling.Value = new decimal( new int[] { + 2048, + 0, + 0, + 0} ); + // + // lThrottling + // + this.lThrottling.AutoSize = true; + this.lThrottling.Location = new System.Drawing.Point( 22, 122 ); + this.lThrottling.Name = "lThrottling"; + this.lThrottling.Size = new System.Drawing.Size( 87, 15 ); + this.lThrottling.TabIndex = 15; + this.lThrottling.Text = "Block throttling"; + // + // lThrottlingUnits + // + this.lThrottlingUnits.AutoSize = true; + this.lThrottlingUnits.Location = new System.Drawing.Point( 191, 122 ); + this.lThrottlingUnits.Name = "lThrottlingUnits"; + this.lThrottlingUnits.Size = new System.Drawing.Size( 129, 15 ); + this.lThrottlingUnits.TabIndex = 17; + this.lThrottlingUnits.Text = "blocks / second / client"; + // + // gAdvancedMisc + // + this.gAdvancedMisc.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gAdvancedMisc.Controls.Add( this.nMaxUndoStates ); + this.gAdvancedMisc.Controls.Add( this.lMaxUndoStates ); + this.gAdvancedMisc.Controls.Add( this.xHeartbeatToWoMDirect ); + this.gAdvancedMisc.Controls.Add( this.lIPWarning ); + this.gAdvancedMisc.Controls.Add( this.tIP ); + this.gAdvancedMisc.Controls.Add( this.xIP ); + this.gAdvancedMisc.Controls.Add( this.lConsoleName ); + this.gAdvancedMisc.Controls.Add( this.tConsoleName ); + this.gAdvancedMisc.Controls.Add( this.nMaxUndo ); + this.gAdvancedMisc.Controls.Add( this.lMaxUndoUnits ); + this.gAdvancedMisc.Controls.Add( this.xMaxUndo ); + this.gAdvancedMisc.Controls.Add( this.xRelayAllBlockUpdates ); + this.gAdvancedMisc.Controls.Add( this.xNoPartialPositionUpdates ); + this.gAdvancedMisc.Location = new System.Drawing.Point( 8, 118 ); + this.gAdvancedMisc.Name = "gAdvancedMisc"; + this.gAdvancedMisc.Size = new System.Drawing.Size( 636, 204 ); + this.gAdvancedMisc.TabIndex = 1; + this.gAdvancedMisc.TabStop = false; + this.gAdvancedMisc.Text = "Miscellaneous"; + // + // nMaxUndoStates + // + this.nMaxUndoStates.Location = new System.Drawing.Point( 115, 71 ); + this.nMaxUndoStates.Minimum = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxUndoStates.Name = "nMaxUndoStates"; + this.nMaxUndoStates.Size = new System.Drawing.Size( 58, 21 ); + this.nMaxUndoStates.TabIndex = 23; + this.nMaxUndoStates.Value = new decimal( new int[] { + 1, + 0, + 0, + 0} ); + this.nMaxUndoStates.ValueChanged += new System.EventHandler( this.nMaxUndo_ValueChanged ); + // + // lMaxUndoStates + // + this.lMaxUndoStates.AutoSize = true; + this.lMaxUndoStates.Location = new System.Drawing.Point( 179, 73 ); + this.lMaxUndoStates.Name = "lMaxUndoStates"; + this.lMaxUndoStates.Size = new System.Drawing.Size( 72, 15 ); + this.lMaxUndoStates.TabIndex = 22; + this.lMaxUndoStates.Text = "states, up to"; + // + // xHeartbeatToWoMDirect + // + this.xHeartbeatToWoMDirect.AutoSize = true; + this.xHeartbeatToWoMDirect.Location = new System.Drawing.Point( 6, 176 ); + this.xHeartbeatToWoMDirect.Name = "xHeartbeatToWoMDirect"; + this.xHeartbeatToWoMDirect.Size = new System.Drawing.Size( 354, 19 ); + this.xHeartbeatToWoMDirect.TabIndex = 21; + this.xHeartbeatToWoMDirect.Text = "Send heartbeats to WoM Direct (direct.worldofminecraft.net)."; + this.xHeartbeatToWoMDirect.UseVisualStyleBackColor = true; + // + // lIPWarning + // + this.lIPWarning.AutoSize = true; + this.lIPWarning.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lIPWarning.Location = new System.Drawing.Point( 112, 151 ); + this.lIPWarning.Name = "lIPWarning"; + this.lIPWarning.Size = new System.Drawing.Size( 408, 13 ); + this.lIPWarning.TabIndex = 20; + this.lIPWarning.Text = "Note: You do not need to specify an IP address unless you have multiple NICs or I" + + "Ps."; + // + // tIP + // + this.tIP.Location = new System.Drawing.Point( 115, 127 ); + this.tIP.MaxLength = 15; + this.tIP.Name = "tIP"; + this.tIP.Size = new System.Drawing.Size( 97, 21 ); + this.tIP.TabIndex = 19; + this.tIP.Validating += new System.ComponentModel.CancelEventHandler( this.tIP_Validating ); + // + // xIP + // + this.xIP.AutoSize = true; + this.xIP.Location = new System.Drawing.Point( 6, 129 ); + this.xIP.Name = "xIP"; + this.xIP.Size = new System.Drawing.Size( 103, 19 ); + this.xIP.TabIndex = 18; + this.xIP.Text = "Designated IP"; + this.xIP.UseVisualStyleBackColor = true; + this.xIP.CheckedChanged += new System.EventHandler( this.xIP_CheckedChanged ); + // + // lConsoleName + // + this.lConsoleName.AutoSize = true; + this.lConsoleName.Location = new System.Drawing.Point( 22, 103 ); + this.lConsoleName.Name = "lConsoleName"; + this.lConsoleName.Size = new System.Drawing.Size( 87, 15 ); + this.lConsoleName.TabIndex = 7; + this.lConsoleName.Text = "Console name"; + // + // tConsoleName + // + this.tConsoleName.Location = new System.Drawing.Point( 115, 100 ); + this.tConsoleName.Name = "tConsoleName"; + this.tConsoleName.Size = new System.Drawing.Size( 167, 21 ); + this.tConsoleName.TabIndex = 8; + // + // nMaxUndo + // + this.nMaxUndo.Increment = new decimal( new int[] { + 1000, + 0, + 0, + 0} ); + this.nMaxUndo.Location = new System.Drawing.Point( 257, 71 ); + this.nMaxUndo.Maximum = new decimal( new int[] { + 2147483647, + 0, + 0, + 0} ); + this.nMaxUndo.Name = "nMaxUndo"; + this.nMaxUndo.Size = new System.Drawing.Size( 86, 21 ); + this.nMaxUndo.TabIndex = 5; + this.nMaxUndo.Value = new decimal( new int[] { + 2000000, + 0, + 0, + 0} ); + this.nMaxUndo.ValueChanged += new System.EventHandler( this.nMaxUndo_ValueChanged ); + // + // lMaxUndoUnits + // + this.lMaxUndoUnits.AutoSize = true; + this.lMaxUndoUnits.Location = new System.Drawing.Point( 349, 73 ); + this.lMaxUndoUnits.Name = "lMaxUndoUnits"; + this.lMaxUndoUnits.Size = new System.Drawing.Size( 259, 15 ); + this.lMaxUndoUnits.TabIndex = 6; + this.lMaxUndoUnits.Text = "blocks each (up to 16.0 MB of RAM per player)"; + // + // xMaxUndo + // + this.xMaxUndo.AutoSize = true; + this.xMaxUndo.Checked = true; + this.xMaxUndo.CheckState = System.Windows.Forms.CheckState.Checked; + this.xMaxUndo.Location = new System.Drawing.Point( 6, 72 ); + this.xMaxUndo.Name = "xMaxUndo"; + this.xMaxUndo.Size = new System.Drawing.Size( 100, 19 ); + this.xMaxUndo.TabIndex = 4; + this.xMaxUndo.Text = "Limit /undo to"; + this.xMaxUndo.UseVisualStyleBackColor = true; + this.xMaxUndo.CheckedChanged += new System.EventHandler( this.xMaxUndo_CheckedChanged ); + // + // xRelayAllBlockUpdates + // + this.xRelayAllBlockUpdates.AutoSize = true; + this.xRelayAllBlockUpdates.Location = new System.Drawing.Point( 6, 21 ); + this.xRelayAllBlockUpdates.Name = "xRelayAllBlockUpdates"; + this.xRelayAllBlockUpdates.Size = new System.Drawing.Size( 560, 19 ); + this.xRelayAllBlockUpdates.TabIndex = 1; + this.xRelayAllBlockUpdates.Text = "When a player changes a block, send him the redundant update packet anyway (origi" + + "nal behavior)."; + this.xRelayAllBlockUpdates.UseVisualStyleBackColor = true; + // + // xNoPartialPositionUpdates + // + this.xNoPartialPositionUpdates.AutoSize = true; + this.xNoPartialPositionUpdates.Location = new System.Drawing.Point( 6, 46 ); + this.xNoPartialPositionUpdates.Name = "xNoPartialPositionUpdates"; + this.xNoPartialPositionUpdates.Size = new System.Drawing.Size( 326, 19 ); + this.xNoPartialPositionUpdates.TabIndex = 2; + this.xNoPartialPositionUpdates.Text = "Do not use partial position updates (opcodes 9, 10, 11)."; + this.xNoPartialPositionUpdates.UseVisualStyleBackColor = true; + // + // gCrashReport + // + this.gCrashReport.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gCrashReport.Controls.Add( this.lCrashReportDisclaimer ); + this.gCrashReport.Controls.Add( this.xSubmitCrashReports ); + this.gCrashReport.Location = new System.Drawing.Point( 8, 13 ); + this.gCrashReport.Name = "gCrashReport"; + this.gCrashReport.Size = new System.Drawing.Size( 636, 99 ); + this.gCrashReport.TabIndex = 0; + this.gCrashReport.TabStop = false; + this.gCrashReport.Text = "Crash Reporting"; + // + // lCrashReportDisclaimer + // + this.lCrashReportDisclaimer.AutoSize = true; + this.lCrashReportDisclaimer.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lCrashReportDisclaimer.Location = new System.Drawing.Point( 42, 42 ); + this.lCrashReportDisclaimer.Name = "lCrashReportDisclaimer"; + this.lCrashReportDisclaimer.Size = new System.Drawing.Size( 521, 39 ); + this.lCrashReportDisclaimer.TabIndex = 1; + this.lCrashReportDisclaimer.Text = resources.GetString( "lCrashReportDisclaimer.Text" ); + // + // xSubmitCrashReports + // + this.xSubmitCrashReports.AutoSize = true; + this.xSubmitCrashReports.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.xSubmitCrashReports.Location = new System.Drawing.Point( 6, 20 ); + this.xSubmitCrashReports.Name = "xSubmitCrashReports"; + this.xSubmitCrashReports.Size = new System.Drawing.Size( 446, 19 ); + this.xSubmitCrashReports.TabIndex = 0; + this.xSubmitCrashReports.Text = "Automatically submit crash reports to fCraft developers (fCraft.net)"; + this.xSubmitCrashReports.UseVisualStyleBackColor = true; + // + // bOK + // + this.bOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bOK.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bOK.Location = new System.Drawing.Point( 360, 528 ); + this.bOK.Name = "bOK"; + this.bOK.Size = new System.Drawing.Size( 100, 28 ); + this.bOK.TabIndex = 1; + this.bOK.Text = "OK"; + this.bOK.Click += new System.EventHandler( this.bSave_Click ); + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bCancel.Location = new System.Drawing.Point( 466, 528 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 28 ); + this.bCancel.TabIndex = 2; + this.bCancel.Text = "Cancel"; + this.bCancel.Click += new System.EventHandler( this.bCancel_Click ); + // + // bResetTab + // + this.bResetTab.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bResetTab.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bResetTab.Location = new System.Drawing.Point( 132, 528 ); + this.bResetTab.Name = "bResetTab"; + this.bResetTab.Size = new System.Drawing.Size( 100, 28 ); + this.bResetTab.TabIndex = 5; + this.bResetTab.Text = "Reset Tab"; + this.bResetTab.UseVisualStyleBackColor = true; + this.bResetTab.Click += new System.EventHandler( this.bResetTab_Click ); + // + // bResetAll + // + this.bResetAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bResetAll.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bResetAll.Location = new System.Drawing.Point( 12, 528 ); + this.bResetAll.Name = "bResetAll"; + this.bResetAll.Size = new System.Drawing.Size( 114, 28 ); + this.bResetAll.TabIndex = 4; + this.bResetAll.Text = "Reset All Defaults"; + this.bResetAll.UseVisualStyleBackColor = true; + this.bResetAll.Click += new System.EventHandler( this.bResetAll_Click ); + // + // bApply + // + this.bApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bApply.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bApply.Location = new System.Drawing.Point( 572, 528 ); + this.bApply.Name = "bApply"; + this.bApply.Size = new System.Drawing.Size( 100, 28 ); + this.bApply.TabIndex = 3; + this.bApply.Text = "Apply"; + this.bApply.Click += new System.EventHandler( this.bApply_Click ); + // + // toolTip + // + this.toolTip.AutoPopDelay = 11111; + this.toolTip.InitialDelay = 500; + this.toolTip.IsBalloon = true; + this.toolTip.ReshowDelay = 100; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 684, 568 ); + this.Controls.Add( this.bApply ); + this.Controls.Add( this.bResetAll ); + this.Controls.Add( this.bResetTab ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bOK ); + this.Controls.Add( this.tabs ); + this.Icon = ((System.Drawing.Icon)(resources.GetObject( "$this.Icon" ))); + this.MinimumSize = new System.Drawing.Size( 700, 547 ); + this.Name = "MainForm"; + this.Text = "fCraft Config Tool"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler( this.ConfigUI_FormClosing ); + this.tabs.ResumeLayout( false ); + this.tabGeneral.ResumeLayout( false ); + this.gUpdaterSettings.ResumeLayout( false ); + this.gUpdaterSettings.PerformLayout(); + this.groupBox2.ResumeLayout( false ); + this.gHelpAndSupport.ResumeLayout( false ); + this.gInformation.ResumeLayout( false ); + this.gInformation.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nAnnouncements)).EndInit(); + this.gBasic.ResumeLayout( false ); + this.gBasic.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxPlayersPerWorld)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nPort)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nUploadBandwidth)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxPlayers)).EndInit(); + this.tabChat.ResumeLayout( false ); + this.gChatColors.ResumeLayout( false ); + this.gChatColors.PerformLayout(); + this.gAppearence.ResumeLayout( false ); + this.gAppearence.PerformLayout(); + this.tabWorlds.ResumeLayout( false ); + this.tabWorlds.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvWorlds)).EndInit(); + this.tabRanks.ResumeLayout( false ); + this.tabRanks.PerformLayout(); + this.gPermissionLimits.ResumeLayout( false ); + this.gRankOptions.ResumeLayout( false ); + this.gRankOptions.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nFillLimit)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nCopyPasteSlots)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntiGriefSeconds)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nDrawLimit)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nKickIdle)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntiGriefBlocks)).EndInit(); + this.tabSecurity.ResumeLayout( false ); + this.gBlockDB.ResumeLayout( false ); + this.gBlockDB.PerformLayout(); + this.gSecurityMisc.ResumeLayout( false ); + this.gSecurityMisc.PerformLayout(); + this.gSpamChat.ResumeLayout( false ); + this.gSpamChat.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamMaxWarnings)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nSpamMute)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamInterval)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nAntispamMessageCount)).EndInit(); + this.gVerify.ResumeLayout( false ); + this.gVerify.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxConnectionsPerIP)).EndInit(); + this.tabSavingAndBackup.ResumeLayout( false ); + this.gDataBackup.ResumeLayout( false ); + this.gDataBackup.PerformLayout(); + this.gSaving.ResumeLayout( false ); + this.gSaving.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nSaveInterval)).EndInit(); + this.gBackups.ResumeLayout( false ); + this.gBackups.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxBackupSize)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxBackups)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nBackupInterval)).EndInit(); + this.tabLogging.ResumeLayout( false ); + this.gLogFile.ResumeLayout( false ); + this.gLogFile.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nLogLimit)).EndInit(); + this.gConsole.ResumeLayout( false ); + this.gConsole.PerformLayout(); + this.tabIRC.ResumeLayout( false ); + this.tabIRC.PerformLayout(); + this.gIRCOptions.ResumeLayout( false ); + this.gIRCOptions.PerformLayout(); + this.gIRCNetwork.ResumeLayout( false ); + this.gIRCNetwork.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nIRCDelay)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nIRCBotPort)).EndInit(); + this.tabAdvanced.ResumeLayout( false ); + this.gPerformance.ResumeLayout( false ); + this.gPerformance.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nTickInterval)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nThrottling)).EndInit(); + this.gAdvancedMisc.ResumeLayout( false ); + this.gAdvancedMisc.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxUndoStates)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nMaxUndo)).EndInit(); + this.gCrashReport.ResumeLayout( false ); + this.gCrashReport.PerformLayout(); + this.ResumeLayout( false ); + + } + + #endregion + + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.Button bOK; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.Button bResetTab; + private System.Windows.Forms.TabPage tabGeneral; + private System.Windows.Forms.TabPage tabRanks; + private System.Windows.Forms.Label lServerName; + private System.Windows.Forms.TextBox tServerName; + private System.Windows.Forms.Label lMOTD; + private System.Windows.Forms.TextBox tMOTD; + private System.Windows.Forms.Label lMaxPlayers; + private System.Windows.Forms.NumericUpDown nMaxPlayers; + private System.Windows.Forms.TabPage tabSavingAndBackup; + private System.Windows.Forms.ComboBox cPublic; + private System.Windows.Forms.Label lPublic; + private System.Windows.Forms.Button bMeasure; + private System.Windows.Forms.Label lUploadBandwidthUnits; + private System.Windows.Forms.NumericUpDown nUploadBandwidth; + private System.Windows.Forms.Label lUploadBandwidth; + private System.Windows.Forms.TabPage tabLogging; + private System.Windows.Forms.TabPage tabAdvanced; + private System.Windows.Forms.Label lTickIntervalUnits; + private System.Windows.Forms.NumericUpDown nTickInterval; + private System.Windows.Forms.Label lTickInterval; + private System.Windows.Forms.Label lAdvancedWarning; + private System.Windows.Forms.ListBox vRanks; + private System.Windows.Forms.Button bAddRank; + private System.Windows.Forms.Label lPermissions; + private System.Windows.Forms.ListView vPermissions; + private System.Windows.Forms.ColumnHeader chPermissions; + private System.Windows.Forms.GroupBox gRankOptions; + private System.Windows.Forms.Button bDeleteRank; + private System.Windows.Forms.Label lRankColor; + private System.Windows.Forms.TextBox tRankName; + private System.Windows.Forms.Label lRankName; + private System.Windows.Forms.TextBox tPrefix; + private System.Windows.Forms.Label lPrefix; + private System.Windows.Forms.Label lAntiGrief2; + private System.Windows.Forms.NumericUpDown nAntiGriefBlocks; + private System.Windows.Forms.CheckBox xDrawLimit; + private System.Windows.Forms.Label lDrawLimitUnits; + private System.Windows.Forms.GroupBox gBasic; + private System.Windows.Forms.ComboBox cDefaultRank; + private System.Windows.Forms.Label lDefaultRank; + private System.Windows.Forms.GroupBox gSaving; + private System.Windows.Forms.NumericUpDown nSaveInterval; + private System.Windows.Forms.Label lSaveIntervalUnits; + private System.Windows.Forms.CheckBox xSaveInterval; + private System.Windows.Forms.GroupBox gBackups; + private System.Windows.Forms.CheckBox xBackupOnStartup; + private System.Windows.Forms.NumericUpDown nBackupInterval; + private System.Windows.Forms.Label lBackupIntervalUnits; + private System.Windows.Forms.CheckBox xBackupInterval; + private System.Windows.Forms.CheckBox xBackupOnJoin; + private System.Windows.Forms.CheckBox xRelayAllBlockUpdates; + private System.Windows.Forms.ComboBox cProcessPriority; + private System.Windows.Forms.Label lProcessPriority; + private System.Windows.Forms.Button bResetAll; + private System.Windows.Forms.GroupBox gLogFile; + private System.Windows.Forms.ComboBox cLogMode; + private System.Windows.Forms.Label lLogMode; + private System.Windows.Forms.GroupBox gConsole; + private System.Windows.Forms.ListView vLogFileOptions; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.Label lLogLimitUnits; + private System.Windows.Forms.NumericUpDown nLogLimit; + private System.Windows.Forms.ListView vConsoleOptions; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.CheckBox xLogLimit; + private System.Windows.Forms.CheckBox xReserveSlot; + private System.Windows.Forms.NumericUpDown nDrawLimit; + private System.Windows.Forms.Label lKickIdleUnits; + private System.Windows.Forms.NumericUpDown nKickIdle; + private System.Windows.Forms.CheckBox xKickIdle; + private System.Windows.Forms.CheckBox xNoPartialPositionUpdates; + private System.Windows.Forms.Label lMaxBackups; + private System.Windows.Forms.NumericUpDown nMaxBackups; + private System.Windows.Forms.Label lMaxBackupSize; + private System.Windows.Forms.NumericUpDown nMaxBackupSize; + private System.Windows.Forms.CheckBox xMaxBackupSize; + private System.Windows.Forms.CheckBox xMaxBackups; + private System.Windows.Forms.Label lThrottlingUnits; + private System.Windows.Forms.NumericUpDown nThrottling; + private System.Windows.Forms.Label lThrottling; + private System.Windows.Forms.Button bApply; + private System.Windows.Forms.Button bColorRank; + private System.Windows.Forms.TabPage tabSecurity; + private System.Windows.Forms.GroupBox gVerify; + private System.Windows.Forms.Label lVerifyNames; + private System.Windows.Forms.ComboBox cVerifyNames; + private System.Windows.Forms.GroupBox gSpamChat; + private System.Windows.Forms.Label lAntispamInterval; + private System.Windows.Forms.NumericUpDown nAntispamInterval; + private System.Windows.Forms.Label lAntispamMessageCount; + private System.Windows.Forms.NumericUpDown nAntispamMessageCount; + private System.Windows.Forms.Label lSpamChat; + private System.Windows.Forms.CheckBox xLowLatencyMode; + private System.Windows.Forms.CheckBox xAntispamKicks; + private System.Windows.Forms.Label lSpamMuteSeconds; + private System.Windows.Forms.NumericUpDown nSpamMute; + private System.Windows.Forms.Label lSpamMute; + private System.Windows.Forms.Label lAntispamMaxWarnings; + private System.Windows.Forms.NumericUpDown nAntispamMaxWarnings; + private System.Windows.Forms.CheckBox xBackupOnlyWhenChanged; + private System.Windows.Forms.Label lPort; + private System.Windows.Forms.NumericUpDown nPort; + private System.Windows.Forms.Button bRules; + private System.Windows.Forms.TabPage tabIRC; + private System.Windows.Forms.GroupBox gIRCNetwork; + private System.Windows.Forms.CheckBox xIRCBotEnabled; + private System.Windows.Forms.CheckBox xIRCBotAnnounceServerJoins; + private System.Windows.Forms.Label lIRCBotChannels; + private System.Windows.Forms.NumericUpDown nIRCBotPort; + private System.Windows.Forms.Label lIRCBotPort; + private System.Windows.Forms.TextBox tIRCBotNetwork; + private System.Windows.Forms.Label lIRCBotNetwork; + private System.Windows.Forms.Label lIRCBotNick; + private System.Windows.Forms.TextBox tIRCBotNick; + private System.Windows.Forms.CheckBox xIRCBotForwardFromServer; + private System.Windows.Forms.GroupBox gIRCOptions; + private System.Windows.Forms.TextBox tIRCBotChannels; + private System.Windows.Forms.Label lIRCBotChannels3; + private System.Windows.Forms.Label lIRCBotChannels2; + private System.Windows.Forms.CheckBox xIRCBotForwardFromIRC; + private System.Windows.Forms.CheckBox xMaxConnectionsPerIP; + private System.Windows.Forms.TabPage tabWorlds; + private System.Windows.Forms.DataGridView dgvWorlds; + private System.Windows.Forms.Button bWorldDelete; + private System.Windows.Forms.Button bAddWorld; + private System.Windows.Forms.Button bWorldEdit; + private System.Windows.Forms.ComboBox cMainWorld; + private System.Windows.Forms.Label lMainWorld; + private System.Windows.Forms.GroupBox gInformation; + private System.Windows.Forms.CheckBox xAnnouncements; + private System.Windows.Forms.Button bAnnouncements; + private System.Windows.Forms.Label lAnnouncementsUnits; + private System.Windows.Forms.NumericUpDown nAnnouncements; + private System.Windows.Forms.Label lAntiGrief3; + private System.Windows.Forms.NumericUpDown nAntiGriefSeconds; + private System.Windows.Forms.CheckBox xAntiGrief; + private System.Windows.Forms.Label lAntiGrief1; + private System.Windows.Forms.GroupBox gSecurityMisc; + private System.Windows.Forms.CheckBox xAnnounceKickAndBanReasons; + private System.Windows.Forms.CheckBox xRequireRankChangeReason; + private System.Windows.Forms.CheckBox xRequireBanReason; + private System.Windows.Forms.CheckBox xAnnounceRankChanges; + private System.Windows.Forms.Button bPortCheck; + private System.Windows.Forms.Button bColorIRC; + private System.Windows.Forms.Label lColorIRC; + private System.Windows.Forms.CheckBox xIRCBotAnnounceIRCJoins; + private System.Windows.Forms.CheckBox xSubmitCrashReports; + private System.Windows.Forms.GroupBox gCrashReport; + private System.Windows.Forms.Label lCrashReportDisclaimer; + private System.Windows.Forms.GroupBox gAdvancedMisc; + private System.Windows.Forms.Label lIRCDelay; + private System.Windows.Forms.NumericUpDown nIRCDelay; + private System.Windows.Forms.ComboBox cPatrolledRank; + private System.Windows.Forms.Label lPatrolledRank; + private System.Windows.Forms.Label lPatrolledRankAndBelow; + private System.Windows.Forms.Button bLowerRank; + private System.Windows.Forms.Button bRaiseRank; + private System.Windows.Forms.Label lRankList; + private System.Windows.Forms.CheckBox xIRCRegisteredNick; + private System.Windows.Forms.TextBox tIRCNickServMessage; + private System.Windows.Forms.Label lIRCNickServMessage; + private System.Windows.Forms.TextBox tIRCNickServ; + private System.Windows.Forms.Label lIRCNickServ; + private System.Windows.Forms.ToolTip toolTip; + private System.Windows.Forms.Label lIRCNoForwardingMessage; + private System.Windows.Forms.Label lIRCDelayUnits; + private System.Windows.Forms.NumericUpDown nMaxUndo; + private System.Windows.Forms.Label lMaxUndoUnits; + private System.Windows.Forms.CheckBox xMaxUndo; + private System.Windows.Forms.Label lDefaultBuildRank; + private System.Windows.Forms.ComboBox cDefaultBuildRank; + private System.Windows.Forms.Button bGreeting; + private System.Windows.Forms.ComboBox cIRCList; + private System.Windows.Forms.Label lIRCList; + private System.Windows.Forms.CheckBox xIRCListShowNonEnglish; + private System.Windows.Forms.CheckBox xAllowUnverifiedLAN; + private System.Windows.Forms.TabPage tabChat; + private System.Windows.Forms.GroupBox gAppearence; + private System.Windows.Forms.CheckBox xShowJoinedWorldMessages; + private System.Windows.Forms.CheckBox xRankColorsInWorldNames; + private System.Windows.Forms.Button bColorPM; + private System.Windows.Forms.Label lColorPM; + private System.Windows.Forms.Button bColorAnnouncement; + private System.Windows.Forms.Label lColorAnnouncement; + private System.Windows.Forms.Button bColorSay; + private System.Windows.Forms.Button bColorHelp; + private System.Windows.Forms.Button bColorSys; + private System.Windows.Forms.CheckBox xRankPrefixesInList; + private System.Windows.Forms.CheckBox xRankPrefixesInChat; + private System.Windows.Forms.CheckBox xRankColorsInChat; + private System.Windows.Forms.Label lColorSay; + private System.Windows.Forms.Label lColorHelp; + private System.Windows.Forms.Label lColorSys; + private ChatPreview chatPreview; + private System.Windows.Forms.GroupBox gChatColors; + private System.Windows.Forms.Label lColorWarning; + private System.Windows.Forms.Button bColorWarning; + private System.Windows.Forms.Label lColorMe; + private System.Windows.Forms.Button bColorMe; + private System.Windows.Forms.Label lLogFileOptionsDescription; + private System.Windows.Forms.Label lLogConsoleOptionsDescription; + private System.Windows.Forms.CheckBox xIRCBotAnnounceServerEvents; + private System.Windows.Forms.CheckBox xIRCUseColor; + private System.Windows.Forms.CheckBox xPaidPlayersOnly; + private System.Windows.Forms.Button bMapPath; + private System.Windows.Forms.CheckBox xMapPath; + private System.Windows.Forms.TextBox tMapPath; + private System.Windows.Forms.CheckBox xAllowSecurityCircumvention; + private System.Windows.Forms.Label lConsoleName; + private System.Windows.Forms.TextBox tConsoleName; + private System.Windows.Forms.CheckBox xShowConnectionMessages; + private System.Windows.Forms.GroupBox gHelpAndSupport; + private System.Windows.Forms.Button bReadme; + private System.Windows.Forms.Button bReportABug; + private System.Windows.Forms.Button bCredits; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Button bOpenWiki; + private System.Windows.Forms.ComboBox cUpdaterMode; + private System.Windows.Forms.Label lUpdater; + private System.Windows.Forms.GroupBox gUpdaterSettings; + private System.Windows.Forms.Button bShowAdvancedUpdaterSettings; + private System.Windows.Forms.Label lIPWarning; + private System.Windows.Forms.TextBox tIP; + private System.Windows.Forms.CheckBox xIP; + private System.Windows.Forms.NumericUpDown nMaxConnectionsPerIP; + private System.Windows.Forms.NumericUpDown nMaxPlayersPerWorld; + private System.Windows.Forms.Label lMaxPlayersPerWorld; + private System.Windows.Forms.CheckBox xAnnounceRankChangeReasons; + private System.Windows.Forms.CheckBox xRequireKickReason; + private System.Windows.Forms.GroupBox gPermissionLimits; + private System.Windows.Forms.FlowLayoutPanel permissionLimitBoxContainer; + private System.Windows.Forms.GroupBox gDataBackup; + private System.Windows.Forms.CheckBox xBackupDataOnStartup; + private System.Windows.Forms.GroupBox gBlockDB; + private System.Windows.Forms.ComboBox cBlockDBAutoEnableRank; + private System.Windows.Forms.CheckBox xBlockDBAutoEnable; + private System.Windows.Forms.CheckBox xBlockDBEnabled; + private System.Windows.Forms.CheckBox xWoMEnableEnvExtensions; + private System.Windows.Forms.CheckBox xHeartbeatToWoMDirect; + private System.Windows.Forms.NumericUpDown nCopyPasteSlots; + private System.Windows.Forms.Label lCopyPasteSlots; + private System.Windows.Forms.DataGridViewTextBoxColumn dgvcName; + private System.Windows.Forms.DataGridViewTextBoxColumn dgvcDescription; + private System.Windows.Forms.DataGridViewComboBoxColumn dgvcAccess; + private System.Windows.Forms.DataGridViewComboBoxColumn dgvcBuild; + private System.Windows.Forms.DataGridViewComboBoxColumn dgvcBackup; + private System.Windows.Forms.DataGridViewCheckBoxColumn dgvcHidden; + private System.Windows.Forms.DataGridViewCheckBoxColumn dgvcBlockDB; + private System.Windows.Forms.Label lFillLimitUnits; + private System.Windows.Forms.NumericUpDown nFillLimit; + private System.Windows.Forms.Label lFillLimit; + private System.Windows.Forms.NumericUpDown nMaxUndoStates; + private System.Windows.Forms.Label lMaxUndoStates; + private System.Windows.Forms.GroupBox gPerformance; + private System.Windows.Forms.Button bChangelog; + } +} \ No newline at end of file diff --git a/ConfigGUI/MainForm.ToolTips.cs b/ConfigGUI/MainForm.ToolTips.cs new file mode 100644 index 0000000..890a947 --- /dev/null +++ b/ConfigGUI/MainForm.ToolTips.cs @@ -0,0 +1,698 @@ +// Copyright 2009-2012 Matvei Stefarov + +namespace fCraft.ConfigGUI { + partial class MainForm { + + void FillToolTipsGeneral() { + + toolTip.SetToolTip( lServerName, ConfigKey.ServerName.GetDescription() ); + toolTip.SetToolTip( tServerName, ConfigKey.ServerName.GetDescription() ); + + toolTip.SetToolTip( lMOTD, ConfigKey.MOTD.GetDescription() ); + toolTip.SetToolTip( tMOTD, ConfigKey.MOTD.GetDescription() ); + + toolTip.SetToolTip( lMaxPlayers, ConfigKey.MaxPlayers.GetDescription() ); + toolTip.SetToolTip( nMaxPlayers, ConfigKey.MaxPlayers.GetDescription() ); + + toolTip.SetToolTip( lMaxPlayersPerWorld, ConfigKey.MaxPlayersPerWorld.GetDescription() ); + toolTip.SetToolTip( nMaxPlayersPerWorld, ConfigKey.MaxPlayersPerWorld.GetDescription() ); + + toolTip.SetToolTip( lDefaultRank, ConfigKey.DefaultRank.GetDescription() ); + toolTip.SetToolTip( cDefaultRank, ConfigKey.DefaultRank.GetDescription() ); + + toolTip.SetToolTip( lPublic, ConfigKey.IsPublic.GetDescription() ); + toolTip.SetToolTip( cPublic, ConfigKey.IsPublic.GetDescription() ); + + toolTip.SetToolTip( nPort, ConfigKey.Port.GetDescription() ); + toolTip.SetToolTip( lPort, ConfigKey.Port.GetDescription() ); + + toolTip.SetToolTip( bPortCheck, +@"Check if the selected port is connectible. +If port check fails, you may need to set up +port forwarding on your router." ); + + toolTip.SetToolTip( nUploadBandwidth, ConfigKey.UploadBandwidth.GetDescription() ); + toolTip.SetToolTip( lUploadBandwidth, ConfigKey.UploadBandwidth.GetDescription() ); + + toolTip.SetToolTip( bMeasure, +@"Test your connection's upload speed with speedtest.net +Note: to convert from megabits to kilobytes, multiply the +number by 128" ); + + toolTip.SetToolTip( bRules, +@"Edit the list of rules displayed by the ""/Rules"" command. +This list is stored in rules.txt, and can also be edited with any text editor. +If rules.txt is missing or empty, ""/Rules"" shows this message: +""Use common sense!""" ); + + const string tipAnnouncements = +@"Show a random announcement every once in a while. +Announcements are shown to all players, one line at a time, in random order."; + toolTip.SetToolTip( xAnnouncements, tipAnnouncements ); + + toolTip.SetToolTip( nAnnouncements, ConfigKey.AnnouncementInterval.GetDescription() ); + toolTip.SetToolTip( lAnnouncementsUnits, ConfigKey.AnnouncementInterval.GetDescription() ); + + toolTip.SetToolTip( bAnnouncements, +@"Edit the list of announcements (announcements.txt). +One line is shown at a time, in random order. +You can include any color codes in the announcements. +You can also edit announcements.txt with any text editor." ); + + toolTip.SetToolTip( bGreeting, +@"Edit a custom greeting that's shown to connecting players. +You can use any color codes, and these special variables: + {SERVER_NAME} = server name (as defined in config) + {RANK} = connecting player's rank" ); + + } + + + void FillToolTipsChat() { + + toolTip.SetToolTip( xRankColorsInChat, ConfigKey.RankColorsInChat.GetDescription() ); + + toolTip.SetToolTip( xRankColorsInWorldNames, ConfigKey.RankColorsInWorldNames.GetDescription() ); + + toolTip.SetToolTip( xRankPrefixesInChat, ConfigKey.RankPrefixesInChat.GetDescription() ); + + toolTip.SetToolTip( xRankPrefixesInList, ConfigKey.RankPrefixesInList.GetDescription() ); + + toolTip.SetToolTip( xShowConnectionMessages, ConfigKey.ShowConnectionMessages.GetDescription() ); + + toolTip.SetToolTip( xShowJoinedWorldMessages, ConfigKey.ShowJoinedWorldMessages.GetDescription() ); + + toolTip.SetToolTip( bColorSys, ConfigKey.SystemMessageColor.GetDescription() ); + toolTip.SetToolTip( lColorSys, ConfigKey.SystemMessageColor.GetDescription() ); + + toolTip.SetToolTip( bColorHelp, ConfigKey.HelpColor.GetDescription() ); + toolTip.SetToolTip( lColorHelp, ConfigKey.HelpColor.GetDescription() ); + + toolTip.SetToolTip( bColorSay, ConfigKey.SayColor.GetDescription() ); + toolTip.SetToolTip( lColorSay, ConfigKey.SayColor.GetDescription() ); + + toolTip.SetToolTip( bColorAnnouncement, ConfigKey.AnnouncementColor.GetDescription() ); + toolTip.SetToolTip( lColorAnnouncement, ConfigKey.AnnouncementColor.GetDescription() ); + + toolTip.SetToolTip( bColorPM, ConfigKey.PrivateMessageColor.GetDescription() ); + toolTip.SetToolTip( lColorPM, ConfigKey.PrivateMessageColor.GetDescription() ); + + toolTip.SetToolTip( bColorMe, ConfigKey.MeColor.GetDescription() ); + toolTip.SetToolTip( lColorMe, ConfigKey.MeColor.GetDescription() ); + + toolTip.SetToolTip( bColorWarning, ConfigKey.WarningColor.GetDescription() ); + toolTip.SetToolTip( lColorWarning, ConfigKey.WarningColor.GetDescription() ); + } + + + void FillToolTipsWorlds() { + toolTip.SetToolTip( bAddWorld, "Add a new world to the list." ); + toolTip.SetToolTip( bWorldEdit, "Edit or replace an existing world." ); + toolTip.SetToolTip( cMainWorld, "Main world is the first world that players see when they join the server." ); + toolTip.SetToolTip( bWorldDelete, "Delete a world from the list." ); + + toolTip.SetToolTip( lDefaultBuildRank, ConfigKey.DefaultBuildRank.GetDescription() ); + toolTip.SetToolTip( cDefaultBuildRank, ConfigKey.DefaultBuildRank.GetDescription() ); + + toolTip.SetToolTip( tMapPath, ConfigKey.MapPath.GetDescription() ); + toolTip.SetToolTip( xMapPath, ConfigKey.MapPath.GetDescription() ); + + toolTip.SetToolTip( xWoMEnableEnvExtensions, ConfigKey.WoMEnableEnvExtensions.GetDescription() ); + } + + + void FillToolTipsRanks() { + + toolTip.SetToolTip( xAllowSecurityCircumvention, +@"Allows players to manupulate whitelists/blacklists or rank requirements +in order to join restricted worlds, or to build in worlds/zones. Normally +players with ManageWorlds and ManageZones permissions are not allowed to do this. +Affected commands: + /WAccess + /WBuild + /WMain + /ZEdit" ); + + toolTip.SetToolTip( bAddRank, "Add a new rank to the list." ); + toolTip.SetToolTip( bDeleteRank, +@"Delete a rank from the list. You will be prompted to specify a replacement +rank - to be able to convert old references to the deleted rank." ); + toolTip.SetToolTip( bRaiseRank, +@"Raise a rank (and all players of the rank) on the hierarchy. +The hierarchy is used for all permission checks." ); + toolTip.SetToolTip( bLowerRank, +@"Lower a rank (and all players of the rank) on the hierarchy. +The hierarchy is used for all permission checks." ); + + const string tipRankName = +"Name of the rank - between 2 and 16 alphanumeric characters."; + toolTip.SetToolTip( lRankName, tipRankName ); + toolTip.SetToolTip( tRankName, tipRankName ); + + const string tipRankColor = +@"Color associated with this rank. +Rank colors may be applied to player and world names."; + toolTip.SetToolTip( lRankColor, tipRankColor ); + toolTip.SetToolTip( bColorRank, tipRankColor ); + + const string tipPrefix = +@"1-character prefix that may be shown above player names. +The option to show prefixes in chat is on ""General"" tab."; + toolTip.SetToolTip( lPrefix, tipPrefix ); + toolTip.SetToolTip( tPrefix, tipPrefix ); + + + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Kick], +@"Limit on who can be kicked by players of this rank. +By default, players can only kick players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Ban], +@"Limit on who can be banned by players of this rank. +By default, players can only ban players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Promote], +@"Limit on how much can players of this rank promote others. +By default, players can only promote up to the same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Demote], +@"Limit on whom players of this rank can demote. +By default, players can only demote players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Hide], +@"Limit on whom can players of this rank hide from. +By default, players can only hide from players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Freeze], +@"Limit on who can be frozen by players of this rank. +By default, players can only freeze players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Mute], +@"Limit on who can be muted by players of this rank. +By default, players can only mute players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Bring], +@"Limit on who can be brought (forcibly teleported) by players of this rank. +By default, players can only bring players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.Spectate], +@"Limit on who can be spectated by players of this rank. +By default, players can only bring players of same or lower rank." ); + + toolTip.SetToolTip( permissionLimitBoxes[Permission.UndoOthersActions], +@"Limit on whose actions players of this rank can undo. +By default, players can only undo actions of players of same or lower rank." ); + + + + toolTip.SetToolTip( xReserveSlot, +@"Allows players of this rank to join the server +even if it reached the maximum number of players." ); + + const string tipKickIdle = "Allows kicking players who have been inactive/AFK for some time."; + toolTip.SetToolTip( xKickIdle, tipKickIdle ); + toolTip.SetToolTip( nKickIdle, tipKickIdle ); + toolTip.SetToolTip( lKickIdleUnits, tipKickIdle ); + + toolTip.SetToolTip( xAntiGrief, +@"Antigrief is an automated system for kicking players who build +or delete at abnormally high rates. This helps stop certain kinds +of malicious software (like MCTunnel) from doing large-scale damage +to server maps. False positives can sometimes occur if server or +player connection is very laggy." ); + + toolTip.SetToolTip( nAntiGriefBlocks, +@"Maximum number of blocks that players of this rank are +allowed to build in a specified time period." ); + + toolTip.SetToolTip( nAntiGriefBlocks, +@"Minimum time interval that players of this rank are +expected to spent to build a specified number of blocks." ); + + const string tipDrawLimit = +@"Limit on the number of blocks that a player is +allowed to affect with drawing or copy/paste commands +at one time. If unchecked, there is no limit."; + toolTip.SetToolTip( xDrawLimit, tipDrawLimit ); + toolTip.SetToolTip( nDrawLimit, tipDrawLimit ); + toolTip.SetToolTip( lDrawLimitUnits, tipDrawLimit ); + + + + + vPermissions.Items[(int)Permission.Ban].ToolTipText = +@"Ability to ban/unban other players from the server. +Affected commands: + /Ban + /Unban"; + + vPermissions.Items[(int)Permission.BanAll].ToolTipText = +@"Ability to ban/unban a player account, his IP, and all other accounts that used the IP. +BanAll/UnbanAll commands can be used on players who keep evading bans. +Required permissions: Ban & BanIP +Affected commands: + /BanAll + /UnbanAll"; + + vPermissions.Items[(int)Permission.BanIP].ToolTipText = +@"Ability to ban/unban players by IP. +Required permission: Ban +Affected commands: + /BanIP + /UnbanIP"; + + vPermissions.Items[(int)Permission.Bring].ToolTipText = +@"Ability to bring/summon other players to your location. +This works a bit like reverse-teleport - other player is sent to you. +Affected commands: + /Bring + /BringAll"; + + vPermissions.Items[(int)Permission.BringAll].ToolTipText = +@"Ability to bring/summon many players at a time to your location. +Affected command: + /BringAll"; + + vPermissions.Items[(int)Permission.Build].ToolTipText = +@"Ability to place blocks on maps. This is a baseline permission +that can be overridden by world-specific and zone-specific permissions."; + + vPermissions.Items[(int)Permission.Chat].ToolTipText = +@"Ability to chat and PM players. Note that players without this +permission can still type in commands, receive PMs, and read chat. +Affected commands: + /Say + @ (pm) + @@ (rank chat)"; + + vPermissions.Items[(int)Permission.CopyAndPaste].ToolTipText = +@"Ability to copy (or cut) and paste blocks. The total number of +blocks that can be copied or pasted at a time is affected by +the draw limit. +Affected commands: + /Copy + /Cut + /Mirror + /Paste, /PasteNot + /Rotate"; + + vPermissions.Items[(int)Permission.Delete].ToolTipText = +@"Ability to delete or replace blocks on maps. This is a baseline permission +that can be overridden by world-specific and zone-specific permissions."; + + vPermissions.Items[(int)Permission.DeleteAdmincrete].ToolTipText = +@"Ability to delete admincrete (aka adminium) blocks. Even if someone +has this permission, it can be overridden by world-specific and +zone-specific permissions. +Required permission: Delete"; + + vPermissions.Items[(int)Permission.Demote].ToolTipText = +@"Ability to demote other players to a lower rank. +Affected commands: + /Rank + /MassRank"; + + vPermissions.Items[(int)Permission.Draw].ToolTipText = +@"Ability to use drawing tools (commands capable of affecting many blocks +at once). This permission can be overridden by world-specific and +zone-specific permissions. +Required permission: Build, Delete +Affected commands: + /Cuboid, /CuboidH, and /CuboidW + /Ellipsoid and /EllipsoidH + /Line + /Replace and /ReplaceNot + /Undo and /Redo"; + + vPermissions.Items[(int)Permission.DrawAdvanced].ToolTipText = +@"Ability to use advanced drawing tools, such as brushes. +Required permission: Build, Delete, Draw +Affected commands: + /Brush + /ReplaceBrush + /Restore + /Sphere and /SphereH + /Torus"; + + vPermissions.Items[(int)Permission.EditPlayerDB].ToolTipText = +@"Ability to edit the player database directly. This also adds the ability to +promote/demote players by name, even if they have not visited the server yet. +Also allows to manipulate players' records, and to promote/demote players in batches. +Affected commands: + /PruneDB + /AutoRankAll + /MassRank + /SetInfo + /InfoSwap + /DumpStats"; + + vPermissions.Items[(int)Permission.Freeze].ToolTipText = +@"Ability to freeze/unfreeze players. Frozen players cannot +move or build/delete. +Affected commands: + /Freeze + /Unfreeze"; + + vPermissions.Items[(int)Permission.Hide].ToolTipText = +@"Ability to appear hidden from other players. You can still chat, +build/delete blocks, use all commands, and join worlds while hidden. +Hidden players are completely invisible to other players. +Affected commands: + /Hide + /Unhide"; + + vPermissions.Items[(int)Permission.Import].ToolTipText = +@"Ability to import rank and ban lists from files. Useful if you +are switching from another server software. +Affected commands: + /Import"; + + vPermissions.Items[(int)Permission.Kick].ToolTipText = +@"Ability to kick players from the server. +Affected commands: + /Kick"; + + vPermissions.Items[(int)Permission.Lock].ToolTipText = +@"Ability to lock/unlock maps (locking puts a map into read-only state). +Affected commands: + /WLock + /WUnlock"; + + vPermissions.Items[(int)Permission.ManageWorlds].ToolTipText = +@"Ability to manipulate the world list: adding, renaming, and deleting worlds, +loading/saving maps, change per-world permissions, and using the map generator. +Affected commands: + /WLoad + /WUnload + /WRename + /WMain + /WAccess and /WBuild + /WFlush + /Gen"; + + + vPermissions.Items[(int)Permission.ManageBlockDB].ToolTipText = +@"Ability to enable/disable, clear, and configure BlockDB. +Affected command: + /BlockDB"; + + vPermissions.Items[(int)Permission.ManageZones].ToolTipText = +@"Ability to manipulate zones: adding, editing, renaming, and removing zones. +Affected commands: + /ZAdd + /ZEdit + /ZRemove + /ZRename"; + + vPermissions.Items[(int)Permission.Mute].ToolTipText = +@"Ability to temporarily mute players. Muted players cannot write chat or +send PMs, but they can still type in commands, receive PMs, and read chat. +Affected commands: + /Mute + /Unmute"; + + vPermissions.Items[(int)Permission.Patrol].ToolTipText = +@"Ability to patrol lower-ranked players. ""Patrolling"" means teleporting +to other players to check on them, usually while hidden. +Required permission: Teleport +Affected commands: + /Patrol + /SpecPatrol"; + + vPermissions.Items[(int)Permission.PlaceAdmincrete].ToolTipText = +@"Ability to place admincrete/adminium. This also affects draw commands. +Required permission: Build +Affected commands: + /Solid + /Bind"; + + vPermissions.Items[(int)Permission.PlaceGrass].ToolTipText = +@"Ability to place grass blocks. This also affects draw commands. +Required permission: Build +Affected commands: + /Grass + /Bind"; + + vPermissions.Items[(int)Permission.PlaceLava].ToolTipText = +@"Ability to place lava blocks. This also affects draw commands. +Required permission: Build +Affected commands: + /Lava + /Bind"; + + vPermissions.Items[(int)Permission.PlaceWater].ToolTipText = +@"Ability to place water blocks. This also affects draw commands. +Required permission: Build +Affected commands: + /Water + /Bind"; + + vPermissions.Items[(int)Permission.Promote].ToolTipText = +@"Ability to promote players to a higher rank. +Affected commands: + /Rank"; + + vPermissions.Items[(int)Permission.ReadStaffChat].ToolTipText = +@"Ability to read staff chat."; + + vPermissions.Items[(int)Permission.ReloadConfig].ToolTipText = +@"Ability to reload the configuration file without restarting. +Affected commands: + /Reload"; + + vPermissions.Items[(int)Permission.Say].ToolTipText = +@"Ability to use /Say command. +Required permission: Chat +Affected commands: + /Say"; + + vPermissions.Items[(int)Permission.SetSpawn].ToolTipText = +@"Ability to change the spawn point of a world or a player. +Affected commands: + /SetSpawn"; + + vPermissions.Items[(int)Permission.ShutdownServer].ToolTipText = +@"Ability to shut down or restart the server remotely. +Useful for servers that run on dedicated machines. +Affected commands: + /Shutdown + /Restart"; + + vPermissions.Items[(int)Permission.Spectate].ToolTipText = +@"Ability to spectate/follow other players in first-person view. +Affected commands: + /Spectate"; + + vPermissions.Items[(int)Permission.Teleport].ToolTipText = +@"Ability to teleport to other players. +Affected commands: + /TP"; + + vPermissions.Items[(int)Permission.UndoOthersActions].ToolTipText = +@"Ability to undo actions of other players, using the BlockDB. +Affected commands: + /UndoArea + /UndoPlayer"; + + vPermissions.Items[(int)Permission.UseColorCodes].ToolTipText = +@"Ability to use color codes in chat messages."; + + vPermissions.Items[(int)Permission.UseSpeedHack].ToolTipText = +@"Ability to move at a faster-than-normal rate (using hacks). +WARNING: Speedhack detection is often inaccurate, and may produce many +false positives - especially on laggy servers."; + + vPermissions.Items[(int)Permission.ViewOthersInfo].ToolTipText = +@"Ability to view extended information about other players. +Affected commands: + /Info + /BanInfo + /Where"; + + vPermissions.Items[(int)Permission.ViewPlayerIPs].ToolTipText = +@"Ability to view players' IP addresses. +Affected commands: + /Info + /BanInfo + /BanIP, /BanAll, /UnbanIP, /UnbanAll"; + } + + + void FillToolTipsSecurity() { + toolTip.SetToolTip( lVerifyNames, ConfigKey.VerifyNames.GetDescription() ); + toolTip.SetToolTip( cVerifyNames, ConfigKey.VerifyNames.GetDescription() ); + + toolTip.SetToolTip( xMaxConnectionsPerIP, ConfigKey.MaxConnectionsPerIP.GetDescription() ); + toolTip.SetToolTip( nMaxConnectionsPerIP, ConfigKey.MaxConnectionsPerIP.GetDescription() ); + + toolTip.SetToolTip( xAllowUnverifiedLAN, ConfigKey.AllowUnverifiedLAN.GetDescription() ); + + toolTip.SetToolTip( xRequireBanReason, ConfigKey.RequireBanReason.GetDescription() ); + toolTip.SetToolTip( xRequireKickReason, ConfigKey.RequireKickReason.GetDescription() ); + toolTip.SetToolTip( xRequireRankChangeReason, ConfigKey.RequireRankChangeReason.GetDescription() ); + + toolTip.SetToolTip( xAnnounceKickAndBanReasons, ConfigKey.AnnounceKickAndBanReasons.GetDescription() ); + toolTip.SetToolTip( xAnnounceRankChanges, ConfigKey.AnnounceRankChanges.GetDescription() ); + toolTip.SetToolTip( xAnnounceRankChangeReasons, ConfigKey.AnnounceRankChanges.GetDescription() ); + + + toolTip.SetToolTip( lPatrolledRank, ConfigKey.PatrolledRank.GetDescription() ); + toolTip.SetToolTip( cPatrolledRank, ConfigKey.PatrolledRank.GetDescription() ); + toolTip.SetToolTip( lPatrolledRankAndBelow, ConfigKey.PatrolledRank.GetDescription() ); + + toolTip.SetToolTip( nAntispamMessageCount, ConfigKey.AntispamMessageCount.GetDescription() ); + toolTip.SetToolTip( lAntispamMessageCount, ConfigKey.AntispamMessageCount.GetDescription() ); + toolTip.SetToolTip( nAntispamInterval, ConfigKey.AntispamInterval.GetDescription() ); + toolTip.SetToolTip( lAntispamInterval, ConfigKey.AntispamInterval.GetDescription() ); + + toolTip.SetToolTip( xAntispamKicks, "Kick players who repeatedly trigger antispam warnings." ); + toolTip.SetToolTip( nAntispamMaxWarnings, ConfigKey.AntispamMaxWarnings.GetDescription() ); + toolTip.SetToolTip( lAntispamMaxWarnings, ConfigKey.AntispamMaxWarnings.GetDescription() ); + + toolTip.SetToolTip( xPaidPlayersOnly, ConfigKey.PaidPlayersOnly.GetDescription() ); + + toolTip.SetToolTip( xBlockDBEnabled, ConfigKey.BlockDBEnabled.GetDescription() ); + toolTip.SetToolTip( xBlockDBAutoEnable, ConfigKey.BlockDBAutoEnable.GetDescription() ); + toolTip.SetToolTip( cBlockDBAutoEnableRank, ConfigKey.BlockDBAutoEnableRank.GetDescription() ); + } + + + void FillToolTipsSavingAndBackup() { + + toolTip.SetToolTip( xSaveInterval, ConfigKey.SaveInterval.GetDescription() ); + toolTip.SetToolTip( nSaveInterval, ConfigKey.SaveInterval.GetDescription() ); + toolTip.SetToolTip( lSaveIntervalUnits, ConfigKey.SaveInterval.GetDescription() ); + + toolTip.SetToolTip( xBackupOnStartup, ConfigKey.BackupOnStartup.GetDescription() ); + + toolTip.SetToolTip( xBackupOnJoin, ConfigKey.BackupOnJoin.GetDescription() ); + + toolTip.SetToolTip( xBackupInterval, ConfigKey.DefaultBackupInterval.GetDescription() ); + toolTip.SetToolTip( nBackupInterval, ConfigKey.DefaultBackupInterval.GetDescription() ); + toolTip.SetToolTip( lBackupIntervalUnits, ConfigKey.DefaultBackupInterval.GetDescription() ); + + toolTip.SetToolTip( xBackupOnlyWhenChanged, ConfigKey.DefaultBackupInterval.GetDescription() ); + + toolTip.SetToolTip( xMaxBackups, ConfigKey.MaxBackups.GetDescription() ); + toolTip.SetToolTip( nMaxBackups, ConfigKey.MaxBackups.GetDescription() ); + toolTip.SetToolTip( lMaxBackups, ConfigKey.MaxBackups.GetDescription() ); + + toolTip.SetToolTip( xMaxBackupSize, ConfigKey.MaxBackupSize.GetDescription() ); + toolTip.SetToolTip( nMaxBackupSize, ConfigKey.MaxBackupSize.GetDescription() ); + toolTip.SetToolTip( lMaxBackupSize, ConfigKey.MaxBackupSize.GetDescription() ); + } + + + void FillToolTipsLogging() { + toolTip.SetToolTip( lLogMode, ConfigKey.LogMode.GetDescription() ); + toolTip.SetToolTip( cLogMode, ConfigKey.LogMode.GetDescription() ); + + toolTip.SetToolTip( xLogLimit, ConfigKey.MaxLogs.GetDescription() ); + toolTip.SetToolTip( nLogLimit, ConfigKey.MaxLogs.GetDescription() ); + toolTip.SetToolTip( lLogLimitUnits, ConfigKey.MaxLogs.GetDescription() ); + + vLogFileOptions.Items[(int)LogType.ConsoleInput].ToolTipText = "Commands typed in from the server console."; + vLogFileOptions.Items[(int)LogType.ConsoleOutput].ToolTipText = +@"Things sent directly in response to console input, +e.g. output of commands called from console."; + vLogFileOptions.Items[(int)LogType.Debug].ToolTipText = "Technical information that may be useful to find bugs."; + vLogFileOptions.Items[(int)LogType.Error].ToolTipText = "Major errors and problems."; + vLogFileOptions.Items[(int)LogType.SeriousError].ToolTipText = "Errors that prevent server from starting or result in crashes."; + vLogFileOptions.Items[(int)LogType.GlobalChat].ToolTipText = "Normal chat messages written by players."; + vLogFileOptions.Items[(int)LogType.IRC].ToolTipText = +@"IRC-related status and error messages. +Does not include IRC chatter (see IRCChat)."; + vLogFileOptions.Items[(int)LogType.PrivateChat].ToolTipText = "PMs (Private Messages) exchanged between players (@player message)."; + vLogFileOptions.Items[(int)LogType.RankChat].ToolTipText = "Rank-wide messages (@@rank message)."; + vLogFileOptions.Items[(int)LogType.SuspiciousActivity].ToolTipText = "Suspicious activity - hack attempts, failed logins, unverified names."; + vLogFileOptions.Items[(int)LogType.SystemActivity].ToolTipText = "Status messages regarding normal system activity."; + vLogFileOptions.Items[(int)LogType.UserActivity].ToolTipText = "Status messages regarding players' actions."; + vLogFileOptions.Items[(int)LogType.UserCommand].ToolTipText = "Commands types in by players."; + vLogFileOptions.Items[(int)LogType.Warning].ToolTipText = "Minor, recoverable errors and problems."; + + for( int i = 0; i < vConsoleOptions.Items.Count; i++ ) { + vConsoleOptions.Items[i].ToolTipText = vLogFileOptions.Items[i].ToolTipText; + } + } + + + void FillToolTipsIRC() { + toolTip.SetToolTip( xIRCBotEnabled, ConfigKey.IRCBotEnabled.GetDescription() ); + + const string tipIRCList = +@"Choose one of these popular IRC networks, +or type in address/port manually below."; + toolTip.SetToolTip( lIRCList, tipIRCList ); + toolTip.SetToolTip( cIRCList, tipIRCList ); + + toolTip.SetToolTip( lIRCBotNick, ConfigKey.IRCBotNick.GetDescription() ); + toolTip.SetToolTip( tIRCBotNick, ConfigKey.IRCBotNick.GetDescription() ); + + toolTip.SetToolTip( lIRCBotNetwork, ConfigKey.IRCBotNetwork.GetDescription() ); + toolTip.SetToolTip( tIRCBotNetwork, ConfigKey.IRCBotNetwork.GetDescription() ); + + toolTip.SetToolTip( lIRCBotPort, ConfigKey.IRCBotPort.GetDescription() ); + toolTip.SetToolTip( nIRCBotPort, ConfigKey.IRCBotPort.GetDescription() ); + + toolTip.SetToolTip( lIRCDelay, ConfigKey.IRCDelay.GetDescription() ); + toolTip.SetToolTip( nIRCDelay, ConfigKey.IRCDelay.GetDescription() ); + toolTip.SetToolTip( lIRCDelayUnits, ConfigKey.IRCDelay.GetDescription() ); + + toolTip.SetToolTip( tIRCBotChannels, ConfigKey.IRCBotChannels.GetDescription() ); + + toolTip.SetToolTip( xIRCRegisteredNick, ConfigKey.IRCRegisteredNick.GetDescription() ); + + toolTip.SetToolTip( lIRCNickServ, ConfigKey.IRCNickServ.GetDescription() ); + toolTip.SetToolTip( tIRCNickServ, ConfigKey.IRCNickServ.GetDescription() ); + + toolTip.SetToolTip( lIRCNickServMessage, ConfigKey.IRCNickServMessage.GetDescription() ); + toolTip.SetToolTip( tIRCNickServMessage, ConfigKey.IRCNickServMessage.GetDescription() ); + + toolTip.SetToolTip( lColorIRC, ConfigKey.IRCMessageColor.GetDescription() ); + toolTip.SetToolTip( bColorIRC, ConfigKey.IRCMessageColor.GetDescription() ); + + toolTip.SetToolTip( xIRCBotForwardFromIRC, ConfigKey.IRCBotForwardFromIRC.GetDescription() ); + toolTip.SetToolTip( xIRCBotAnnounceIRCJoins, ConfigKey.IRCBotAnnounceIRCJoins.GetDescription() ); + + toolTip.SetToolTip( xIRCBotForwardFromServer, ConfigKey.IRCBotForwardFromServer.GetDescription() ); + toolTip.SetToolTip( xIRCBotAnnounceServerJoins, ConfigKey.IRCBotAnnounceServerJoins.GetDescription() ); + toolTip.SetToolTip( xIRCBotAnnounceServerEvents, ConfigKey.IRCBotAnnounceServerEvents.GetDescription() ); + + // TODO: IRCThreads + + toolTip.SetToolTip( xIRCUseColor, ConfigKey.IRCUseColor.GetDescription() ); + } + + + void FillToolTipsAdvanced() { + toolTip.SetToolTip( xRelayAllBlockUpdates, ConfigKey.RelayAllBlockUpdates.GetDescription() ); + + toolTip.SetToolTip( xNoPartialPositionUpdates, ConfigKey.NoPartialPositionUpdates.GetDescription() ); + + toolTip.SetToolTip( xLowLatencyMode, ConfigKey.LowLatencyMode.GetDescription() ); + + toolTip.SetToolTip( lProcessPriority, ConfigKey.ProcessPriority.GetDescription() ); + toolTip.SetToolTip( cProcessPriority, ConfigKey.ProcessPriority.GetDescription() ); + + toolTip.SetToolTip( lUpdater, ConfigKey.UpdaterMode.GetDescription() ); + toolTip.SetToolTip( cUpdaterMode, ConfigKey.UpdaterMode.GetDescription() ); + + toolTip.SetToolTip( lThrottling, ConfigKey.BlockUpdateThrottling.GetDescription() ); + toolTip.SetToolTip( nThrottling, ConfigKey.BlockUpdateThrottling.GetDescription() ); + toolTip.SetToolTip( lThrottlingUnits, ConfigKey.BlockUpdateThrottling.GetDescription() ); + + toolTip.SetToolTip( lTickInterval, ConfigKey.TickInterval.GetDescription() ); + toolTip.SetToolTip( nTickInterval, ConfigKey.TickInterval.GetDescription() ); + toolTip.SetToolTip( lTickIntervalUnits, ConfigKey.TickInterval.GetDescription() ); + + toolTip.SetToolTip( xMaxUndo, ConfigKey.MaxUndo.GetDescription() ); + toolTip.SetToolTip( nMaxUndo, ConfigKey.MaxUndo.GetDescription() ); + toolTip.SetToolTip( lMaxUndoUnits, ConfigKey.MaxUndo.GetDescription() ); + + toolTip.SetToolTip( xIP, ConfigKey.IP.GetDescription() ); + toolTip.SetToolTip( tIP, ConfigKey.IP.GetDescription() ); + + toolTip.SetToolTip( xHeartbeatToWoMDirect, ConfigKey.HeartbeatToWoMDirect.GetDescription() ); + } + } +} \ No newline at end of file diff --git a/ConfigGUI/MainForm.cs b/ConfigGUI/MainForm.cs new file mode 100644 index 0000000..91ee672 --- /dev/null +++ b/ConfigGUI/MainForm.cs @@ -0,0 +1,1551 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Windows.Forms; +using fCraft.GUI; +using JetBrains.Annotations; + + +namespace fCraft.ConfigGUI { + public sealed partial class MainForm : Form { + static MainForm instance; + readonly Font bold; + Rank selectedRank; + readonly UpdaterSettingsPopup updaterWindow = new UpdaterSettingsPopup(); + internal static readonly SortableBindingList Worlds = new SortableBindingList(); + + + #region Initialization + + public MainForm() { + instance = this; + InitializeComponent(); + dgvcBlockDB.TrueValue = YesNoAuto.Yes; + dgvcBlockDB.FalseValue = YesNoAuto.No; + dgvcBlockDB.IndeterminateValue = YesNoAuto.Auto; + bold = new Font( Font, FontStyle.Bold ); + Shown += Init; + Text = "fCraft Configuration (" + Updater.CurrentRelease.VersionString + ")"; + } + + + void Init( object sender, EventArgs e ) { + // fills Permission and LogType lists + FillEnumLists(); + + // create hidden boxes for permission limits + FillPermissionLimitBoxes(); + + // fill out all the tool tips + FillToolTipsGeneral(); + FillToolTipsChat(); + FillToolTipsWorlds(); + FillToolTipsRanks(); + FillToolTipsSecurity(); + FillToolTipsSavingAndBackup(); + FillToolTipsLogging(); + FillToolTipsIRC(); + FillToolTipsAdvanced(); + + FillIRCNetworkList( false ); + + // Initialize fCraft's args, paths, and logging backend. + Server.InitLibrary( Environment.GetCommandLineArgs() ); + + dgvWorlds.DataError += WorldListErrorHandler; + + LoadConfig(); + + // Redraw chat preview when re-entering the tab. + // This ensured that changes to rank colors/prefixes are applied. + tabChat.Enter += ( o, e2 ) => UpdateChatPreview(); + + bReadme.Enabled = File.Exists( ReadmeFileName ); + bChangelog.Enabled = File.Exists( ChangelogFileName ); + } + + + void FillEnumLists() { + foreach( Permission permission in Enum.GetValues( typeof( Permission ) ) ) { + ListViewItem item = new ListViewItem( permission.ToString() ) { Tag = permission }; + vPermissions.Items.Add( item ); + } + + foreach( LogType type in Enum.GetValues( typeof( LogType ) ) ) { + if( type == LogType.Trace ) continue; + ListViewItem item = new ListViewItem( type.ToString() ) { Tag = type }; + vLogFileOptions.Items.Add( item ); + vConsoleOptions.Items.Add( (ListViewItem)item.Clone() ); + } + } + + + void FillWorldList() { + cMainWorld.Items.Clear(); + foreach( WorldListEntry world in Worlds ) { + cMainWorld.Items.Add( world.Name ); + } + } + + #endregion + + + #region Input Handlers + + #region General + + private void bMeasure_Click( object sender, EventArgs e ) { + Process.Start( "http://www.speedtest.net/" ); + } + + private void bAnnouncements_Click( object sender, EventArgs e ) { + TextEditorPopup popup = new TextEditorPopup( Paths.AnnouncementsFileName, "" ); + popup.ShowDialog(); + } + + private void xAnnouncements_CheckedChanged( object sender, EventArgs e ) { + nAnnouncements.Enabled = xAnnouncements.Checked; + bAnnouncements.Enabled = xAnnouncements.Checked; + } + + private void bPortCheck_Click( object sender, EventArgs e ) { + bPortCheck.Text = "Checking"; + Enabled = false; + TcpListener listener = null; + + try { + listener = new TcpListener( IPAddress.Any, (int)nPort.Value ); + listener.Start(); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create( "http://www.utorrent.com/testport?plain=1&port=" + nPort.Value ); + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + + if( response.StatusCode == HttpStatusCode.OK ) { + using( Stream stream = response.GetResponseStream() ) { + if( stream != null ) { + StreamReader reader = new StreamReader( stream ); + string returnMessage = reader.ReadLine(); + if( returnMessage != null && returnMessage.StartsWith( "ok" ) ) { + MessageBox.Show( "Port " + nPort.Value + " is open!", "Port check success" ); + return; + } + } + } + } + MessageBox.Show( "Port " + nPort.Value + " is closed. You will need to set up forwarding.", "Port check failed" ); + + } catch { + MessageBox.Show( "Could not start listening on port " + nPort.Value + ". Another program may be using the port.", "Port check failed" ); + } finally { + if( listener != null ) { + listener.Stop(); + } + Enabled = true; + bPortCheck.Text = "Check"; + } + } + + private void tIP_Validating( object sender, CancelEventArgs e ) { + IPAddress IP; + if( Server.IsIP( tIP.Text ) && IPAddress.TryParse( tIP.Text, out IP ) ) { + tIP.ForeColor = SystemColors.ControlText; + } else { + tIP.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + } + } + + private void xIP_CheckedChanged( object sender, EventArgs e ) { + tIP.Enabled = xIP.Checked; + } + + private void bGreeting_Click( object sender, EventArgs e ) { + TextEditorPopup popup = new TextEditorPopup( Paths.GreetingFileName, +@"Welcome to {SERVER_NAME} +Your rank is {RANK}&S. Type &H/Help&S for help." ); + popup.ShowDialog(); + } + + private void bShowAdvancedUpdaterSettings_Click( object sender, EventArgs e ) { + updaterWindow.ShowDialog(); + cUpdaterMode.SelectedIndex = (int)updaterWindow.UpdaterMode; + } + + private void cUpdaterMode_SelectedIndexChanged( object sender, EventArgs e ) { + updaterWindow.UpdaterMode = (UpdaterMode)cUpdaterMode.SelectedIndex; + } + + private void bOpenWiki_Click( object sender, EventArgs e ) { + Process.Start( "http://www.fcraft.net/wiki/Main_Page" ); + } + + private void bReportABug_Click( object sender, EventArgs e ) { + Process.Start( "http://forum.fcraft.net/viewforum.php?f=5" ); + } + + private void nMaxPlayerPerWorld_Validating( object sender, CancelEventArgs e ) { + CheckMaxPlayersPerWorldValue(); + } + + private void nMaxPlayers_ValueChanged( object sender, EventArgs e ) { + CheckMaxPlayersPerWorldValue(); + } + + private void bCredits_Click( object sender, EventArgs e ) { + new AboutWindow().Show(); + } + + #endregion + + + #region Worlds + + void WorldListErrorHandler( object sender, DataGridViewDataErrorEventArgs e ) { + if( e.Exception is FormatException ) { + string columnName = dgvWorlds.Columns[e.ColumnIndex].HeaderText; + MessageBox.Show( e.Exception.Message, "Error editing " + columnName ); + } else { + MessageBox.Show( e.Exception.ToString(), "An error occured in the world list" ); + } + } + + private void bAddWorld_Click( object sender, EventArgs e ) { + AddWorldPopup popup = new AddWorldPopup( null ); + if( popup.ShowDialog() == DialogResult.OK ) { + Worlds.Add( popup.World ); + popup.World.LoadedBy = WorldListEntry.WorldInfoSignature; + popup.World.LoadedOn = DateTime.UtcNow; + } + if( cMainWorld.SelectedItem == null ) { + FillWorldList(); + if( cMainWorld.Items.Count > 0 ) { + cMainWorld.SelectedIndex = 0; + } + } else { + string mainWorldName = cMainWorld.SelectedItem.ToString(); + FillWorldList(); + cMainWorld.SelectedItem = mainWorldName; + } + } + + private void bWorldEdit_Click( object sender, EventArgs e ) { + AddWorldPopup popup = new AddWorldPopup( Worlds[dgvWorlds.SelectedRows[0].Index] ); + if( popup.ShowDialog() == DialogResult.OK ) { + string oldName = Worlds[dgvWorlds.SelectedRows[0].Index].Name; + Worlds[dgvWorlds.SelectedRows[0].Index] = popup.World; + HandleWorldRename( oldName, popup.World.Name ); + } + } + + private void dgvWorlds_Click( object sender, EventArgs e ) { + bool oneRowSelected = (dgvWorlds.SelectedRows.Count == 1); + bWorldDelete.Enabled = oneRowSelected; + bWorldEdit.Enabled = oneRowSelected; + } + + private void bWorldDel_Click( object sender, EventArgs e ) { + if( dgvWorlds.SelectedRows.Count > 0 ) { + WorldListEntry world = Worlds[dgvWorlds.SelectedRows[0].Index]; + + // prompt to delete map file, if it exists + if( File.Exists( world.FullFileName ) ) { + string promptMessage = String.Format( "Are you sure you want to delete world \"{0}\"?", world.Name ); + + if( MessageBox.Show( promptMessage, "Deleting a world", MessageBoxButtons.YesNo ) == DialogResult.No ) { + return; + } + + string fileDeleteWarning = "Do you want to delete the map file (" + world.FileName + ") as well?"; + if( MessageBox.Show( fileDeleteWarning, "Warning", MessageBoxButtons.YesNo ) == DialogResult.Yes ) { + try { + File.Delete( world.FullFileName ); + } catch( Exception ex ) { + MessageBox.Show( "You have to delete the file (" + world.FileName + ") manually. " + + "An error occured while trying to delete it automatically:" + Environment.NewLine + ex, + "Could not delete map file" ); + } + } + } + + Worlds.Remove( world ); + + if( cMainWorld.SelectedItem == null ) { + // deleting non-main world + FillWorldList(); + if( cMainWorld.Items.Count > 0 ) { + cMainWorld.SelectedIndex = 0; + } + + } else { + // deleting main world + string mainWorldName = cMainWorld.SelectedItem.ToString(); + FillWorldList(); + if( mainWorldName == world.Name ) { + MessageBox.Show( "Main world has been reset." ); + if( cMainWorld.Items.Count > 0 ) { + cMainWorld.SelectedIndex = 0; + } + } else { + cMainWorld.SelectedItem = mainWorldName; + } + } + } + } + + private void bMapPath_Click( object sender, EventArgs e ) { + FolderBrowserDialog dialog = new FolderBrowserDialog { + SelectedPath = tMapPath.Text, + Description = "Select a directory to save map files to" + }; + if( dialog.ShowDialog() == DialogResult.OK ) { + tMapPath.Text = dialog.SelectedPath; + } + } + + #endregion + + + #region Security + + private void cVerifyNames_SelectedIndexChanged( object sender, EventArgs e ) { + xAllowUnverifiedLAN.Enabled = (cVerifyNames.SelectedIndex != 0); + xAllowUnverifiedLAN.Checked = !xAllowUnverifiedLAN.Enabled; + } + + private void xMaxConnectionsPerIP_CheckedChanged( object sender, EventArgs e ) { + nMaxConnectionsPerIP.Enabled = xMaxConnectionsPerIP.Checked; + } + + #endregion + + + #region Logging + + private void vConsoleOptions_ItemChecked( object sender, ItemCheckedEventArgs e ) { + if( e.Item.Checked ) { + e.Item.Font = bold; + } else { + e.Item.Font = vConsoleOptions.Font; + } + } + + private void vLogFileOptions_ItemChecked( object sender, ItemCheckedEventArgs e ) { + if( e.Item.Checked ) { + e.Item.Font = bold; + } else { + e.Item.Font = vLogFileOptions.Font; + } + } + + private void xLogLimit_CheckedChanged( object sender, EventArgs e ) { + nLogLimit.Enabled = xLogLimit.Checked; + } + + #endregion + + + #region Saving & Backup + + private void xSaveAtInterval_CheckedChanged( object sender, EventArgs e ) { + nSaveInterval.Enabled = xSaveInterval.Checked; + } + + private void xBackupAtInterval_CheckedChanged( object sender, EventArgs e ) { + nBackupInterval.Enabled = xBackupInterval.Checked; + } + + private void xMaxBackups_CheckedChanged( object sender, EventArgs e ) { + nMaxBackups.Enabled = xMaxBackups.Checked; + } + + private void xMaxBackupSize_CheckedChanged( object sender, EventArgs e ) { + nMaxBackupSize.Enabled = xMaxBackupSize.Checked; + } + + #endregion + + + #region IRC + + private void xIRC_CheckedChanged( object sender, EventArgs e ) { + gIRCNetwork.Enabled = xIRCBotEnabled.Checked; + gIRCOptions.Enabled = xIRCBotEnabled.Checked; + lIRCList.Enabled = xIRCBotEnabled.Checked; + cIRCList.Enabled = xIRCBotEnabled.Checked; + xIRCListShowNonEnglish.Enabled = xIRCBotEnabled.Checked; + } + + + struct IRCNetwork { + const int DefaultIRCPort = 6667; + + public readonly string Name, Host; + public readonly int Port; + public readonly bool IsNonEnglish; + + public IRCNetwork( string name, string host ) + : this( name, host, DefaultIRCPort, false ) { + } + + public IRCNetwork( string name, string host, int port, bool isNonEnglish ) { + Name = name; + Host = host; + Port = port; + IsNonEnglish = isNonEnglish; + } + } + + static readonly IRCNetwork[] IRCNetworks = new[]{ + new IRCNetwork("FreeNode", "chat.freenode.net"), + new IRCNetwork("QuakeNet", "irc.quakenet.org"), + new IRCNetwork("IRCnet", "irc.belwue.de"), + new IRCNetwork("Undernet", "irc.undernet.org"), + new IRCNetwork("EFNet", "irc.servercentral.net"), + new IRCNetwork("Ustream", "c.ustream.tv"), + new IRCNetwork("WebChat", "irc.webchat.org"), + new IRCNetwork("DALnet", "irc.dal.net"), + new IRCNetwork("Rizon","irc.rizon.net"), + new IRCNetwork("IRC-Hispano [ES]", "irc.irc-hispano.org", 6667, true), + new IRCNetwork("FCirc", "irc.friend.td.nu"), + new IRCNetwork("GameSurge", "irc.gamesurge.net"), + new IRCNetwork("LinkNet", "irc.link-net.org"), + new IRCNetwork("OltreIrc [IT]", "irc.oltreirc.net", 6667,true), + new IRCNetwork("AllNetwork", "irc.allnetwork.org"), + new IRCNetwork("SwiftIRC", "irc.swiftirc.net"), + new IRCNetwork("OpenJoke", "irc.openjoke.org"), + new IRCNetwork("Abjects", "irc.abjects.net"), + new IRCNetwork("OFTC", "irc.oftc.net"), + new IRCNetwork("ChatZona [ES]", "irc.chatzona.org", 6667, true ), + new IRCNetwork("synIRC", "irc.synirc.net"), + new IRCNetwork("OnlineGamesNet", "irc.OnlineGamesNet.net"), + new IRCNetwork("DarkSin [IT]", "irc.darksin.it", 6667,true), + new IRCNetwork("RusNet", "irc.run.net", 6667,true), + new IRCNetwork("ExplosionIRC", "irc.explosionirc.net"), + new IRCNetwork("IrCQ-Net", "irc.icq.com"), + new IRCNetwork("IRCHighWay", "irc.irchighway.net"), + new IRCNetwork("EsperNet", "irc.esper.net"), + new IRCNetwork("euIRC", "irc.euirc.net"), + new IRCNetwork("P2P-NET", "irc.p2p-irc.net"), + new IRCNetwork("Mibbit", "irc.mibbit.com"), + new IRCNetwork("kiss0fdeath", "irc.kiss0fdeath.net"), + new IRCNetwork("P2P-NET.EU", "titan.ca.p2p-net.eu"), + new IRCNetwork("2ch [JP]", "irc.2ch.net", 6667,true), + new IRCNetwork("SorceryNet", "irc.sorcery.net", 9000,false), + new IRCNetwork("FurNet", "irc.furnet.org"), + new IRCNetwork("GIMPnet", "irc.gimp.org"), + new IRCNetwork("Coldfront", "irc.coldfront.net"), + new IRCNetwork("MindForge", "irc.mindforge.org"), + new IRCNetwork("Zurna.Net [TR]","irc.zurna.net",6667,true), + new IRCNetwork("7-indonesia [ID]", "irc.7-indonesia.org", 6667,true), + new IRCNetwork("EpiKnet", "irc.epiknet.org"), + new IRCNetwork("EnterTheGame", "irc.enterthegame.com"), + new IRCNetwork("DalNet(ru) [RU]", "irc.chatnet.ru", 6667,true), + new IRCNetwork("GalaxyNet", "irc.galaxynet.org"), + new IRCNetwork("Omerta", "irc.barafranca.com"), + new IRCNetwork("SlashNET", "irc.slashnet.org"), + new IRCNetwork("DarkMyst", "irc2.darkmyst.org"), + new IRCNetwork("iZ-smart.net", "irc.iZ-smart.net"), + new IRCNetwork("ItaLiaN-AmiCi [IT]", "irc.italian-amici.com", 6667,true), + new IRCNetwork("Aitvaras [LT]", "irc.data.lt", 6667,true), + new IRCNetwork("V-IRC [RU]", "irc.v-irc.ru", 6667,true), + new IRCNetwork("ByroeNet [ID]", "irc.byroe.net", 6667,true), + new IRCNetwork("Azzurra [IT]", "irc.azzurra.org", 6667,true), + new IRCNetwork("Europa-IRC.DE [DE]", "irc.europa-irc.de", 6667,true), + new IRCNetwork("ByNets [BY]", "irc.bynets.org", 6667,true), + new IRCNetwork("GRNet [GR]", "global.irc.gr", 6667,true), + new IRCNetwork("OceanIRC", "irc.oceanirc.net"), + new IRCNetwork("UniBG [BG]", "irc.ITDNet.net", 6667,true), + new IRCNetwork("KampungChat.Org [MY]", "irc.kampungchat.org", 6667,true), + new IRCNetwork("WeNet [RU]", "ircworld.ru", 6667,true), + new IRCNetwork("Stratics", "irc.stratics.com"), + new IRCNetwork("Mozilla", "irc.mozilla.org"), + new IRCNetwork("bondage.com", "irc.bondage.com"), + new IRCNetwork("ShakeIT [BG]", "irc.index.bg", 6667,true), + new IRCNetwork("NetGamers.Org", "firefly.no.eu.netgamers.org"), + new IRCNetwork("FroZyn", "irc.Frozyn.us"), + new IRCNetwork("PTnet", "irc.ptnet.org"), + new IRCNetwork("Recycled-IRC", "yare.recycled-irc.net"), + new IRCNetwork("Foonetic", "irc.foonetic.net"), + new IRCNetwork("AlphaIRC", "irc.alphairc.com"), + new IRCNetwork("KreyNet", "chat.be.krey.net"), + new IRCNetwork("GeekShed", "irc.geekshed.net"), + new IRCNetwork("VirtuaLife.com.br [BR]", "irc.virtualife.com.br", 6667,true), + new IRCNetwork("IRCGate.it [IT]", "marte.ircgate.it", 6667,true), + new IRCNetwork("Worldnet", "irc.worldnet.net"), + new IRCNetwork("PIK [BA]", "irc.krstarica.com", 6667,true), + new IRCNetwork("Friend4ever [IT]", "irc.friend4ever.it", 6667,true), + new IRCNetwork("AustNet", "irc.austnet.org"), + new IRCNetwork("GamesNET","irc.GamesNET.net") + }.OrderBy( network => network.Name ).ToArray(); + + private void cIRCList_SelectedIndexChanged( object sender, EventArgs e ) { + if( cIRCList.SelectedIndex < 0 ) return; + string selectedNetwork = (string)cIRCList.Items[cIRCList.SelectedIndex]; + IRCNetwork network = IRCNetworks.First( n => (n.Name == selectedNetwork) ); + tIRCBotNetwork.Text = network.Host; + nIRCBotPort.Value = network.Port; + } + + private void xIRCListShowNonEnglish_CheckedChanged( object sender, EventArgs e ) { + FillIRCNetworkList( xIRCListShowNonEnglish.Checked ); + } + + void FillIRCNetworkList( bool showNonEnglishNetworks ) { + cIRCList.Items.Clear(); + foreach( IRCNetwork network in IRCNetworks ) { + if( showNonEnglishNetworks || !network.IsNonEnglish ) { + cIRCList.Items.Add( network.Name ); + } + } + } + + private void xIRCRegisteredNick_CheckedChanged( object sender, EventArgs e ) { + tIRCNickServ.Enabled = xIRCRegisteredNick.Checked; + tIRCNickServMessage.Enabled = xIRCRegisteredNick.Checked; + } + + #endregion + + + #region Advanced + + private void nMaxUndo_ValueChanged( object sender, EventArgs e ) { + if( xMaxUndo.Checked ) { + decimal maxMemUsage = Math.Ceiling( nMaxUndoStates.Value * (nMaxUndo.Value * 8) / (1024 * 1024) ); + lMaxUndoUnits.Text = String.Format( "blocks each (up to {0} MB of RAM per player)", maxMemUsage ); + } else { + lMaxUndoUnits.Text = "blocks each"; + } + } + + private void xMaxUndo_CheckedChanged( object sender, EventArgs e ) { + nMaxUndo.Enabled = xMaxUndo.Checked; + lMaxUndoUnits.Enabled = xMaxUndo.Checked; + } + + private void xMapPath_CheckedChanged( object sender, EventArgs e ) { + tMapPath.Enabled = xMapPath.Checked; + bMapPath.Enabled = xMapPath.Checked; + } + + #endregion + + private void xAnnounceRankChanges_CheckedChanged( object sender, EventArgs e ) { + xAnnounceRankChangeReasons.Enabled = xAnnounceRankChanges.Checked; + } + + #endregion + + + #region Ranks + + BindingList rankNameList; + + void SelectRank( Rank rank ) { + if( rank == null ) { + if( vRanks.SelectedIndex != -1 ) { + vRanks.ClearSelected(); + return; + } + DisableRankOptions(); + return; + } + if( vRanks.SelectedIndex != rank.Index ) { + vRanks.SelectedIndex = rank.Index; + return; + } + selectedRank = rank; + tRankName.Text = rank.Name; + + ApplyColor( bColorRank, Color.ParseToIndex( rank.Color ) ); + + tPrefix.Text = rank.Prefix; + + foreach( var box in permissionLimitBoxes.Values ) { + box.SelectRank( rank ); + } + + xReserveSlot.Checked = rank.ReservedSlot; + xKickIdle.Checked = rank.IdleKickTimer > 0; + nKickIdle.Value = rank.IdleKickTimer; + nKickIdle.Enabled = xKickIdle.Checked; + xAntiGrief.Checked = (rank.AntiGriefBlocks > 0 && rank.AntiGriefSeconds > 0); + nAntiGriefBlocks.Value = rank.AntiGriefBlocks; + nAntiGriefBlocks.Enabled = xAntiGrief.Checked; + nAntiGriefSeconds.Value = rank.AntiGriefSeconds; + nAntiGriefSeconds.Enabled = xAntiGrief.Checked; + xDrawLimit.Checked = (rank.DrawLimit > 0); + nDrawLimit.Value = rank.DrawLimit; + nCopyPasteSlots.Value = rank.CopySlots; + nFillLimit.Value = rank.FillLimit; + xAllowSecurityCircumvention.Checked = rank.AllowSecurityCircumvention; + + foreach( ListViewItem item in vPermissions.Items ) { + item.Checked = rank.Permissions[item.Index]; + if( item.Checked ) { + item.Font = bold; + } else { + item.Font = vPermissions.Font; + } + } + + foreach( ListViewItem item in vPermissions.Items ) { + CheckPermissionConsistency( (Permission)item.Tag, item.Checked ); + } + + xDrawLimit.Enabled = rank.Can( Permission.Draw ) || rank.Can( Permission.CopyAndPaste ); + nDrawLimit.Enabled = xDrawLimit.Checked; + xAllowSecurityCircumvention.Enabled = rank.Can( Permission.ManageWorlds ) || rank.Can( Permission.ManageZones ); + + gRankOptions.Enabled = true; + lPermissions.Enabled = true; + vPermissions.Enabled = true; + + bDeleteRank.Enabled = true; + bRaiseRank.Enabled = (selectedRank != RankManager.HighestRank); + bLowerRank.Enabled = (selectedRank != RankManager.LowestRank); + } + + + void RebuildRankList() { + vRanks.Items.Clear(); + foreach( Rank rank in RankManager.Ranks ) { + vRanks.Items.Add( MainForm.ToComboBoxOption( rank ) ); + } + + FillRankList( cDefaultRank, "(lowest rank)" ); + cDefaultRank.SelectedIndex = RankManager.GetIndex( RankManager.DefaultRank ); + FillRankList( cDefaultBuildRank, "(default rank)" ); + cDefaultBuildRank.SelectedIndex = RankManager.GetIndex( RankManager.DefaultBuildRank ); + FillRankList( cPatrolledRank, "(default rank)" ); + cPatrolledRank.SelectedIndex = RankManager.GetIndex( RankManager.PatrolledRank ); + FillRankList( cBlockDBAutoEnableRank, "(default rank)" ); + cBlockDBAutoEnableRank.SelectedIndex = RankManager.GetIndex( RankManager.BlockDBAutoEnableRank ); + + if( selectedRank != null ) { + vRanks.SelectedIndex = selectedRank.Index; + } + SelectRank( selectedRank ); + + foreach( var box in permissionLimitBoxes.Values ) { + box.RebuildList(); + box.SelectRank( selectedRank ); + } + } + + + void DisableRankOptions() { + selectedRank = null; + bDeleteRank.Enabled = false; + bRaiseRank.Enabled = false; + bLowerRank.Enabled = false; + tRankName.Text = ""; + bColorRank.Text = ""; + tPrefix.Text = ""; + + foreach( var box in permissionLimitBoxes.Values ) { + box.SelectRank( null ); + } + + xReserveSlot.Checked = false; + xKickIdle.Checked = false; + nKickIdle.Value = 0; + xAntiGrief.Checked = false; + nAntiGriefBlocks.Value = 0; + xDrawLimit.Checked = false; + nDrawLimit.Value = 0; + xAllowSecurityCircumvention.Checked = false; + nCopyPasteSlots.Value = 0; + nFillLimit.Value = 32; + foreach( ListViewItem item in vPermissions.Items ) { + item.Checked = false; + item.Font = vPermissions.Font; + } + gRankOptions.Enabled = false; + lPermissions.Enabled = false; + vPermissions.Enabled = false; + } + + + static void FillRankList( [NotNull] ComboBox box, string firstItem ) { + if( box == null ) throw new ArgumentNullException( "box" ); + box.Items.Clear(); + box.Items.Add( firstItem ); + foreach( Rank rank in RankManager.Ranks ) { + box.Items.Add( MainForm.ToComboBoxOption(rank) ); + } + } + + + #region Ranks Input Handlers + + private void bAddRank_Click( object sender, EventArgs e ) { + int number = 1; + while( RankManager.RanksByName.ContainsKey( "rank" + number ) ) number++; + + Rank rank = new Rank( "rank" + number, RankManager.GenerateID() ); + + RankManager.AddRank( rank ); + selectedRank = null; + + RebuildRankList(); + SelectRank( rank ); + + rankNameList.Insert( rank.Index + 1, MainForm.ToComboBoxOption(rank) ); + } + + private void bDeleteRank_Click( object sender, EventArgs e ) { + if( vRanks.SelectedItem != null ) { + selectedRank = null; + int index = vRanks.SelectedIndex; + Rank deletedRank = RankManager.FindRank( index ); + if( deletedRank == null ) return; + + string messages = ""; + + // Ask for substitute rank + DeleteRankPopup popup = new DeleteRankPopup( deletedRank ); + if( popup.ShowDialog() != DialogResult.OK ) return; + + Rank replacementRank = popup.SubstituteRank; + + // Update default rank + if( RankManager.DefaultRank == deletedRank ) { + RankManager.DefaultRank = replacementRank; + messages += "DefaultRank has been changed to \"" + replacementRank.Name + "\"" + Environment.NewLine; + } + + // Update defaultbuild rank + if( RankManager.DefaultBuildRank == deletedRank ) { + RankManager.DefaultBuildRank = replacementRank; + messages += "DefaultBuildRank has been changed to \"" + replacementRank.Name + "\"" + Environment.NewLine; + } + + // Update patrolled rank + if( RankManager.PatrolledRank == deletedRank ) { + RankManager.PatrolledRank = replacementRank; + messages += "PatrolledRank has been changed to \"" + replacementRank.Name + "\"" + Environment.NewLine; + } + + // Update patrolled rank + if( RankManager.BlockDBAutoEnableRank == deletedRank ) { + RankManager.BlockDBAutoEnableRank = replacementRank; + messages += "BlockDBAutoEnableRank has been changed to \"" + replacementRank.Name + "\"" + Environment.NewLine; + } + + // Delete rank + if( RankManager.DeleteRank( deletedRank, replacementRank ) ) { + messages += "Some of the rank limits for kick, ban, promote, and/or demote have been reset." + Environment.NewLine; + } + vRanks.Items.RemoveAt( index ); + + // Update world permissions + string worldUpdates = ""; + foreach( WorldListEntry world in Worlds ) { + if( world.AccessPermission == MainForm.ToComboBoxOption(deletedRank) ) { + world.AccessPermission = MainForm.ToComboBoxOption(replacementRank); + worldUpdates += " - " + world.Name + ": access permission changed to " + replacementRank.Name + Environment.NewLine; + } + if( world.BuildPermission == MainForm.ToComboBoxOption(deletedRank) ) { + world.BuildPermission = MainForm.ToComboBoxOption(replacementRank); + worldUpdates += " - " + world.Name + ": build permission changed to " + replacementRank.Name + Environment.NewLine; + } + } + + rankNameList.RemoveAt( index + 1 ); + + if( worldUpdates.Length > 0 ) { + messages += "The following worlds were affected:" + Environment.NewLine + worldUpdates; + } + + if( messages.Length > 0 ) { + MessageBox.Show( messages, "Warning" ); + } + + RebuildRankList(); + + if( index < vRanks.Items.Count ) { + vRanks.SelectedIndex = index; + } + } + } + + + private void tPrefix_Validating( object sender, CancelEventArgs e ) { + if( selectedRank == null ) return; + tPrefix.Text = tPrefix.Text.Trim(); + if( tPrefix.Text.Length > 0 && !Rank.IsValidPrefix( tPrefix.Text ) ) { + MessageBox.Show( "Invalid prefix character!\n" + + "Prefixes may only contain characters that are allowed in chat (except space).", "Warning" ); + tPrefix.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + } else { + tPrefix.ForeColor = SystemColors.ControlText; + } + if( selectedRank.Prefix == tPrefix.Text ) return; + + string oldName = MainForm.ToComboBoxOption(selectedRank); + + // To avoid DataErrors in World tab's DataGridView while renaming a rank, + // the new name is first added to the list of options (without removing the old name) + rankNameList.Insert( selectedRank.Index + 1, String.Format( "{0,1}{1}", tPrefix.Text, selectedRank.Name ) ); + + selectedRank.Prefix = tPrefix.Text; + + // Remove the old name from the list of options + rankNameList.Remove( oldName ); + + Worlds.ResetBindings(); + RebuildRankList(); + } + + private void xReserveSlot_CheckedChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + selectedRank.ReservedSlot = xReserveSlot.Checked; + } + + private void nKickIdle_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null || !xKickIdle.Checked ) return; + selectedRank.IdleKickTimer = Convert.ToInt32( nKickIdle.Value ); + } + + private void nAntiGriefBlocks_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null || !xAntiGrief.Checked ) return; + selectedRank.AntiGriefBlocks = Convert.ToInt32( nAntiGriefBlocks.Value ); + } + + private void nAntiGriefSeconds_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null || !xAntiGrief.Checked ) return; + selectedRank.AntiGriefSeconds = Convert.ToInt32( nAntiGriefSeconds.Value ); + } + + private void nDrawLimit_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null || !xDrawLimit.Checked ) return; + selectedRank.DrawLimit = Convert.ToInt32( nDrawLimit.Value ); + double cubed = Math.Pow( Convert.ToDouble( nDrawLimit.Value ), 1 / 3d ); + lDrawLimitUnits.Text = String.Format( "blocks ({0:0}\u00B3)", cubed ); + } + + private void nCopyPasteSlots_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + selectedRank.CopySlots = Convert.ToInt32( nCopyPasteSlots.Value ); + } + + private void xAllowSecurityCircumvention_CheckedChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + selectedRank.AllowSecurityCircumvention = xAllowSecurityCircumvention.Checked; + } + + + private void xSpamChatKick_CheckedChanged( object sender, EventArgs e ) { + nAntispamMaxWarnings.Enabled = xAntispamKicks.Checked; + } + + private void vRanks_SelectedIndexChanged( object sender, EventArgs e ) { + if( vRanks.SelectedIndex != -1 ) { + SelectRank( RankManager.FindRank( vRanks.SelectedIndex ) ); + } else { + DisableRankOptions(); + } + } + + private void xKickIdle_CheckedChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + if( xKickIdle.Checked ) { + nKickIdle.Value = selectedRank.IdleKickTimer; + } else { + nKickIdle.Value = 0; + selectedRank.IdleKickTimer = 0; + } + nKickIdle.Enabled = xKickIdle.Checked; + } + + private void xAntiGrief_CheckedChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + if( xAntiGrief.Checked ) { + nAntiGriefBlocks.Value = selectedRank.AntiGriefBlocks; + nAntiGriefSeconds.Value = selectedRank.AntiGriefSeconds; + } else { + nAntiGriefBlocks.Value = 0; + selectedRank.AntiGriefBlocks = 0; + nAntiGriefSeconds.Value = 0; + selectedRank.AntiGriefSeconds = 0; + } + nAntiGriefBlocks.Enabled = xAntiGrief.Checked; + nAntiGriefSeconds.Enabled = xAntiGrief.Checked; + } + + private void xDrawLimit_CheckedChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + if( xDrawLimit.Checked ) { + nDrawLimit.Value = selectedRank.DrawLimit; + double cubed = Math.Pow( Convert.ToDouble( nDrawLimit.Value ), 1 / 3d ); + lDrawLimitUnits.Text = String.Format( "blocks ({0:0}\u00B3)", cubed ); + } else { + nDrawLimit.Value = 0; + selectedRank.DrawLimit = 0; + lDrawLimitUnits.Text = "blocks"; + } + nDrawLimit.Enabled = xDrawLimit.Checked; + } + + private void vPermissions_ItemChecked( object sender, ItemCheckedEventArgs e ) { + bool check = e.Item.Checked; + if( check ) { + e.Item.Font = bold; + } else { + e.Item.Font = vPermissions.Font; + } + if( selectedRank == null ) return; + + Permission permission = (Permission)e.Item.Tag; + CheckPermissionConsistency( permission, check ); + + selectedRank.Permissions[(int)e.Item.Tag] = e.Item.Checked; + } + + void CheckPermissionConsistency( Permission permission, bool check ) { + switch( permission ) { + case Permission.Chat: + if( !check ) { + vPermissions.Items[(int)Permission.Say].Checked = false; + vPermissions.Items[(int)Permission.Say].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.UseColorCodes].Checked = false; + vPermissions.Items[(int)Permission.UseColorCodes].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.Say].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.UseColorCodes].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.Say: + if( check ) vPermissions.Items[(int)Permission.Chat].Checked = true; + break; + + case Permission.UseColorCodes: + if( check ) vPermissions.Items[(int)Permission.Chat].Checked = true; + break; + + case Permission.Ban: + if( !check ) { + vPermissions.Items[(int)Permission.BanIP].Checked = false; + vPermissions.Items[(int)Permission.BanIP].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.BanAll].Checked = false; + vPermissions.Items[(int)Permission.BanAll].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.BanIP].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.BanAll].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.BanIP: + if( check ) { + vPermissions.Items[(int)Permission.Ban].Checked = true; + vPermissions.Items[(int)Permission.BanAll].ForeColor = SystemColors.ControlText; + } else { + vPermissions.Items[(int)Permission.BanAll].Checked = false; + vPermissions.Items[(int)Permission.BanAll].ForeColor = SystemColors.GrayText; + } + break; + + case Permission.BanAll: + if( check ) { + vPermissions.Items[(int)Permission.Ban].Checked = true; + vPermissions.Items[(int)Permission.BanIP].Checked = true; + } + break; + + case Permission.Draw: + xDrawLimit.Enabled = vPermissions.Items[(int)Permission.Draw].Checked || + vPermissions.Items[(int)Permission.CopyAndPaste].Checked; + if( check ) { + vPermissions.Items[(int)Permission.DrawAdvanced].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.CopyAndPaste].ForeColor = SystemColors.ControlText; + } else { + vPermissions.Items[(int)Permission.DrawAdvanced].Checked = false; + vPermissions.Items[(int)Permission.DrawAdvanced].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.CopyAndPaste].Checked = false; + vPermissions.Items[(int)Permission.CopyAndPaste].ForeColor = SystemColors.GrayText; + } + break; + + case Permission.DrawAdvanced: + lFillLimit.Enabled = check; + lFillLimitUnits.Enabled = check; + nFillLimit.Enabled = check; + break; + + case Permission.CopyAndPaste: + xDrawLimit.Enabled = vPermissions.Items[(int)Permission.Draw].Checked || + vPermissions.Items[(int)Permission.CopyAndPaste].Checked; + lCopyPasteSlots.Enabled = check; + nCopyPasteSlots.Enabled = check; + break; + + case Permission.ManageWorlds: + case Permission.ManageZones: + xAllowSecurityCircumvention.Enabled = vPermissions.Items[(int)Permission.ManageWorlds].Checked || + vPermissions.Items[(int)Permission.ManageZones].Checked; + break; + + case Permission.Teleport: + if( !check ) { + vPermissions.Items[(int)Permission.Patrol].Checked = false; + vPermissions.Items[(int)Permission.Patrol].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.Patrol].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.Patrol: + if( check ) vPermissions.Items[(int)Permission.Teleport].Checked = true; + break; + + case Permission.Delete: + if( !check ) { + vPermissions.Items[(int)Permission.DeleteAdmincrete].Checked = false; + vPermissions.Items[(int)Permission.DeleteAdmincrete].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.DeleteAdmincrete].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.DeleteAdmincrete: + if( check ) vPermissions.Items[(int)Permission.Delete].Checked = true; + break; + + case Permission.Build: + if( !check ) { + vPermissions.Items[(int)Permission.PlaceAdmincrete].Checked = false; + vPermissions.Items[(int)Permission.PlaceAdmincrete].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.PlaceGrass].Checked = false; + vPermissions.Items[(int)Permission.PlaceGrass].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.PlaceLava].Checked = false; + vPermissions.Items[(int)Permission.PlaceLava].ForeColor = SystemColors.GrayText; + vPermissions.Items[(int)Permission.PlaceWater].Checked = false; + vPermissions.Items[(int)Permission.PlaceWater].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.PlaceAdmincrete].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.PlaceGrass].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.PlaceLava].ForeColor = SystemColors.ControlText; + vPermissions.Items[(int)Permission.PlaceWater].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.PlaceAdmincrete: + case Permission.PlaceGrass: + case Permission.PlaceLava: + case Permission.PlaceWater: + if( check ) vPermissions.Items[(int)Permission.Build].Checked = true; + break; + + case Permission.Bring: + if( !check ) { + vPermissions.Items[(int)Permission.BringAll].Checked = false; + vPermissions.Items[(int)Permission.BringAll].ForeColor = SystemColors.GrayText; + } else { + vPermissions.Items[(int)Permission.BringAll].ForeColor = SystemColors.ControlText; + } + break; + + case Permission.BringAll: + if( check ) vPermissions.Items[(int)Permission.Bring].Checked = true; + break; + + } + + if( permissionLimitBoxes.ContainsKey( permission ) ) { + permissionLimitBoxes[permission].PermissionToggled( check ); + } + } + + private void tRankName_Validating( object sender, CancelEventArgs e ) { + tRankName.ForeColor = SystemColors.ControlText; + if( selectedRank == null ) return; + + string newName = tRankName.Text.Trim(); + + if( newName == selectedRank.Name ) { + return; + + } else if( newName.Length == 0 ) { + MessageBox.Show( "Rank name cannot be blank." ); + tRankName.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + + } else if( !Rank.IsValidRankName( newName ) ) { + MessageBox.Show( "Rank name can only contain letters, digits, and underscores." ); + tRankName.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + + } else if( !RankManager.CanRenameRank( selectedRank, newName ) ) { + MessageBox.Show( "There is already another rank named \"" + newName + "\".\n" + + "Duplicate rank names are not allowed." ); + tRankName.ForeColor = System.Drawing.Color.Red; + e.Cancel = true; + + } else { + string oldName = MainForm.ToComboBoxOption(selectedRank); + + // To avoid DataErrors in World tab's DataGridView while renaming a rank, + // the new name is first added to the list of options (without removing the old name) + rankNameList.Insert( selectedRank.Index + 1, String.Format( "{0,1}{1}", selectedRank.Prefix, newName ) ); + + RankManager.RenameRank( selectedRank, newName ); + + // Remove the old name from the list of options + rankNameList.Remove( oldName ); + + Worlds.ResetBindings(); + RebuildRankList(); + } + } + + + private void bRaiseRank_Click( object sender, EventArgs e ) { + if( selectedRank == null ) return; + if( RankManager.RaiseRank( selectedRank ) ) { + RebuildRankList(); + rankNameList.Insert( selectedRank.Index + 1, MainForm.ToComboBoxOption(selectedRank) ); + rankNameList.RemoveAt( selectedRank.Index + 3 ); + } + } + + private void bLowerRank_Click( object sender, EventArgs e ) { + if( selectedRank == null ) return; + if( RankManager.LowerRank( selectedRank ) ) { + RebuildRankList(); + rankNameList.Insert( selectedRank.Index + 2, MainForm.ToComboBoxOption(selectedRank) ); + rankNameList.RemoveAt( selectedRank.Index ); + } + } + + #endregion + + #endregion + + + #region Apply / Save / Cancel Buttons + + private void bApply_Click( object sender, EventArgs e ) { + SaveEverything(); + } + + private void bSave_Click( object sender, EventArgs e ) { + SaveEverything(); + Application.Exit(); + } + + void SaveEverything() { + using( LogRecorder applyLogger = new LogRecorder() ) { + SaveConfig(); + if( applyLogger.HasMessages ) { + MessageBox.Show( applyLogger.MessageString, "Some problems were encountered with the selected values." ); + return; + } + } + using( LogRecorder saveLogger = new LogRecorder() ) { + if( Config.Save() ) { + bApply.Enabled = false; + } + if( saveLogger.HasMessages ) { + MessageBox.Show( saveLogger.MessageString, "Some problems were encountered while saving." ); + } + } + } + + private void bCancel_Click( object sender, EventArgs e ) { + Application.Exit(); + } + + #endregion + + + #region Reset + + private void bResetAll_Click( object sender, EventArgs e ) { + if( MessageBox.Show( "Are you sure you want to reset everything to defaults?", "Warning", + MessageBoxButtons.OKCancel ) != DialogResult.OK ) return; + Config.LoadDefaults(); + Config.ResetRanks(); + Config.ResetLogOptions(); + + ApplyTabGeneral(); + ApplyTabChat(); + ApplyTabWorlds(); // also reloads world list + ApplyTabRanks(); + ApplyTabSecurity(); + ApplyTabSavingAndBackup(); + ApplyTabLogging(); + ApplyTabIRC(); + ApplyTabAdvanced(); + } + + private void bResetTab_Click( object sender, EventArgs e ) { + if( MessageBox.Show( "Are you sure you want to reset this tab to defaults?", "Warning", + MessageBoxButtons.OKCancel ) != DialogResult.OK ) return; + switch( tabs.SelectedIndex ) { + case 0:// General + Config.LoadDefaults( ConfigSection.General ); + ApplyTabGeneral(); + break; + case 1: // Chat + Config.LoadDefaults( ConfigSection.Chat ); + ApplyTabChat(); + break; + case 2:// Worlds + Config.LoadDefaults( ConfigSection.Worlds ); + ApplyTabWorlds(); // also reloads world list + break; + case 3:// Ranks + Config.ResetRanks(); + ApplyTabWorlds(); + ApplyTabRanks(); + RebuildRankList(); + break; + case 4:// Security + Config.LoadDefaults( ConfigSection.Security ); + ApplyTabSecurity(); + break; + case 5:// Saving and Backup + Config.LoadDefaults( ConfigSection.SavingAndBackup ); + ApplyTabSavingAndBackup(); + break; + case 6:// Logging + Config.LoadDefaults( ConfigSection.Logging ); + Config.ResetLogOptions(); + ApplyTabLogging(); + break; + case 7:// IRC + Config.LoadDefaults( ConfigSection.IRC ); + ApplyTabIRC(); + break; + case 8:// Advanced + Config.LoadDefaults( ConfigSection.Logging ); + ApplyTabAdvanced(); + break; + } + } + + #endregion + + + #region Utils + + #region Change Detection + + void SomethingChanged( object sender, EventArgs args ) { + bApply.Enabled = true; + } + + + void AddChangeHandler( Control c, EventHandler handler ) { + if( c is CheckBox ) { + ((CheckBox)c).CheckedChanged += handler; + } else if( c is ComboBox ) { + ((ComboBox)c).SelectedIndexChanged += handler; + } else if( c is ListView ) { + ((ListView)c).ItemChecked += (( o, e ) => handler( o, e )); + } else if( c is NumericUpDown ) { + ((NumericUpDown)c).ValueChanged += handler; + } else if( c is ListBox ) { + ((ListBox)c).SelectedIndexChanged += handler; + } else if( c is TextBoxBase ) { + c.TextChanged += handler; + } else if( c is ButtonBase ) { + if( c != bPortCheck && c != bMeasure ) { + c.Click += handler; + } + } + foreach( Control child in c.Controls ) { + AddChangeHandler( child, handler ); + } + } + + #endregion + + + #region Colors + int colorSys, colorSay, colorHelp, colorAnnouncement, colorPM, colorIRC, colorMe, colorWarning; + + void ApplyColor( Button button, int color ) { + button.Text = Color.GetName( color ); + button.BackColor = ColorPicker.ColorPairs[color].Background; + button.ForeColor = ColorPicker.ColorPairs[color].Foreground; + bApply.Enabled = true; + } + + private void bColorSys_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "System message color", colorSys ); + picker.ShowDialog(); + colorSys = picker.ColorIndex; + ApplyColor( bColorSys, colorSys ); + Color.Sys = Color.Parse( colorSys ); + } + + private void bColorHelp_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "Help message color", colorHelp ); + picker.ShowDialog(); + colorHelp = picker.ColorIndex; + ApplyColor( bColorHelp, colorHelp ); + Color.Help = Color.Parse( colorHelp ); + } + + private void bColorSay_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "/Say message color", colorSay ); + picker.ShowDialog(); + colorSay = picker.ColorIndex; + ApplyColor( bColorSay, colorSay ); + Color.Say = Color.Parse( colorSay ); + } + + private void bColorAnnouncement_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "Announcement color", colorAnnouncement ); + picker.ShowDialog(); + colorAnnouncement = picker.ColorIndex; + ApplyColor( bColorAnnouncement, colorAnnouncement ); + Color.Announcement = Color.Parse( colorAnnouncement ); + } + + private void bColorPM_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "Private / rank chat color", colorPM ); + picker.ShowDialog(); + colorPM = picker.ColorIndex; + ApplyColor( bColorPM, colorPM ); + Color.PM = Color.Parse( colorPM ); + } + + private void bColorWarning_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "Warning / Error message color", colorWarning ); + picker.ShowDialog(); + colorWarning = picker.ColorIndex; + ApplyColor( bColorWarning, colorWarning ); + Color.Warning = Color.Parse( colorWarning ); + } + + private void bColorMe_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "/Me command color", colorMe ); + picker.ShowDialog(); + colorMe = picker.ColorIndex; + ApplyColor( bColorMe, colorMe ); + Color.Me = Color.Parse( colorMe ); + } + + private void bColorIRC_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "IRC message color", colorIRC ); + picker.ShowDialog(); + colorIRC = picker.ColorIndex; + ApplyColor( bColorIRC, colorIRC ); + Color.IRC = Color.Parse( colorIRC ); + } + + private void bColorRank_Click( object sender, EventArgs e ) { + ColorPicker picker = new ColorPicker( "Rank color for \"" + selectedRank.Name + "\"", Color.ParseToIndex( selectedRank.Color ) ); + picker.ShowDialog(); + ApplyColor( bColorRank, picker.ColorIndex ); + selectedRank.Color = Color.Parse( picker.ColorIndex ); + } + + + void HandleTabChatChange( object sender, EventArgs args ) { + UpdateChatPreview(); + } + + void UpdateChatPreview() { + List lines = new List(); + if( xShowConnectionMessages.Checked ) { + lines.Add( String.Format( "&SPlayer {0}{1}Notch&S connected, joined {2}{3}main", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "", + xRankColorsInWorldNames.Checked ? RankManager.LowestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.LowestRank.Prefix : "" ) ); + } + lines.Add( "&R<*- This is a random announcement -*>" ); + lines.Add( "&YSomeone wrote this message with /Say" ); + lines.Add( String.Format( "{0}{1}Notch&F: This is a normal chat message", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "" ) ); + lines.Add( "&Pfrom Notch: This is a private message / whisper" ); + lines.Add( "&M*Notch is using /Me to write this" ); + if( xShowJoinedWorldMessages.Checked ) { + Rank midRank = RankManager.LowestRank; + if( RankManager.LowestRank.NextRankUp != null ) { + midRank = RankManager.LowestRank.NextRankUp; + } + + lines.Add( String.Format( "&SPlayer {0}{1}Notch&S joined {2}{3}SomeOtherMap", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "", + xRankColorsInWorldNames.Checked ? midRank.Color : "", + xRankPrefixesInChat.Checked ? midRank.Prefix : "" ) ); + } + lines.Add( "&SUnknown command \"kikc\", see &H/Commands" ); + if( xAnnounceKickAndBanReasons.Checked ) { + lines.Add( String.Format( "&W{0}{1}Notch&W was kicked by {0}{1}gamer1&W: Reason goes here", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "" ) ); + } else { + lines.Add( String.Format( "&W{0}{1}Notch&W was kicked by {0}{1}gamer1", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "" ) ); + } + + if( xShowConnectionMessages.Checked ) { + lines.Add( String.Format( "&S{0}{1}Notch&S left the server.", + xRankColorsInChat.Checked ? RankManager.HighestRank.Color : "", + xRankPrefixesInChat.Checked ? RankManager.HighestRank.Prefix : "" ) ); + } + + chatPreview.SetText( lines.ToArray() ); + } + + #endregion + + + private void bRules_Click( object sender, EventArgs e ) { + TextEditorPopup popup = new TextEditorPopup( Paths.RulesFileName, "Use common sense!" ); + popup.ShowDialog(); + } + + + internal static bool IsWorldNameTaken( string name ) { + return Worlds.Any( world => world.Name.Equals( name, StringComparison.OrdinalIgnoreCase ) ); + } + + + void CheckMaxPlayersPerWorldValue() { + if( nMaxPlayersPerWorld.Value > nMaxPlayers.Value ) { + nMaxPlayersPerWorld.Value = nMaxPlayers.Value; + } + nMaxPlayersPerWorld.Maximum = Math.Min( 128, nMaxPlayers.Value ); + } + + + internal static void HandleWorldRename( string from, string to ) { + if( instance.cMainWorld.Items.Count == 0 ) return; + if( instance.cMainWorld.SelectedItem == null ) { + instance.cMainWorld.SelectedIndex = 0; + } else { + string mainWorldName = instance.cMainWorld.SelectedItem.ToString(); + instance.FillWorldList(); + if( mainWorldName == from ) { + instance.cMainWorld.SelectedItem = to; + } else { + instance.cMainWorld.SelectedItem = mainWorldName; + } + } + } + + #endregion + + + private void ConfigUI_FormClosing( object sender, FormClosingEventArgs e ) { + if( !bApply.Enabled ) return; + switch( MessageBox.Show( "Would you like to save the changes before exiting?", "Warning", MessageBoxButtons.YesNoCancel ) ) { + case DialogResult.Yes: + SaveEverything(); + return; + + case DialogResult.Cancel: + e.Cancel = true; + return; + } + } + + + readonly Dictionary permissionLimitBoxes = new Dictionary(); + + const string DefaultPermissionLimitString = "(own rank)"; + void FillPermissionLimitBoxes() { + + permissionLimitBoxes[Permission.Kick] = new PermissionLimitBox( "Kick limit", Permission.Kick, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Ban] = new PermissionLimitBox( "Ban limit", Permission.Ban, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Promote] = new PermissionLimitBox( "Promote limit", Permission.Promote, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Demote] = new PermissionLimitBox( "Demote limit", Permission.Demote, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Hide] = new PermissionLimitBox( "Can hide from", Permission.Hide, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Freeze] = new PermissionLimitBox( "Freeze limit", Permission.Freeze, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Mute] = new PermissionLimitBox( "Mute limit", Permission.Mute, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Bring] = new PermissionLimitBox( "Bring limit", Permission.Bring, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.Spectate] = new PermissionLimitBox( "Spectate limit", Permission.Spectate, DefaultPermissionLimitString ); + permissionLimitBoxes[Permission.UndoOthersActions] = new PermissionLimitBox( "Undo limit", Permission.UndoOthersActions, DefaultPermissionLimitString ); + + foreach( var box in permissionLimitBoxes.Values ) { + permissionLimitBoxContainer.Controls.Add( box ); + } + } + + + private void cDefaultRank_SelectedIndexChanged( object sender, EventArgs e ) { + RankManager.DefaultRank = RankManager.FindRank( cDefaultRank.SelectedIndex - 1 ); + } + + private void cDefaultBuildRank_SelectedIndexChanged( object sender, EventArgs e ) { + RankManager.DefaultBuildRank = RankManager.FindRank( cDefaultBuildRank.SelectedIndex - 1 ); + } + + private void cPatrolledRank_SelectedIndexChanged( object sender, EventArgs e ) { + RankManager.PatrolledRank = RankManager.FindRank( cPatrolledRank.SelectedIndex - 1 ); + } + + private void cBlockDBAutoEnableRank_SelectedIndexChanged( object sender, EventArgs e ) { + RankManager.BlockDBAutoEnableRank = RankManager.FindRank( cBlockDBAutoEnableRank.SelectedIndex - 1 ); + } + + private void xBlockDBEnabled_CheckedChanged( object sender, EventArgs e ) { + xBlockDBAutoEnable.Enabled = xBlockDBEnabled.Checked; + cBlockDBAutoEnableRank.Enabled = xBlockDBEnabled.Checked && xBlockDBAutoEnable.Checked; + } + + private void xBlockDBAutoEnable_CheckedChanged( object sender, EventArgs e ) { + cBlockDBAutoEnableRank.Enabled = xBlockDBEnabled.Checked && xBlockDBAutoEnable.Checked; + } + + private void nFillLimit_ValueChanged( object sender, EventArgs e ) { + if( selectedRank == null ) return; + selectedRank.FillLimit = Convert.ToInt32( nFillLimit.Value ); + } + + const string ReadmeFileName = "README.txt"; + private void bReadme_Click( object sender, EventArgs e ) { + try { + if( File.Exists( ReadmeFileName ) ) { + Process.Start( ReadmeFileName ); + } + } catch( Exception ) { } + } + + const string ChangelogFileName = "CHANGELOG.txt"; + private void bChangelog_Click( object sender, EventArgs e ) { + try { + if( File.Exists( ChangelogFileName ) ) { + Process.Start( ChangelogFileName ); + } + } catch( Exception ) { } + } + + public static bool usePrefixes = false; + + + public static string ToComboBoxOption( Rank rank) { + if( usePrefixes ) { + return String.Format( "{0,1}{1}", rank.Prefix, rank.Name ); + } else { + return rank.Name; + } + } + + private void xRankPrefixesInChat_CheckedChanged( object sender, EventArgs e ) { + usePrefixes = xRankPrefixesInChat.Checked; + tPrefix.Enabled = usePrefixes; + lPrefix.Enabled = usePrefixes; + RebuildRankList(); + } + } +} \ No newline at end of file diff --git a/ConfigGUI/MainForm.resx b/ConfigGUI/MainForm.resx new file mode 100644 index 0000000..f0e835b --- /dev/null +++ b/ConfigGUI/MainForm.resx @@ -0,0 +1,1187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + Crash reports are created when serious unexpected errors occur. Being able to receive crash reports helps +identify bugs and improve fCraft! The report consists of the error information, OS and runtime versions, +a copy of config.xml, and last 25 lines of the log file. Reports are confidential and are never displayed publicly. + + + 17, 6 + + + 28 + + + + + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////A+ + Afnv3//7////99///+///z/f/++fn///zz///+Z//+///////P//9/9///uHv/wAP8////////f////7 + //////////////v///////n/3H///+H8+///fgf/79///5/////+////8b///+/////9/////////ygA + AAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv8/QEAAAAAAAAAAMzQ + 5gXO1OYSzdPlDQAAAAAAAAAAzNXtAgAAAAAAAAAAAAAAAAAAAADM0+YFzNPmBs3T5gbN0+YGzdPmBs3T + 5gbN0+YGzdPlBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKS1 + 4yMAHaOyABqe4gAbn+4AGZnpABaN0QAWiGoAAAAAAB2mAQAmwgMAAAAAACrAeQAboeYAGZnjABia5AAY + muQAGJvkABea5AAYm+EAF5jkABePjwAAAAAAAAAAAAAAAAAkwwIAAAAAAB6pnAAViZ8AGJUJ+fv9AgAA + AACmt+gkACa54wU0xv8IPM//GEnT/xhI0v8LPMn/ASyr/wMolJEAAAAAAjG5BAAAAAAqV9iaCj3R/wU5 + 0P8XR9P/HkzS/xtK0v8bStL/HEvS/xdH0v8DM73FAAAAAAAAAAADNckDAAAAAAM0w3wDNcf/AjK6/wIu + rTz9/f4CAAAAAAAuyrICNs//Cj/U8lt+4IF5leY6gpznZUlw3tcRRNT+ACus/wAol1EAAAAAAAAAACpY + 134MQdH8AjTG+RxM0YY8Z95kRGzcZ0Vt3GVFbdxlQWrcZh5P1yoAAAAAADTHBAAAAAAAMr9WADTG/QM5 + 0P8HPtOvBDrPBwAAAACUq+suADLQ9QMxyvw3YdR2AAAAAAAAAAAAAAAAW3/gGita2eUGPMr9ACufxgAq + pgUAAAAAN2TbUB1Q2P4AMLn9ACmgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU90gIAAAAAADXEPAA1 + xvcBOdD/BT3S0Ak/0g4AAAAAAAAAAERu3mQPRtb/ACnE/HmU2xz+/v4D9/j8BPz8/gUAAAAALFvahxFH + 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 + AAAAAAAAAAAAAAB6eXnPzs53d3cAAAATExOqqanb2dnq6elYWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCgouLi4FBQUAAAABAQFubm7l4+Pd3d00 + NDQAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAABBQUEAAAASExM7OzsyMjIFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA/Af8AB/7AAD7Df/AH/UAAPb2////6wAA//v9f//X + AAD7/X///78AAP///r//bwAA5/7/3//fAAD//v9v/r8AAP///7f9fwAA////2/r/AAD//3/t9/8AAP// + v/b9/wAA//9/+/v/AAD//9//X/8AAP//f/2//wAA///f/9//AAD//7/76/8AAP//7+b1/wAA/4DQHft/ + AAD/gHgD/r8AAP//j///PwAA///f////AAD///////8AAP//3////wAA///3////AAD//+////8AAP// + 9////wAA///v////AAD///v///8AAP5/6/8//wAA//P/////AADvj/X+//8AANH//v+//wAA/h/7ff// + AAD17/2Df/8AAP/9fgD//wAA2/T/////AAC7//////8AAM3r/////wAA9t//////AAD/J/////8AAPz9 + /////wAA8///////AAD/n/////8AAP+//////wAA////////AAAoAAAAMAAAAGAAAAABACAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8CAAAAAAAAAAAAAAAA8vP5AvLz + +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 + AAAAAAAAQUFBAgAAAAASExMqOzs7aTIyMlwFBQUVAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + AAD///////8AAPwH/AAf+wAA+w3/wB/1AAD29v///+sAAP/7/X//1wAA+/1///+/AAD///6//28AAOf+ + /9//3wAA//7/b/6/AAD///+3/X8AAP///9v6/wAA//9/7ff/AAD//7/2/f8AAP//f/v7/wAA///f/1// + AAD//3/9v/8AAP//3//f/wAA//+/++v/AAD//+/m9f8AAP+A0B37fwAA/4B4A/6/AAD//4///z8AAP// + 3////wAA////////AAD//9////8AAP//9////wAA///v////AAD///f///8AAP//7////wAA///7//// + AAD+f+v/P/8AAP/z/////wAA74/1/v//AADR//7/v/8AAP4f+33//wAA9e/9g3//AAD//X4A//8AANv0 + /////wAAu///////AADN6/////8AAPbf/////wAA/yf/////AAD8/f////8AAPP//////wAA/5////// + AAD/v/////8AAP///////wAAKAAAAEAAAACAAAAAAQAEAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAA + AADQyMgAAFDoAAA4yAAAMKAAQEhYADBo4ACYmJgA+Pj4AABg+AAYWOAAeHh4APDw8AA4gPAAKCgoAAhI + yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABERERVVAAAAAAABERERERERERFVAAA + AAAABEQAAAAABERERERFVAAAAAAzREREREREREREAAAAAAA0RUAAAABEQzMzNEREQAAAAP8zMzMzMzMz + M0QAAAAAAzNERAAAAzMzP///M0REAAAA//MzP///////MwAAAAADMzNEAAAzMz/2Zmb/NERAAAD/8z/2 + ZmZmZmbzAAAAADMzMzMAAzMz//ZmZmbzREAAAP/zM/ZmZmZmZm8AAAADMzM/MAADMzM/AAAAZm80RAAA + ZvM0QAAAAAAAAAAAADMzM/MAAAMzMzAAAAAGbzNEAAAGbzREAAAAAAAAAAADMzM/MAAA//MzMAAAAAZm + 80RAAAZvM0QAAAAAAAAAADMzMzMAAACq8zMwAAAAAGbzNEAABmrzREAAAAAAAAAAMzMzMwAAAKaqMwAA + AAAAZqM0QAAAZq80RAAAAAAAAAMzM//wAAAAZmajAAAAAABmrzRAAABmajNEQAAAAAAAMzM//wAAAAAG + ZmAAAAAAAAZiNEAAAAZmozREAAAAAAMzM/LwAAAAAAAAAAAAAAAABmozRAAAAGZqM0RAAAAAMzM/8gAA + AAAAAAAAAAAAAAAGaiNEAAAABmajNEQAAAMzMiIgAAAAAAAAAAAAAAAAAAqqI0QAAAAA1mojREAAAzMi + IiAAAAAAAAAAAAAAAAAABmojRAAAAAANZqI0RAAz8iIiAAAAAAAAAAAAAAAAAAAAaiNEAAAAAADWavNE + Az8iIiAAAAAAAAAAAAAAAAAAAABmrzRAAAAAAAZqI0Qz8iIiAAAAAAAAAAAAAAAAAAAAAGaiNEAAAAAA + AGaiNDMiIiAAAAAAAAAAAAAAAAAAAAAAaqLzQAAAAAAAZqLzPyLzAAAAAAAAAAAAAAAAAAAAAABmovRA + AAAAAACqoiKqIjRAAAAAAAAAAAAAAAAAAAAAAAai9EAAAAAAACIiImaq9EQAAAAAAAAAAAAAAAAAAAAA + CqIjQAAAAABD8iIirdYvREAAAAAAAAAAAAAAA0RERERPIi9EREREREPyIiIg3Wr0REAAAAAAAAAAAAAi + 9ERERD8iL/REREQ//yIiIgAN1qI0RAAAAAAAAAAAAKov////IiIiIv8iIiIiIqIiAADdai9EQAAAAAAA + AAAApqqqqqqqIiIiKqqqqqqqqqAAAA3dYvREAAAAAAAAAABt1mZmZmZqIiqmZmZmZmaqAAAAAN3WIv/w + AAAAAAAAAA3d3d3d3daiIqbd3d3W1gAAAAAADd1qIgAAAAAAAAAAAAAAAAAA1qL/IAAAAAAAAAAAAAAA + DdagAAAAAAAAAAAAAAAAAABqov/wAAAAAAAAAAAAAAAA1gAAAAAAAAAAAAAAAAAAAGaiL0AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAADWIvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANai9AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZqL/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqoi + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGaiL/AAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAA + AADaIvQAAAAAAAAAAAAAAAAAAAAAAAAAAADrGwAAAAAAAN2i//AAAAAAAP9EQAAAAAAAAAAAAAAAAFiI + UAAOAAAA3ZIv8AAAAAACL/9AAAAAAAAAAAAAAAAAWIiwDlGwAADdmS9AAAAAAAmS//AAAAAAAAAAAAAO + 4AC4iMXnERsAAA3ZL08AAAAACZkiIAAAAAAAAAAAAFEb7hjBERERGwAADdmS9AAAAACZmSIgAAAAAAAA + AAAOcRjIiMERFxEVAAAN3ZL0QAAAACmZIiAAAAAAAAAAAA5xHIiIwREREbAAAADdmS9EAAD/IikiAAAA + AAAAAAAAAFHMHIjBERzBsAAAAN3ZkvRET/8imSIAAAAAAAAAAAAABREciBu3HIwXtVAADd2ZL///8imZ + IAAAAAAAAAAAAAAOERjHAA58iMyIEAAA3dmZIiKZmZmQAAAAAAAAAAAADufBGB4AALyIzMjAAAAN3dmZ + mZmZmQAAAAAAAAAAAADnyMEccAAAXIjMiBAAAADd3d3d3dmQAAAAAAAAAAAAAHzIwcgQAAC8iMgbUAAA + AADd3d3d0AAAAAAAAAAAAAAAfMiByIsADhiIhwAAAAAAAAAAAAAAAAAAAAAAAAAAAABREYzIgbVRyIiL + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlfIiMERHIiIHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADhyIwR + EcjIiB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwRERERHMiMGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + fBERd3ERscEVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXBEXtxERUOt1AAAAAAAAAAAAAAAAAAAAAAAAAA + AAAADnEbAFERHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7AA4RweAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAALEXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///////////wA/+AAA/+P+AA/wAAD/wfwAB/AAAP+A+AAD8AAA/4DwAAHwAAD/AOAA + AfAAAP4B4D8A8B///APgf4D4D//4B8B/gHgP//APwH/AeAf/8A/A/8B8A//gH8D/wHwB/8A/4f/gfgD/ + gH///+A/AH8A////4D+APgH////gP8AeAf///+A/4AwD////8D/wCAf////wH/gAD/////Af/AAf//// + 8B/8AD/////wH/wAH/////gf/AAP////+B/wAAf//+AAAAAEAf//wAAAAA4A///AAAAADwB//8AAAAAf + gD//wAAAAD/AH//gAAAA/+A////8B///+H////wH///8/////Af////////+B/////////4H//////// + /gP////////+A///////B/4D//////4D/wP//////gIPAf/B//9+AAcB/4H//0AABwH/gf//gAAHgP+B + //8AAAeA/wH//wAAB4B/Af//AAAHwDwD//8AAADAAAP//4AAAOAAB///AAAA8AAH//8AEAD4AA///wAY + APwAH///ABAA/wB///8AAAD//////wAAA///////AAAB//////+AAAH//////8AAAf//////wAAB//// + ///AAAH//////8AAA///////wAB////////wgH/////////A////////KAAAAEAAAACAAAAAAQAYAAAA + AAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq + pQAnmwAnmgAlkwAlkgAlkAAkjgAkjQAkjQAlkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAArqAAnmAAlkgAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAlkQAkjgAjiwAlkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqpwAmlgAmlQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAtsQArqQApogApogApowApowApogApoAAnmgAmlQAkjgAjiQAmlAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAU4zgAwvwArqgAongAongAongAongAongAongAongAongAongAongAongAongAongAo + ngAonQAnmgAmlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwuwAsrgAongAkjQAlkAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAvtQAvtgAuswAvuAAxvQAywQAywgAywgAxvwAvtwAusQAqogAnmAAlkQAl + kQAAAAAAAAAAAAAAAAAAAAAAAAAAACFP1BZG0gE2zgAxvgAxvwAxvwAxvwAxvwAxvwAxvwAxvwAxvwAx + vwAxvwAxvwAxvwAxvwAwuwAtrgAqowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzxwAywwAxvwAs + qwAonAAmlQApoAAAAAAAAAAAAAAAAAAAAAAxvQAwuwAxvwAzxwA1zgk80BdH0iRS1SRS1SFP1BdH0gQ4 + zwAxvQAtrwAonQAlkQAlkQAAAAAAAAAAAAAAAAAAAAAAAClV1iZT1RZG0gA1zgA1zgI3zgY6zxBC0RZG + 0hZG0hZG0hZG0hZG0hZG0hZG0hZG0hZG0hFC0QA0yQAvuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAywwAzxgA0yAAzxQAwugAtsAAsqwAAAAAAAAAAAAAAAAAzwwAzwgA0yAA1zAc80BRG0yJR1S9b2D1m + 2j5n20Fp2ztl2ilW1xhJ1AA1zQAvtgAqoQAnlgApnQAAAAAAAAAAAAAAAAAAACVT1iRS1hRG0wA2zwU6 + 0BBD0iVT1i9b2Ddh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2Tdh2TBc2BpK1AA2zQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAywAAyvwA0xwA1zQY70AA2zwA1yQAzwgAAAAAAAAAAAAY80QA3zwE40AA2zQM50QxA + 0h9P1iVU1zBd2Ttl2z9o3Epx3k503kZu3TZh2hpL1QA2zQAvswAqnwAnlAAAAAAAAAAAAAAAAAAAACZV + 1yVU1xZI1AI50AA2zQA3zxRH1C9c2UFq3EJr3EJr3EJr3EJr3EJr3EJr3EJr3EJr3Dtl2y9c2RZI1AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAzwQAzwAA1yQA2zgU70QY80QtA0go/0gAAAAAAAAAAAAAAAAU80gU8 + 0gI60QE50QE50QE50RJG1AAAAAAAAAAAAAAAAAAAAAAAAElx3kVu3TBd2hZJ1QA0wQAtqgApmAApmgAA + AAAAAAAAAAAAACta2S5c2R1P1gY90gAzvwAvrwAvsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wQA0wQA2yAA3zQI60QQ70gpA0wk/0wAAAAAAAAAA + AAAAAAAAAAM70wQ80wA50AE60gA3ygA4zgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFr3TVi2x5Q1wI7 + 0gAzvQAtpQAolQAAAAAAAAAAAAAAAAAAAD1o3Sxb2hVJ1gA2yAAwsQArnQAtpwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1wgA1wgA2xgA3zAM70wY+0wtC1Ag/ + 0wAAAAAAAAAAAAAAAAAAABdL1hZK1g5E1AA50QA3ygA0vwA0wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ADZj3D1o3S1c2hVJ1gA2xwAwrwArnQAtpQAAAAAAAAAAAAAAADtn3DBe2hpN1wA50QAzugAsowAplgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ywA2xwA3ywA3ywA5 + 0gQ80wI70gM70wAAAAAAAAAAAAAAAAAAAAAAABtP2B5R2BRJ1gE70wA2xgA0vQA0vgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAD1p3jBf2xlN1wA50AA0vQAvrAAvqgAAAAAAAAAAAAAAADJh3Ddk3CRW2QxD + 1QA1wgAvqwAplwAplQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3 + xwA4ywA4zQA60wc/1AI80wM81AE70wAAAAAAAAAAAAAAAAAAAAAAACNW2ilb2yZY2h5S2QU/1QA4yAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtc2yha2xdN2AI91AA1wAAwqwAupQAAAAAAAAAAAAAA + AAAAAENu3zdl3R5S2QZA1QA1vwAupgAqlgAqlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAA2wwA2wwA5zAA60gU/1QhB1QpD1gtD1gAAAAAAAAAAAAAAAAAAAAAAAAAAADRk3j5r3zxq + 3zVl3hpQ2QA70gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxe3DFh3SBU2ghC1gA3wwAwrAAt + oAAAAAAAAAAAAAAAAAAAAD9s30Ju4DVl3htR2QA80wA1vAAupQAqlgAqlgAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4xQA3xAA6zQA70gQ/1gVA1gtE1wpE1wAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEJv4UBu4Dxr4Cte3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlo3ylc + 3Q9I2AA6zAAztAAuoQAAAAAAAAAAAAAAAAAAAAAAAEVx4URx4TJj3hhP2gA80QA2vAAvpQArlgArlgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4xgA4xQA7zgA80gM/1gRA1wpF2AlE1wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAADVm3ytf3hZP2gA90wA2vQAxqQAyrQAAAAAAAAAAAAAAAAAAAAAAAEd04kRx4jBi3xhQ2wA9 + 0wA3vgAwpgAslwAslwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5xgA5xgA7zAA80QE/1wJA1wNA1wdD2AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAC1i4Clf3xdR3ABA2AA6xQA0sgA0sQAAAAAAAAAAAAAAAAAAAAAAAAAA + AEl35EV04zJl4BlT3QA/1gA5wAAyqAAtmAAtmAAAAAAAAAAAAAAAAAAAAAA9zgA7yQA8ywA+0gBA2QhG + 2gJB2QBA2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACde4CZd4BdS3QJC2gA7xQA1sQAzrAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAEp45UV05DRo4hpU3gBA2AA6wQAyqQAumgAumgAAAAAAAAAAAAAAAAA+0AA+ + 0QA+0QBB2QZF2wlI2wlI2wND2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACph4S9l4h5Y3wZG3AA8yAA1 + sQAypwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEl45UV15TRp4hpV3wBB2AA6wQA0qwAwoAAypwAA + AAAAAAA9ywA/0QBA1gBC2gVG3AdH3AtK3QtK3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADds + 5CZf4Q5N3gA/zwA3tgAypAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEh45kV25TFn4xZT3wBA + 0wA6vwAzqQAwnQAAAAA9yQA+ywBB1ABC2QRG3QVH3QpK3QlK3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADds5Cxk4xZU4ABC1gA7vwA1qwA2sQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEZ35j9y5SVf4glL3gA/zAA4tQAypAA6vAA+ygBB0wBD2QRH3gVI3gtM3glL3gAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9n5Clj4xdW4QBE3AA+xwA4tQA4tQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAEN25zFp5BdW4QBE2wA9xAA2rgA6uQA/zABD2ABF3gBF3QBE2wVJ3wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChj5CZi5BZW4gFH3wBAywA6uAA5tgAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADRs5ixm5RdX4gBG3wBBzgA9wwA/ygBE1wBG3gBG3gBA + zAA9wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpm5i1o5hxc5AVM4gBB + zAA6twA4sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdZ5B1d5BRW4wBI4QJJ4QhO4hRW + 4xhZ5AZM4gBH3QA/xAA2qgA1pQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ADdw6CZk5g1S4wBDzwA7twA3qQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABH2wZN4wRM + 4gBJ4gNL4gpQ4yxo5zpy6Sdl5hJW5ABCzAA4rwAznwAynAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABda5hlc5gdP5ABH2AA+vwA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7 + tQA/wQBEzwBH2gBJ3wFL4wBJ3wBH2R9g5kJ56kZ86zBs6A1T5ABDzQA5rwA0nwAymgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vgA5 + rQA2owA1oQA1oQA1oQA1oQA1oQA1oQA7sgBEzgBK4ABL4wBK4ABBxgA3qAA0nQA1ogA2owA1oQA1oQA1 + oQA1ogA2pAA4qQA5rQA+vABF0wBJ3wBL4wNN5ABJ3QBI2w5V5QAAAEyB7EV86zRw6hJY5gBF0wA7swA1 + oAAzmwA2pQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAlT5gBJ2QBCxQA8sgA7sQA7sQA7sQA7sQA7sQA7sQA+uQBFzwBL3gBN5ABN5ABH0wBAvQA7sQA8 + swA8tAA8tAA8tAA8tAA9tQA+uABAvwBCxABG0QBL3gBN5ANP5QFO5QBM4wBM4QAAAAAAAAAAAEuB7UV9 + 7DZz6xhe5wBJ2QA+uAA4qAA1ngA2oQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAACBk6hde6QJP5wBI1gBI1ABI1ABI1ABI1ABI1ABI1ABK2gBM4gBO5gBO5wBO + 5wBO5gBL3wBJ2QBI1wBI1wBJ2ABJ2ABJ2ABJ2QBK2gBM4QBN5QBO5wJP5wlU6BRc6Q1X6ANQ5wBN5AAA + AAAAAAAAAAAAAEmB7kZ/7jh17B5j6gBL3wBBwQA6rAA2nwA3ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNn6yhr7CFm6xRd6hNc6hNc6hNc6hNc6hNc6hNc6hNc + 6hFb6gxX6QRS6ABP6ABP6ABP6AJQ6AlV6RFb6hNc6hNc6hNc6hNc6hNc6hRd6hdf6h1j6yJm6yBl6x5k + 6xxi6xBa6QAAAAAAAAAAAAAAAAAAAAAAAEZ/7kiB7j157ilr7AlV6QBFzAA9sgA4pAA4pAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFy7T577j167jZ17jR07TR07TR0 + 7TR07TR07TR07TR07TV07jBx7R5l7AlW6gBQ6QVT6RBb6idr7DJy7TR07TR07TR07TR07TR07TR07TNz + 7TZ17jd27ils7SJn7B5l7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEV/70mC7z977jBx7Q1Z6gBK2ABE + xQBExQBExwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmD8EuE + 8ER/8EJ+70J+70J+70J+70J+70J+70J+70iC8EmD8DZ27hdh7ABR6gBQ5wJS6iJo7Th370J+70J+70J+ + 70J+70J+70F97zp57zh37zl47y9x7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF970qD + 8EWA8DV17hdh7AVU6gJS6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD188TBz8Bhj7gBT7ABM1wBIzQBQ + 5QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAEyG8kWC8Stw7xRg7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClv8Cdu8Bhk + 7wJV7QBM1wBEwABGxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEOB8jR38QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAACxy8TF28SFr8Alb7wBO2QBFwABBtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAADd68ihx8g9g8ABR4ABGxABAswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADR58ypz8hVl8QBU5wBKzABCtwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC118ylz8xdn8gBX7gBO1wBG + wABHwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy9CVx + 9BZn8wBZ8gBR3ABJxgBIxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAACl19ix39hts9QVe9ABS3QBKxQBHvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dHRUVFQwLCwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADd/9yZ09gxk9QBU4ABKxQBDtAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4eHYuLi9bW1ouLixMT + EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmB+C16+BZr9wBZ6wBOzwBGugBKwwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMygBGuABFtQBDsgBFtQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdV + Vf////////v7+0NCQgAAAAAAAAAAAAkJCUA/Pw8ODwAAAAAAAAAAAAAAAAAAADF9+St5+Bdt+ABd9ABU + 3ABMyABMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVh9wBW4wBNyQBGuABGuQBEswAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAG1sbP///////////4iJiAYGBgAAAB4eHmhnZ7a1tXt7exISEgAAAAAAAAAAAAAAACx7 + +TB9+R5y+Qdj+ABW4ABLwwBGtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJq+ANh+ABY5QBS + 1QBQ0ABNyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAUFBTY1NSIiIgsLCwAAAAAAAH5+fv////39/fr7++rp6U5NTSQkJJGQkNDPz8LBwcfHx3JxcQcH + BwAAAAAAAAAAAAAAAD6H+i99+hZu+gBY5ABNyABGtwBKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAVj+Qlm+QFh+QBf9wBa6ABY5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAEBAQM/OztjY2IqJiSwsLC8vL8bGxv7+/vb29uXl5dfW1tDPz8rIyM7OzsG/ + v7m3t728vHx7ewAAAAAAAAAAAAAAAAAAADqE+jJ/+h1y+gBg+ABU2QBKwABGtwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAVj+Qlm+Qpm+QBg+ABc7wBX4QBX4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgoKKOjotXU1N/e3vn4+Pb39/j4+P////39/fLy8uDe3s/O + zsbFxbm3t6qpqba0tLm4uLGwr0NCQgAAAAAAAAAAAAAAAAAAADOB+zuG+y19+xVu+gBZ5ABNxgBEsABE + sAAAAAAAAAAAAAAAAAAAAAAAAAAAAABd8ARj+gRj+gJi+gBb6gBW3wBW3QAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgnJ6alpNXV1eLi4vDw8Pj4+Pn5+f7+ + /v7+/vHx8eDg4NbW1tfW1uDg4NXV1ba0tLy6unR0cwAAAAAAAAAAAAAAAAAAAAAAAAAAAD+J/DaD/CJ3 + /AJj+wBW3QBLwABErgBErQAAAAAAAAAAAAAAAABQzQBT1QBZ5ABe8QBg9wJj+wBf8gBd7wAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREQ8nIx+3s + 7O/v7+Tk5PHw8P////39/fHw8N7e3s7NzdXT0+Li4vPz8+/u7tLR0Xl4eBEQEAYGBgAAAAAAAAAAAAAA + AAAAADeF/T6J/TSD/Rx0/ABi+QBX3ABLwABFsABCpwBDqgBHtABLwABNxABS0QBb6ABg9QJk/ABi+QBg + 9QBg9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEtLS+Xl5ePi4tzb2/b19f////j4+Lu7u4SDg3FxcZKQkNnY2PHw8P////Py8tXU1JmYmHt6 + emxsbEJBQQAAAAAAAAAAAAAAAESN/kSN/jSE/Rt0/QBk/ABa5ABU1ABOxgBMvwBNwwBSzwBV1wBb5wBh + 9QJl/Qhp/QJl/QBi9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAABgYGOLi4tfW1t3d3fn5+fTz85SUlA0NDQAAAAAAAC0sLKmoqPPz8/7+ + /v///+vq6u7u7v///////9TU1AAAAAAAAAAAAAAAAAAAAEeP/kWO/jaF/R52/Qxr/QBi+QBf7wBb5wBc + 6ABf8QBi+QFl/Qhp/RJv/RZx/Qxr/QRm/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZGS4uLp6envLx8c7NzeLi4vj4+Lu7uxkZGQAAAAAAAAAA + AAAAAHJxcfPy8v/+/v////T19ezq6vf39/////f29gAAAAAAAAAAAAAAAAAAAAAAAEiQ/kmR/j6K/jCC + /iN6/h13/hRx/hJw/hNw/hdz/ht1/h94/h53/h13/hBv/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADk4OKqpqfPz8////+zs7NDPz+bm5vf396am + pgAAAAAAAAAAAAAAAAAAAFdXV/Lx8f7+/vz8/PX19fX09P/+/v79/cjHxwAAAAAAAAAAAAAAAAAAAAAA + AAAAAEqS/lGW/kmR/kKN/j2K/jaG/jSE/jOE/jOE/jSE/il+/iN6/h94/gAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKempvb29vHx8fv7+/T0 + 9NbW1ujo6Pv7+8C/vxQUFAAAAAAAAAAAAAAAAHh4ePf39/7+/vr6+vX19fn5+bu6un59fUBAQAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAESP/0iR/0iR/0WP/0KO/z+M/zmI/ziI/zCD/wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKi + oe/v7/f39/////39/eTj4+vq6v39/fn5+X5+fgAAAAAAAAAAACcnJ8vLy/n5+f7+/vn5+fz8/JqZmRUU + FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGdmZsC/v83MzNnY2Pn5+ff19e/v7/7+/v///+Pi4n19fURERFdWVrCvr/Dw8Pn5+f// + //39/f///3BwbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8fH0JBQZaVlfLy8vr6+v////z8/O/v79bV1b69vcbF + xd/e3uzs7Pz8/P////z8/P39/d/f3ykpKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlJdnY2Pf39/r5+f39 + /e/w8ODf39nY2NrZ2ePj4/Lx8fj4+Pf39/n5+f7+/v///8LCwiAgHwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEVE + RPb19eLh4dza2t7d3d3d3dXU1M/OztHQ0NfX19/e3unq6vPz8/////////Ly8t7d3X17ewAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAsKCq+vr/Tz89rZ2c7NzcPCwri3t62srKmoqK6trb68vNfW1rOysoiHh8LCwvLy8ubl5cHA + wExMTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAEFAQO3s7OPi4tPS0szLy56dnYB/f62srLe2tsTCwtvZ2c3MzEhIRwAA + ACMjInh4eJ+enmhoZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQjI62srNrZ2c3MzIaGhhEREQAAAGhnZ8bFxdDO + zuXk5MbFxTY1NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEREYODg3V1dQAAAAAA + AAAAAC4tLbm4uNfW1u7t7cXFxR4dHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAsLC4qJieLh4efm5pybmwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////wP/8AAH///8AP/gAAf/j/gAP/AAB/+P8Hgf8AAP/w/h/ + h/x///+H+P/D/D///w/w/+H8P//+H/H/4f4///w/8f/x/h///D/x//H+D//4f///8P8H//D////w/4P/ + 4f////D/wf/D////+P/g/4P////4//B/B/////h/+D8P////+H/8Ph/////4f/4cP/////x//hh///// + /H//AH/////8f/8Af/////w//wg//////D/+Hh/////8P/wfD///8AAAAD+H///wAAAAP8H///AAAAD/ + 4P//8AAAB//4f////h////z/////H/////////8f/////////w//////////D/////////8P//////// + /4//////////j/////////+P/////////4f/////////h///////j//H//////+P/8f/4////4cfw//D + ///ngB/j/8f//+AAP+H/x///4AA/8P+H///wAD/wfw////h8H/gAD///+H4D/AAf///w/gH+AH///8D/ + A//B////gP4P//////+A/h////////h8H///////+AAf///////8AA////////gAB///////+AHv//// + ///w4//////////j//////////P/////////9///////////////////KAAAAEAAAACAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+vgMAOa1TADajbgA1 + oW4ANaFuADWhbgA1oW4ANaFuADWhbgA7sm4ARM7EAErg/wBL4/8ASuD/AEHG/wA3qPgANJ2MADWibgA2 + o4YANaGGADWhhgA1oYYANaKGADakjAA4qa8AOa3uAD68/gBF0/8ASd//AEvj/wNN5P8ASd3/AEjb8A5V + 5QUAAAAATIHsgEV86/00cOr/Eljm/wBF0/8AO7P/ADWg/QAzm4AANqUBAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlT + 5gEASdnEAELF/wA8sv8AO7H/ADux/wA7sf8AO7H/ADux/wA7sf8APrn/AEXP/wBL3v8ATeT/AE3k/wBH + 0/8AQL3/ADux/wA8s/8APLT/ADy0/wA8tP8APLT/AD21/wA+uP8AQL//AELE/wBG0f8AS97/AE3k/wNP + 5f8BTuX/AEzj/gBM4VoAAAAAAAAAAAAAAABLge1NRX3s+jZz6/8YXuf/AEnZ/wA+uP8AOKj+ADWeswA2 + oQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAgZOoCF17p6AJP5/8ASNb/AEjU/wBI1P8ASNT/AEjU/wBI1P8ASNT/AEra/wBM + 4v8ATub/AE7n/wBO5/8ATub/AEvf/wBJ2f8ASNf/AEjX/wBJ2P8ASdj/AEnY/wBJ2f8AStr/AEzh/wBN + 5f8ATuf/Ak/n/wlU6P8UXOn/DVfo/wNQ58QATeQBAAAAAAAAAAAAAAAAAAAAAEmB7ilGf+7zOHXs/x5j + 6v8AS9//AEHB/wA6rP8ANp/hADeiDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI2frAihr7OghZuv/FF3q/xNc6v8TXOr/E1zq/xNc + 6v8TXOr/E1zq/xNc6v8RW+r/DFfp/wRS6P8AT+j/AE/o/wBP6P8CUOj/CVXp/xFb6v8TXOr/E1zq/xNc + 6v8TXOr/E1zq/xRd6v8XX+r/HWPr/yJm6/8gZev/HmTr/hxi67oQWukCAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAARn/uD0iB7uA9ee7/KWvs/wlV6f8ARcz/AD2y/wA4pPQAOKQSAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFy7QE+e+7HPXru/zZ1 + 7v80dO3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/NXTu/zBx7f8eZez/CVbq/wBQ6f8FU+n/EFvq/ydr + 7P8ycu3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/M3Pt/zZ17v43du78KWzt5SJn7EYeZewBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFf+8DSYLvtz977v4wce3/DVnq/wBK2P8ARMX/AETFwQBE + xwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASYPwA0uE8GdEf/B6Qn7vekJ+73pCfu96Qn7vekJ+73pCfu96Qn7vekiC8HpJg/DKNnbu/xdh + 7P8AUer/AFDn/wJS6vciaO2XOHfvekJ+73pCfu96Qn7vekJ+73pCfu96QX3vejp572E4d+9GOXjvGi9x + 7gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF97wFKg/CGRYDw/TV1 + 7v8XYez/BVTq/QJS6k0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAPXzxGjBz8PwYY+7/AFPs/wBM1/8ASM33AFDlDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEyG8lpFgvH5K3Dv+xRg7XQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAClv8AcnbvD0GGTv/wJV7f8ATNf/AETA/QBGxCMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ4HyDzR38RYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAscvEBMXbx2SFr8P8JW+//AE7Z/wBF + wP8AQbdGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADd6 + 8pEocfL/D2Dw/wBR4P8ARsT/AECzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA0efNTKnPy/xVl8f8AVOf/AErM/wBCt9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALXXzKSlz8/4XZ/L/AFfu/wBO1/8ARsDzAEfDBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy9A8lcfT6Fmfz/wBZ + 8v8AUdz/AEnG/ABIxBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAAAABAAAAAYAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAApdfYDLHf27xts9f8FXvT/AFLd/wBKxf4AR706AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + ADVoZ2etxsXF/9DOzv/l5OT/xsXF9jY1NWMAAAAJAAAABgAAABIAAAAZAAAAEAAAAAQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAMERERM4OD + g5B1dXV/AAAAJgAAAAgAAAAJLi0tV7m4uPbX1tb/7u3t/8XFxfMeHR1GAAAABAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAYAAAAPAAAADgAAAAQAAAAAAAAAAgsLCyOKiYmy4uHh/+fm5vqcm5u7AAAAIQAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAHwAA + ADYAAAA2AAAAIAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////////////8D//AAB////AD/4AAH/4/4A + D/wAAf/j/B4H/AAD/8P4f4f8f///h/j/w/w///8P8P/h/D///h/x/+H+P//8P/H/8f4f//w/8f/x/g// + +H////D/B//w////8P+D/+H////w/8H/w/////j/4P+D////+P/wfwf////4f/g/D/////h//D4f//// + +H/+HD/////8f/4Yf/////x//wB//////H//AH/////8P/8IP/////w//h4f/////D/8Hw////AAAAA/ + h///8AAAAD/B///wAAAA/+D///AAAAf/+H////4f///8/////x//////////H/////////8P//////// + /w//////////D/////////+P/////////4//////////j/////////+H/////////4f//////4//x/// + ////j//H/+P///+HH8P/w///54Af4//H///gAD/h/8f//+AAP/D/h///8AA/8H8P///4fB/4AA////h+ + A/wAH///8P4B/gB////A/wP/wf///4D+D///////gP4f///////4fB////////gAH////////AAP//// + ///4AAf///////gB7///////8OP/////////4//////////z//////////f//////////////////w== + + + \ No newline at end of file diff --git a/ConfigGUI/MinecraftFont/LICENSE.txt b/ConfigGUI/MinecraftFont/LICENSE.txt new file mode 100644 index 0000000..e0ed6f3 --- /dev/null +++ b/ConfigGUI/MinecraftFont/LICENSE.txt @@ -0,0 +1,9 @@ +This "Minecraft" font was adapted into TrueType file by me (DjDCH). + +This "Minecraft" font is licensed under a Creative Commons Attribution Share Alike license (http://creativecommons.org/licenses/by-sa/3.0/). + +The "DjDCH" name is own by me (http://djdch.com/). + +The "Minecraft" font style was made by Notch. + +The "Minecraft" game is own by Mojang Specifications. \ No newline at end of file diff --git a/ConfigGUI/MinecraftFont/README.txt b/ConfigGUI/MinecraftFont/README.txt new file mode 100644 index 0000000..c81e1d6 --- /dev/null +++ b/ConfigGUI/MinecraftFont/README.txt @@ -0,0 +1,28 @@ +This font was created by "DjDCH" : +http://djdch.com/ +The font file in this archive was created using Fontstruct the free, online +font-building tool from FontShop International. Try Fontstruct at : +http://fontstruct.fontshop.com + +LEGAL NOTICE: +In using this font you must comply with the licensing terms described in the +file "LICENSE.txt" included with this archive. +If you redistribute the font file in this archive, it must be accompanied by +all the other files from this archive, including this one. + +NOTE FOR FLASH USERS: +Fontstruct fonts (fontstructions) are optimized for +Flash. If the font in this archive is a pixel font, it is best displayed at a +font-size of 8. + +FONTSTRUCT: +Fontstruct is brought to you by FontShop. +Visit us at http://www.fontshop.com +FontShop is the original independent font retailer. We’ve been around since +the dawn of digital type. Whether you need the right font or need to create the +right font from scratch, let our 19 years of experience work for you. + +COPYRIGHT: +Fontstruct is copyright ©2010 Fontshop FSI and Rob Meek +Fontstruct was created for FontShop International by Rob Meek +(http://www.robmeek.com). \ No newline at end of file diff --git a/ConfigGUI/MinecraftFont/minecraft.ttf b/ConfigGUI/MinecraftFont/minecraft.ttf new file mode 100644 index 0000000000000000000000000000000000000000..55c5075c9dff1a1d8b569f6bdee522c40ddfc991 GIT binary patch literal 17676 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 literal 0 HcmV?d00001 diff --git a/ConfigGUI/PermissionLimitBox.Designer.cs b/ConfigGUI/PermissionLimitBox.Designer.cs new file mode 100644 index 0000000..5b23211 --- /dev/null +++ b/ConfigGUI/PermissionLimitBox.Designer.cs @@ -0,0 +1,68 @@ +namespace fCraft.ConfigGUI { + partial class PermissionLimitBox { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.comboBox = new System.Windows.Forms.ComboBox(); + this.label = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // comboBox + // + this.comboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox.FormattingEnabled = true; + this.comboBox.Location = new System.Drawing.Point( 80, 0 ); + this.comboBox.Name = "comboBox"; + this.comboBox.Size = new System.Drawing.Size( 150, 21 ); + this.comboBox.TabIndex = 1; + // + // label + // + this.label.AutoSize = true; + this.label.Location = new System.Drawing.Point( 3, 3 ); + this.label.Name = "label"; + this.label.Size = new System.Drawing.Size( 33, 13 ); + this.label.TabIndex = 0; + this.label.Text = "Label"; + this.label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // PermissionLimitBox + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.Transparent; + this.Controls.Add( this.comboBox ); + this.Controls.Add( this.label ); + this.Name = "PermissionLimitBox"; + this.Size = new System.Drawing.Size( 230, 21 ); + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ComboBox comboBox; + private System.Windows.Forms.Label label; + } +} diff --git a/ConfigGUI/PermissionLimitBox.cs b/ConfigGUI/PermissionLimitBox.cs new file mode 100644 index 0000000..7003063 --- /dev/null +++ b/ConfigGUI/PermissionLimitBox.cs @@ -0,0 +1,69 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + public sealed partial class PermissionLimitBox : UserControl { + + public Permission Permission { get; private set; } + + public string FirstItem { get; private set; } + + public Rank Rank { get; private set; } + + public PermissionLimitBox( string labelText, Permission permission, string firstItem ) { + InitializeComponent(); + + label.Text = labelText; + label.Left = (comboBox.Left - comboBox.Margin.Left) - (label.Width + label.Margin.Right); + + Permission = permission; + FirstItem = firstItem; + RebuildList(); + + comboBox.SelectedIndexChanged += OnPermissionLimitChanged; + } + + + void OnPermissionLimitChanged( object sender, EventArgs args ) { + if( Rank == null ) return; + Rank rankLimit = RankManager.FindRank( comboBox.SelectedIndex - 1 ); + if( rankLimit == null ) { + Rank.ResetLimit( Permission ); + } else { + Rank.SetLimit( Permission, rankLimit ); + } + } + + + public void Reset() { + comboBox.SelectedIndex = 0; + } + + + public void RebuildList() { + comboBox.Items.Clear(); + comboBox.Items.Add( FirstItem ); + foreach( Rank rank in RankManager.Ranks ) { + comboBox.Items.Add( MainForm.ToComboBoxOption( rank ) ); + } + } + + + public void SelectRank( Rank rank ) { + Rank = rank; + if( rank == null ) { + comboBox.SelectedIndex = -1; + Visible = false; + } else { + comboBox.SelectedIndex = rank.GetLimitIndex( Permission ); + Visible = rank.Can( Permission ); + } + } + + + public void PermissionToggled( bool isOn ) { + Visible = isOn; + } + } +} \ No newline at end of file diff --git a/ConfigGUI/PermissionLimitBox.resx b/ConfigGUI/PermissionLimitBox.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ConfigGUI/PermissionLimitBox.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/Program.cs b/ConfigGUI/Program.cs new file mode 100644 index 0000000..d6aaf09 --- /dev/null +++ b/ConfigGUI/Program.cs @@ -0,0 +1,46 @@ +/* + * Copyright 2009-2012 Matvei Stefarov + * + * 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. + * + */ +using System; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + static class Program { + [STAThread] + static void Main() { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault( false ); +#if DEBUG + Application.Run( new MainForm() ); +#else + try { + Application.Run( new MainForm() ); + } catch( Exception ex ) { + Logger.LogAndReportCrash( "Error in ConfigGUI", "ConfigGUI", ex, true ); + if( !Server.HasArg( ArgKey.ExitOnCrash ) ) { + MessageBox.Show( ex.ToString(), "fCraft ConfigGUI has crashed" ); + } + } +#endif + } + } +} \ No newline at end of file diff --git a/ConfigGUI/Properties/AssemblyInfo.cs b/ConfigGUI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1da671e --- /dev/null +++ b/ConfigGUI/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle( "fCraft ConfigGUI" )] +[assembly: AssemblyDescription( "fCraft graphical configuration tool" )] +[assembly: AssemblyConfiguration( "" )] +[assembly: AssemblyCompany( "matvei.org" )] +[assembly: AssemblyProduct( "fCraft ConfigGUI" )] +[assembly: AssemblyCopyright( "fCraft is Copyright © Matvei Stefarov 2009-2012 (matvei.org)" )] +[assembly: AssemblyTrademark( "" )] +[assembly: AssemblyCulture( "" )] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible( false )] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid( "061effe2-df77-48ee-8a23-8be48c5e7684" )] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion( "0.6.1.7" )] +[assembly: AssemblyFileVersion( "0.6.1.7" )] + +[assembly: CLSCompliant( false )] +[assembly: NeutralResourcesLanguage( "en-US" )] \ No newline at end of file diff --git a/ConfigGUI/Properties/Resources.Designer.cs b/ConfigGUI/Properties/Resources.Designer.cs new file mode 100644 index 0000000..7ea04fe --- /dev/null +++ b/ConfigGUI/Properties/Resources.Designer.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.5446 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace fCraft.ConfigGUI.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + sealed internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("fCraft.ConfigGUI.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap ChatBackground { + get { + object obj = ResourceManager.GetObject("ChatBackground", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static byte[] MinecraftFont { + get { + object obj = ResourceManager.GetObject("MinecraftFont", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/ConfigGUI/Properties/Resources.resx b/ConfigGUI/Properties/Resources.resx new file mode 100644 index 0000000..b814424 --- /dev/null +++ b/ConfigGUI/Properties/Resources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\ChatBackground.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\MinecraftFont\minecraft.ttf;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/SortableBindingList.cs b/ConfigGUI/SortableBindingList.cs new file mode 100644 index 0000000..d106719 --- /dev/null +++ b/ConfigGUI/SortableBindingList.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; + +namespace fCraft.ConfigGUI { + + [Serializable] + // SortableBindingList by Tim Van Wassenhove, http://www.timvw.be/presenting-the-sortablebindinglistt/ + public sealed class SortableBindingList : BindingList { + private bool isSorted; + private ListSortDirection dir = ListSortDirection.Ascending; + + [NonSerialized] + private PropertyDescriptor sort; + + #region BindingList Public Sorting API + + public void Sort() { + ApplySortCore( sort, dir ); + } + + + public void Sort( string property ) { + /* Get the PD */ + sort = FindPropertyDescriptor( property ); + + /* Sort */ + ApplySortCore( sort, dir ); + } + + + public void Sort( string property, ListSortDirection direction ) { + /* Get the sort property */ + sort = FindPropertyDescriptor( property ); + dir = direction; + + /* Sort */ + ApplySortCore( sort, dir ); + } + + #endregion + + + #region BindingList Sorting Overrides + + protected override bool SupportsSortingCore { + get { return true; } + } + + + protected override void ApplySortCore( PropertyDescriptor prop, ListSortDirection direction ) { + List items = Items as List; + + if( null != items ) { + PropertyComparer pc = new PropertyComparer( prop, direction ); + items.Sort( pc ); + + /* Set sorted */ + isSorted = true; + } else { + /* Set sorted */ + isSorted = false; + } + } + + + protected override bool IsSortedCore { + get { return isSorted; } + } + + + protected override void RemoveSortCore() { + isSorted = false; + } + + #endregion + + + #region BindingList Private Sorting API + + static PropertyDescriptor FindPropertyDescriptor( string property ) { + PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties( typeof( T ) ); + PropertyDescriptor prop = pdc.Find( property, true ); + return prop; + } + + #endregion + + + #region PropertyComparer + internal sealed class PropertyComparer : IComparer { + /* + * The following code contains code implemented by Rockford Lhotka: + * //msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnadvnet/html/vbnet01272004.asp + */ + + private readonly PropertyDescriptor property; + private readonly ListSortDirection direction; + + public PropertyComparer( PropertyDescriptor property, ListSortDirection direction ) { + this.property = property; + this.direction = direction; + } + + public int Compare( TKey xVal, TKey yVal ) { + /* Get property values */ + object xValue = GetPropertyValue( xVal, property.Name ); + object yValue = GetPropertyValue( yVal, property.Name ); + + foreach( Attribute att in property.Attributes ) { + var sortableAtt = att as SortablePropertyAttribute; + if( sortableAtt != null ) { + int comparisonResult = sortableAtt.Compare( property.Name, xVal, yVal ); + if( direction == ListSortDirection.Ascending ) { + return comparisonResult; + } else { + return -comparisonResult; + } + } + } + + /* Determine sort order */ + if( direction == ListSortDirection.Ascending ) { + return CompareAscending( xValue, yValue ); + } else { + return CompareDescending( xValue, yValue ); + } + } + + public bool Equals( TKey xVal, TKey yVal ) { + return xVal.Equals( yVal ); + } + + public int GetHashCode( TKey obj ) { + return obj.GetHashCode(); + } + + /* Compare two property values of any type */ + static int CompareAscending( object xValue, object yValue ) { + int result; + + /* If values implement IComparer */ + if( xValue is IComparable ) { + result = ((IComparable)xValue).CompareTo( yValue ); + } + /* If values don't implement IComparer but are equivalent */ + else if( xValue.Equals( yValue ) ) { + result = 0; + } + /* Values don't implement IComparer and are not equivalent, so compare as string values */ + else result = xValue.ToString().CompareTo( yValue.ToString() ); + + /* Return result */ + return result; + } + + static int CompareDescending( object xValue, object yValue ) { + /* Return result adjusted for ascending or descending sort order ie + multiplied by 1 for ascending or -1 for descending */ + return CompareAscending( xValue, yValue ) * -1; + } + + static object GetPropertyValue( TKey value, string property ) { + /* Get property */ + PropertyInfo propertyInfo = value.GetType().GetProperty( property ); + + /* Return value */ + return propertyInfo.GetValue( value, null ); + } + } + #endregion + } + + + [AttributeUsage( AttributeTargets.Property )] + public sealed class SortablePropertyAttribute : Attribute { + public SortablePropertyAttribute( Type type, string comparerMethodName ) { + if( type == null ) throw new ArgumentNullException( "type" ); + if( comparerMethodName == null ) throw new ArgumentNullException( "comparerMethodName" ); + method = type.GetMethod( comparerMethodName ); + if( method == null ) throw new ArgumentException( "No such method", "comparerMethodName" ); + } + + + readonly MethodInfo method; + public int Compare( string propertyName, object a, object b ) { + object[] methodArgs = new[] { propertyName, a, b }; + return (int)method.Invoke( null, methodArgs ); + } + } +} diff --git a/ConfigGUI/TextEditorPopup.Designer.cs b/ConfigGUI/TextEditorPopup.Designer.cs new file mode 100644 index 0000000..a4648a2 --- /dev/null +++ b/ConfigGUI/TextEditorPopup.Designer.cs @@ -0,0 +1,143 @@ +namespace fCraft.ConfigGUI { + partial class TextEditorPopup { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.tText = new System.Windows.Forms.TextBox(); + this.bOK = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.bInsertKeyword = new System.Windows.Forms.Button(); + this.bInsertColor = new System.Windows.Forms.Button(); + this.bReset = new System.Windows.Forms.Button(); + this.lWarning = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // tText + // + this.tText.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tText.Font = new System.Drawing.Font( "Lucida Console", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tText.Location = new System.Drawing.Point( 13, 41 ); + this.tText.Multiline = true; + this.tText.Name = "tText"; + this.tText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.tText.Size = new System.Drawing.Size( 485, 227 ); + this.tText.TabIndex = 0; + this.tText.WordWrap = false; + this.tText.KeyDown += new System.Windows.Forms.KeyEventHandler( this.tRules_KeyDown ); + // + // bOK + // + this.bOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bOK.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bOK.Location = new System.Drawing.Point( 292, 274 ); + this.bOK.Name = "bOK"; + this.bOK.Size = new System.Drawing.Size( 100, 28 ); + this.bOK.TabIndex = 1; + this.bOK.Text = "OK"; + this.bOK.Click += new System.EventHandler( this.bOK_Click ); + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Font = new System.Drawing.Font( "Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.bCancel.Location = new System.Drawing.Point( 398, 274 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 28 ); + this.bCancel.TabIndex = 2; + this.bCancel.Text = "Cancel"; + // + // bInsertKeyword + // + this.bInsertKeyword.Location = new System.Drawing.Point( 108, 12 ); + this.bInsertKeyword.Name = "bInsertKeyword"; + this.bInsertKeyword.Size = new System.Drawing.Size( 90, 23 ); + this.bInsertKeyword.TabIndex = 4; + this.bInsertKeyword.Text = "Insert Keyword"; + this.bInsertKeyword.UseVisualStyleBackColor = true; + this.bInsertKeyword.Click += new System.EventHandler( this.bInsertKeyword_Click ); + // + // bInsertColor + // + this.bInsertColor.Location = new System.Drawing.Point( 12, 12 ); + this.bInsertColor.Name = "bInsertColor"; + this.bInsertColor.Size = new System.Drawing.Size( 90, 23 ); + this.bInsertColor.TabIndex = 3; + this.bInsertColor.Text = "Insert Color"; + this.bInsertColor.UseVisualStyleBackColor = true; + this.bInsertColor.Click += new System.EventHandler( this.bInsertColor_Click ); + // + // bReset + // + this.bReset.Location = new System.Drawing.Point( 408, 12 ); + this.bReset.Name = "bReset"; + this.bReset.Size = new System.Drawing.Size( 90, 23 ); + this.bReset.TabIndex = 5; + this.bReset.Text = "Reset"; + this.bReset.UseVisualStyleBackColor = true; + this.bReset.Click += new System.EventHandler( this.bReset_Click ); + // + // lWarning + // + this.lWarning.AutoSize = true; + this.lWarning.Location = new System.Drawing.Point( 12, 283 ); + this.lWarning.Name = "lWarning"; + this.lWarning.Size = new System.Drawing.Size( 272, 13 ); + this.lWarning.TabIndex = 4; + this.lWarning.Text = "Warning: Lines over 64 characters long will be wrapped."; + // + // TextEditorPopup + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 510, 314 ); + this.Controls.Add( this.bReset ); + this.Controls.Add( this.bInsertColor ); + this.Controls.Add( this.bInsertKeyword ); + this.Controls.Add( this.lWarning ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bOK ); + this.Controls.Add( this.tText ); + this.Name = "TextEditorPopup"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "TextEditorPopup"; + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox tText; + private System.Windows.Forms.Button bOK; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.Button bInsertKeyword; + private System.Windows.Forms.Button bInsertColor; + private System.Windows.Forms.Button bReset; + private System.Windows.Forms.Label lWarning; + } +} \ No newline at end of file diff --git a/ConfigGUI/TextEditorPopup.cs b/ConfigGUI/TextEditorPopup.cs new file mode 100644 index 0000000..34b4ca4 --- /dev/null +++ b/ConfigGUI/TextEditorPopup.cs @@ -0,0 +1,70 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + public sealed partial class TextEditorPopup : Form { + public string OriginalText { get; private set; } + public string FileName { get; private set; } + + + public TextEditorPopup( string fileName, string defaultValue ) { + InitializeComponent(); + + FileName = fileName; + Text = "Editing " + FileName; + + if( File.Exists( fileName ) ) { + OriginalText = File.ReadAllText( fileName ); + } else { + OriginalText = defaultValue; + } + + tText.Text = OriginalText; + lWarning.Visible = ContainsLongLines(); + } + + bool ContainsLongLines() { + return tText.Lines.Any( line => (line.Length > 62) ); + } + + + private void tRules_KeyDown( object sender, KeyEventArgs e ) { + lWarning.Visible = ContainsLongLines(); + } + + private void bOK_Click( object sender, EventArgs e ) { + File.WriteAllText( FileName, tText.Text ); + Close(); + } + + ColorPicker colorPicker; + private void bInsertColor_Click( object sender, EventArgs e ) { + if( colorPicker == null ) colorPicker = new ColorPicker("Insert color",0); + if( colorPicker.ShowDialog() == DialogResult.OK){ + string colorToInsert = Color.Parse( colorPicker.ColorIndex ); + int selectionStart = tText.SelectionStart; + tText.Paste( colorToInsert ); + tText.Select( selectionStart, 2 ); + tText.Focus(); + } + } + + KeywordPicker keywordPicker; + private void bInsertKeyword_Click( object sender, EventArgs e ) { + if( keywordPicker == null ) keywordPicker = new KeywordPicker(); + if( keywordPicker.ShowDialog() == DialogResult.OK ) { + int selectionStart = tText.SelectionStart; + tText.Paste( keywordPicker.Result ); + tText.Select( selectionStart, keywordPicker.Result.Length ); + tText.Focus(); + } + } + + private void bReset_Click( object sender, EventArgs e ) { + tText.Text = OriginalText; + } + } +} diff --git a/ConfigGUI/TextEditorPopup.resx b/ConfigGUI/TextEditorPopup.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ConfigGUI/TextEditorPopup.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/UpdaterSettingsPopup.Designer.cs b/ConfigGUI/UpdaterSettingsPopup.Designer.cs new file mode 100644 index 0000000..b87e256 --- /dev/null +++ b/ConfigGUI/UpdaterSettingsPopup.Designer.cs @@ -0,0 +1,235 @@ +namespace fCraft.ConfigGUI { + partial class UpdaterSettingsPopup { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.gMode = new System.Windows.Forms.GroupBox(); + this.rAutomatic = new System.Windows.Forms.RadioButton(); + this.rPrompt = new System.Windows.Forms.RadioButton(); + this.rNotify = new System.Windows.Forms.RadioButton(); + this.rDisabled = new System.Windows.Forms.RadioButton(); + this.gOptions = new System.Windows.Forms.GroupBox(); + this.tRunAfterUpdate = new System.Windows.Forms.TextBox(); + this.xRunAfterUpdate = new System.Windows.Forms.CheckBox(); + this.tRunBeforeUpdate = new System.Windows.Forms.TextBox(); + this.xRunBeforeUpdate = new System.Windows.Forms.CheckBox(); + this.xBackupBeforeUpdating = new System.Windows.Forms.CheckBox(); + this.bOK = new System.Windows.Forms.Button(); + this.bCancel = new System.Windows.Forms.Button(); + this.gMode.SuspendLayout(); + this.gOptions.SuspendLayout(); + this.SuspendLayout(); + // + // gMode + // + this.gMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gMode.Controls.Add( this.rAutomatic ); + this.gMode.Controls.Add( this.rPrompt ); + this.gMode.Controls.Add( this.rNotify ); + this.gMode.Controls.Add( this.rDisabled ); + this.gMode.Location = new System.Drawing.Point( 13, 13 ); + this.gMode.Name = "gMode"; + this.gMode.Size = new System.Drawing.Size( 322, 119 ); + this.gMode.TabIndex = 0; + this.gMode.TabStop = false; + this.gMode.Text = "Updater preference"; + // + // rAutomatic + // + this.rAutomatic.AutoSize = true; + this.rAutomatic.Location = new System.Drawing.Point( 15, 92 ); + this.rAutomatic.Name = "rAutomatic"; + this.rAutomatic.Size = new System.Drawing.Size( 274, 17 ); + this.rAutomatic.TabIndex = 3; + this.rAutomatic.TabStop = true; + this.rAutomatic.Text = "Automatic - Download and apply all updates at once."; + this.rAutomatic.UseVisualStyleBackColor = true; + // + // rPrompt + // + this.rPrompt.AutoSize = true; + this.rPrompt.Location = new System.Drawing.Point( 15, 69 ); + this.rPrompt.Name = "rPrompt"; + this.rPrompt.Size = new System.Drawing.Size( 282, 17 ); + this.rPrompt.TabIndex = 2; + this.rPrompt.TabStop = true; + this.rPrompt.Text = "Prompt - Prepare updates to install, and ask to confirm."; + this.rPrompt.UseVisualStyleBackColor = true; + // + // rNotify + // + this.rNotify.AutoSize = true; + this.rNotify.Location = new System.Drawing.Point( 15, 46 ); + this.rNotify.Name = "rNotify"; + this.rNotify.Size = new System.Drawing.Size( 278, 17 ); + this.rNotify.TabIndex = 1; + this.rNotify.TabStop = true; + this.rNotify.Text = "Notify - Show a message when updates are available."; + this.rNotify.UseVisualStyleBackColor = true; + // + // rDisabled + // + this.rDisabled.AutoSize = true; + this.rDisabled.Location = new System.Drawing.Point( 15, 23 ); + this.rDisabled.Name = "rDisabled"; + this.rDisabled.Size = new System.Drawing.Size( 199, 17 ); + this.rDisabled.TabIndex = 0; + this.rDisabled.TabStop = true; + this.rDisabled.Text = "Disabled - Do not check for updates."; + this.rDisabled.UseVisualStyleBackColor = true; + this.rDisabled.CheckedChanged += new System.EventHandler( this.rDisabled_CheckedChanged ); + // + // gOptions + // + this.gOptions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gOptions.Controls.Add( this.tRunAfterUpdate ); + this.gOptions.Controls.Add( this.xRunAfterUpdate ); + this.gOptions.Controls.Add( this.tRunBeforeUpdate ); + this.gOptions.Controls.Add( this.xRunBeforeUpdate ); + this.gOptions.Controls.Add( this.xBackupBeforeUpdating ); + this.gOptions.Location = new System.Drawing.Point( 13, 138 ); + this.gOptions.Name = "gOptions"; + this.gOptions.Size = new System.Drawing.Size( 322, 147 ); + this.gOptions.TabIndex = 1; + this.gOptions.TabStop = false; + this.gOptions.Text = "Optional features"; + // + // tRunAfterUpdate + // + this.tRunAfterUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tRunAfterUpdate.Enabled = false; + this.tRunAfterUpdate.Font = new System.Drawing.Font( "Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tRunAfterUpdate.Location = new System.Drawing.Point( 43, 118 ); + this.tRunAfterUpdate.Name = "tRunAfterUpdate"; + this.tRunAfterUpdate.Size = new System.Drawing.Size( 273, 20 ); + this.tRunAfterUpdate.TabIndex = 4; + this.tRunAfterUpdate.TextChanged += new System.EventHandler( this.tRunAfterUpdate_TextChanged ); + // + // xRunAfterUpdate + // + this.xRunAfterUpdate.AutoSize = true; + this.xRunAfterUpdate.Location = new System.Drawing.Point( 15, 95 ); + this.xRunAfterUpdate.Name = "xRunAfterUpdate"; + this.xRunAfterUpdate.Size = new System.Drawing.Size( 215, 17 ); + this.xRunAfterUpdate.TabIndex = 3; + this.xRunAfterUpdate.Text = "Run a script or command after updating:"; + this.xRunAfterUpdate.UseVisualStyleBackColor = true; + this.xRunAfterUpdate.CheckedChanged += new System.EventHandler( this.xRunAfterUpdate_CheckedChanged ); + // + // tRunBeforeUpdate + // + this.tRunBeforeUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tRunBeforeUpdate.Enabled = false; + this.tRunBeforeUpdate.Font = new System.Drawing.Font( "Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.tRunBeforeUpdate.Location = new System.Drawing.Point( 43, 69 ); + this.tRunBeforeUpdate.Name = "tRunBeforeUpdate"; + this.tRunBeforeUpdate.Size = new System.Drawing.Size( 273, 20 ); + this.tRunBeforeUpdate.TabIndex = 2; + this.tRunBeforeUpdate.TextChanged += new System.EventHandler( this.tRunBeforeUpdate_TextChanged ); + // + // xRunBeforeUpdate + // + this.xRunBeforeUpdate.AutoSize = true; + this.xRunBeforeUpdate.Location = new System.Drawing.Point( 15, 46 ); + this.xRunBeforeUpdate.Name = "xRunBeforeUpdate"; + this.xRunBeforeUpdate.Size = new System.Drawing.Size( 224, 17 ); + this.xRunBeforeUpdate.TabIndex = 1; + this.xRunBeforeUpdate.Text = "Run a script or command before updating:"; + this.xRunBeforeUpdate.UseVisualStyleBackColor = true; + this.xRunBeforeUpdate.CheckedChanged += new System.EventHandler( this.xRunBeforeUpdate_CheckedChanged ); + // + // xBackupBeforeUpdating + // + this.xBackupBeforeUpdating.AutoSize = true; + this.xBackupBeforeUpdating.Location = new System.Drawing.Point( 15, 23 ); + this.xBackupBeforeUpdating.Name = "xBackupBeforeUpdating"; + this.xBackupBeforeUpdating.Size = new System.Drawing.Size( 284, 17 ); + this.xBackupBeforeUpdating.TabIndex = 0; + this.xBackupBeforeUpdating.Text = "Back up server data and configuration before updating"; + this.xBackupBeforeUpdating.UseVisualStyleBackColor = true; + // + // bOK + // + this.bOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.bOK.Location = new System.Drawing.Point( 179, 291 ); + this.bOK.Name = "bOK"; + this.bOK.Size = new System.Drawing.Size( 75, 23 ); + this.bOK.TabIndex = 2; + this.bOK.Text = "OK"; + this.bOK.UseVisualStyleBackColor = true; + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Location = new System.Drawing.Point( 260, 291 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 75, 23 ); + this.bCancel.TabIndex = 3; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + // + // UpdaterSettingsWindow + // + this.AcceptButton = this.bOK; + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.bCancel; + this.ClientSize = new System.Drawing.Size( 347, 326 ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.bOK ); + this.Controls.Add( this.gOptions ); + this.Controls.Add( this.gMode ); + this.Name = "UpdaterSettingsWindow"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Advanced updater settings"; + this.gMode.ResumeLayout( false ); + this.gMode.PerformLayout(); + this.gOptions.ResumeLayout( false ); + this.gOptions.PerformLayout(); + this.ResumeLayout( false ); + + } + + #endregion + + private System.Windows.Forms.GroupBox gMode; + private System.Windows.Forms.RadioButton rDisabled; + private System.Windows.Forms.RadioButton rAutomatic; + private System.Windows.Forms.RadioButton rPrompt; + private System.Windows.Forms.RadioButton rNotify; + private System.Windows.Forms.GroupBox gOptions; + private System.Windows.Forms.TextBox tRunAfterUpdate; + private System.Windows.Forms.CheckBox xRunAfterUpdate; + private System.Windows.Forms.TextBox tRunBeforeUpdate; + private System.Windows.Forms.CheckBox xRunBeforeUpdate; + private System.Windows.Forms.CheckBox xBackupBeforeUpdating; + private System.Windows.Forms.Button bOK; + private System.Windows.Forms.Button bCancel; + } +} \ No newline at end of file diff --git a/ConfigGUI/UpdaterSettingsPopup.cs b/ConfigGUI/UpdaterSettingsPopup.cs new file mode 100644 index 0000000..56a1098 --- /dev/null +++ b/ConfigGUI/UpdaterSettingsPopup.cs @@ -0,0 +1,97 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Windows.Forms; + +namespace fCraft.ConfigGUI { + public sealed partial class UpdaterSettingsPopup : Form { + + public string RunBeforeUpdate { + get { + if( xRunBeforeUpdate.Checked) return tRunBeforeUpdate.Text; + else return ""; + } + set { tRunBeforeUpdate.Text = value; } + } + + public string RunAfterUpdate { + get { + if( xRunAfterUpdate.Checked ) return tRunAfterUpdate.Text; + else return ""; + } + set { tRunAfterUpdate.Text = value; } + } + + public UpdaterMode UpdaterMode { + get { + if( rDisabled.Checked ) return UpdaterMode.Disabled; + if( rNotify.Checked ) return UpdaterMode.Notify; + if( rPrompt.Checked ) return UpdaterMode.Prompt; + return UpdaterMode.Auto; + } + set { + switch( value ) { + case UpdaterMode.Disabled: + rDisabled.Checked = true; break; + case UpdaterMode.Notify: + rNotify.Checked = true; break; + case UpdaterMode.Prompt: + rPrompt.Checked = true; break; + case UpdaterMode.Auto: + rAutomatic.Checked = true; break; + } + } + } + + public bool BackupBeforeUpdate { + get { return xBackupBeforeUpdating.Checked; } + set { xBackupBeforeUpdating.Checked = value; } + } + + string oldRunBeforeUpdate, oldRunAfterUpdate; + UpdaterMode oldUpdaterMode; + bool oldBackupBeforeUpdate; + + public UpdaterSettingsPopup() { + InitializeComponent(); + Shown += delegate { + oldRunBeforeUpdate = RunBeforeUpdate; + oldRunAfterUpdate = RunAfterUpdate; + oldUpdaterMode = UpdaterMode; + oldBackupBeforeUpdate = BackupBeforeUpdate; + }; + FormClosed += delegate { + if( DialogResult != DialogResult.OK ) { + RunBeforeUpdate = oldRunBeforeUpdate; + RunAfterUpdate = oldRunAfterUpdate; + UpdaterMode = oldUpdaterMode; + BackupBeforeUpdate = oldBackupBeforeUpdate; + } + }; + } + + private void xRunBeforeUpdate_CheckedChanged( object sender, EventArgs e ) { + tRunBeforeUpdate.Enabled = xRunBeforeUpdate.Checked; + } + + private void xRunAfterUpdate_CheckedChanged( object sender, EventArgs e ) { + tRunAfterUpdate.Enabled = xRunAfterUpdate.Checked; + } + + private void rDisabled_CheckedChanged( object sender, EventArgs e ) { + gOptions.Enabled = !rDisabled.Checked; + } + + + private void tRunBeforeUpdate_TextChanged( object sender, EventArgs e ) { + if( tRunBeforeUpdate.Text.Length > 0 ) { + xRunBeforeUpdate.Checked = true; + } + } + + private void tRunAfterUpdate_TextChanged( object sender, EventArgs e ) { + if( tRunAfterUpdate.Text.Length > 0 ) { + xRunAfterUpdate.Checked = true; + } + } + } +} \ No newline at end of file diff --git a/ConfigGUI/UpdaterSettingsPopup.resx b/ConfigGUI/UpdaterSettingsPopup.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ConfigGUI/UpdaterSettingsPopup.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ConfigGUI/WorldListEntry.cs b/ConfigGUI/WorldListEntry.cs new file mode 100644 index 0000000..467784c --- /dev/null +++ b/ConfigGUI/WorldListEntry.cs @@ -0,0 +1,432 @@ +using System; +using System.IO; +using System.Windows.Forms; +using System.Xml.Linq; +using fCraft.MapConversion; +using System.Linq; +using JetBrains.Annotations; + +namespace fCraft.ConfigGUI { + /// + /// A wrapper for per-World metadata, designed to be usable with SortableBindingList. + /// All these properties map directly to the UI controls. + /// + sealed class WorldListEntry : ICloneable { + public const string WorldInfoSignature = "(ConfigGUI)"; + public const string DefaultRankOption = "(everyone)"; + const string MapFileExtension = ".fcm"; + + internal bool LoadingFailed { get; private set; } + + + public WorldListEntry( [NotNull] string newName ) { + if( newName == null ) throw new ArgumentNullException( "newName" ); + name = newName; + } + + + public WorldListEntry( [NotNull] WorldListEntry original ) { + if( original == null ) throw new ArgumentNullException( "original" ); + name = original.Name; + Hidden = original.Hidden; + Backup = original.Backup; + BlockDBEnabled = original.BlockDBEnabled; + blockDBIsPreloaded = original.blockDBIsPreloaded; + blockDBLimit = original.blockDBLimit; + blockDBTimeLimit = original.blockDBTimeLimit; + accessSecurity = new SecurityController( original.accessSecurity ); + buildSecurity = new SecurityController( original.buildSecurity ); + LoadedBy = original.LoadedBy; + LoadedOn = original.LoadedOn; + MapChangedBy = original.MapChangedBy; + MapChangedOn = original.MapChangedOn; + environmentEl = original.environmentEl; + } + + + public WorldListEntry( [NotNull] XElement el ) { + if( el == null ) throw new ArgumentNullException( "el" ); + XAttribute temp; + + if( (temp = el.Attribute( "name" )) == null ) { + throw new FormatException( "WorldListEntity: Cannot parse XML: Unnamed worlds are not allowed." ); + } + if( !World.IsValidName( temp.Value ) ) { + throw new FormatException( "WorldListEntity: Cannot parse XML: Invalid world name skipped \"" + temp.Value + "\"." ); + } + name = temp.Value; + + if( (temp = el.Attribute( "hidden" )) != null && !String.IsNullOrEmpty( temp.Value ) ) { + bool hidden; + if( Boolean.TryParse( temp.Value, out hidden ) ) { + Hidden = hidden; + } else { + throw new FormatException( "WorldListEntity: Cannot parse XML: Invalid value for \"hidden\" attribute." ); + } + } else { + Hidden = false; + } + + if( (temp = el.Attribute( "backup" )) != null ) { + TimeSpan realBackupTimer; + if( temp.Value.ToTimeSpan( out realBackupTimer ) ) { + Backup = BackupNameFromValue( realBackupTimer ); + } else { + Logger.Log( LogType.Error, + "WorldListEntity: Cannot parse backup settings for world \"{0}\". Assuming default.", name ); + Backup = BackupEnumNames[0]; + } + } else { + Backup = BackupEnumNames[0]; + } + + XElement tempEl; + if( (tempEl = el.Element( WorldManager.AccessSecurityXmlTagName )) != null || + (tempEl = el.Element( "accessSecurity" )) != null ) { + accessSecurity = new SecurityController( tempEl, false ); + } + if( (tempEl = el.Element( WorldManager.BuildSecurityXmlTagName )) != null || + (tempEl = el.Element( "buildSecurity" )) != null ) { + buildSecurity = new SecurityController( tempEl, false ); + } + + XElement blockEl = el.Element( BlockDB.XmlRootName ); + if( blockEl == null ) { + BlockDBEnabled = YesNoAuto.Auto; + } else { + if( (temp = blockEl.Attribute( "enabled" )) != null ) { + YesNoAuto enabledStateTemp; + if( EnumUtil.TryParse( temp.Value, out enabledStateTemp, true ) ) { + BlockDBEnabled = enabledStateTemp; + } else { + Logger.Log( LogType.Warning, + "WorldListEntity: Could not parse BlockDB \"enabled\" attribute of world \"{0}\", assuming \"Auto\".", + name ); + BlockDBEnabled = YesNoAuto.Auto; + } + } + + if( (temp = blockEl.Attribute( "preload" )) != null ) { + bool isPreloaded; + if( Boolean.TryParse( temp.Value, out isPreloaded ) ) { + blockDBIsPreloaded = isPreloaded; + } else { + Logger.Log( LogType.Warning, + "WorldListEntity: Could not parse BlockDB \"preload\" attribute of world \"{0}\", assuming NOT preloaded.", + name ); + } + } + if( (temp = blockEl.Attribute( "limit" )) != null ) { + int limit; + if( Int32.TryParse( temp.Value, out limit ) ) { + blockDBLimit = limit; + } else { + Logger.Log( LogType.Warning, + "WorldListEntity: Could not parse BlockDB \"limit\" attribute of world \"{0}\", assuming NO limit.", + name ); + } + } + if( (temp = blockEl.Attribute( "timeLimit" )) != null ) { + int timeLimitSeconds; + if( Int32.TryParse( temp.Value, out timeLimitSeconds ) ) { + blockDBTimeLimit = TimeSpan.FromSeconds( timeLimitSeconds ); + } else { + Logger.Log( LogType.Warning, + "WorldListEntity: Could not parse BlockDB \"timeLimit\" attribute of world \"{0}\", assuming NO time limit.", + name ); + } + } + } + + if( (tempEl = el.Element( "LoadedBy" )) != null ) { + LoadedBy = tempEl.Value; + } + if( (tempEl = el.Element( "MapChangedBy" )) != null ) { + MapChangedBy = tempEl.Value; + } + + if( (tempEl = el.Element( "LoadedOn" )) != null ) { + if( !tempEl.Value.ToDateTime( ref LoadedOn ) ) { + LoadedOn = DateTime.MinValue; + } + } + if( (tempEl = el.Element( "MapChangedOn" )) != null ) { + if( !tempEl.Value.ToDateTime( ref MapChangedOn ) ) { + MapChangedOn = DateTime.MinValue; + } + } + environmentEl = el.Element( WorldManager.EnvironmentXmlTagName ); + } + + public string LoadedBy, MapChangedBy; + public DateTime LoadedOn, MapChangedOn; + readonly XElement environmentEl; + + + #region List Properties + + string name; + [SortableProperty( typeof( WorldListEntry ), "Compare" )] + public string Name { + get { + return name; + } + set { + if( name == value ) return; + if( !World.IsValidName( value ) ) { + throw new FormatException( "Invalid world name" ); + + } else if( !value.Equals( name, StringComparison.OrdinalIgnoreCase ) && MainForm.IsWorldNameTaken( value ) ) { + throw new FormatException( "Duplicate world names are not allowed." ); + + } else { + string oldName = name; + string oldFileName = Path.Combine( Paths.MapPath, oldName + ".fcm" ); + string newFileName = Path.Combine( Paths.MapPath, value + ".fcm" ); + if( File.Exists( oldFileName ) ) { + bool isSameFile; + if( MonoCompat.IsCaseSensitive ) { + isSameFile = newFileName.Equals( oldFileName, StringComparison.Ordinal ); + } else { + isSameFile = newFileName.Equals( oldFileName, StringComparison.OrdinalIgnoreCase ); + } + if( File.Exists( newFileName ) && !isSameFile ) { + string messageText = String.Format( "Map file \"{0}\" already exists. Overwrite?", value + ".fcm" ); + var result = MessageBox.Show( messageText, "", MessageBoxButtons.OKCancel ); + if( result == DialogResult.Cancel ) return; + } + Paths.ForceRename( oldFileName, newFileName ); + } + name = value; + if( oldName != null ) { + MainForm.HandleWorldRename( oldName, name ); + } + } + } + } + + + [SortableProperty( typeof( WorldListEntry ), "Compare" )] + public string Description { + get { + Map mapHeader = MapHeader; + if( LoadingFailed ) { + return "(cannot load file)"; + } else { + return String.Format( "{0} × {1} × {2}", + mapHeader.Width, + mapHeader.Length, + mapHeader.Height ); + } + } + } + + + public bool Hidden { get; set; } + + + readonly SecurityController accessSecurity = new SecurityController(); + string accessRankString; + public string AccessPermission { + get { + if( accessSecurity.HasRankRestriction ) { + return MainForm.ToComboBoxOption( accessSecurity.MinRank ); + } else { + return DefaultRankOption; + } + } + set { + foreach( Rank rank in RankManager.Ranks ) { + if( MainForm.ToComboBoxOption(rank) == value ) { + accessSecurity.MinRank = rank; + accessRankString = rank.FullName; + return; + } + } + accessSecurity.ResetMinRank(); + accessRankString = ""; + } + } + + + readonly SecurityController buildSecurity = new SecurityController(); + string buildRankString; + public string BuildPermission { + get { + if( buildSecurity.HasRankRestriction ) { + return MainForm.ToComboBoxOption(buildSecurity.MinRank); + } else { + return DefaultRankOption; + } + } + set { + foreach( Rank rank in RankManager.Ranks ) { + if( MainForm.ToComboBoxOption(rank) == value ) { + buildSecurity.MinRank = rank; + buildRankString = rank.FullName; + return; + } + } + buildSecurity.ResetMinRank(); + buildRankString = null; + } + } + + + public string Backup { get; set; } + + #endregion + + + internal XElement Serialize() { + XElement element = new XElement( "World" ); + element.Add( new XAttribute( "name", Name ) ); + element.Add( new XAttribute( "hidden", Hidden ) ); + if( Backup != BackupEnumNames[0] ) { + element.Add( new XAttribute( "backup", BackupValueFromName( Backup ).ToTickString() ) ); + } + element.Add( accessSecurity.Serialize( WorldManager.AccessSecurityXmlTagName ) ); + element.Add( buildSecurity.Serialize( WorldManager.BuildSecurityXmlTagName ) ); + XElement blockDB = new XElement( BlockDB.XmlRootName ); + blockDB.Add( new XAttribute( "enabled", BlockDBEnabled ) ); + blockDB.Add( new XAttribute( "preload", blockDBIsPreloaded ) ); + blockDB.Add( new XAttribute( "limit", blockDBLimit ) ); + blockDB.Add( new XAttribute( "timeLimit", (int)blockDBTimeLimit.TotalSeconds ) ); + element.Add( blockDB ); + + if( environmentEl != null ) element.Add( environmentEl ); + + if( !String.IsNullOrEmpty( LoadedBy ) ) element.Add( new XElement( "LoadedBy", LoadedBy ) ); + if( !String.IsNullOrEmpty( MapChangedBy ) ) element.Add( new XElement( "MapChangedBy", MapChangedBy ) ); + if( LoadedOn != DateTime.MinValue ) element.Add( new XElement( "LoadedOn", LoadedOn ) ); + if( MapChangedOn != DateTime.MinValue ) element.Add( new XElement( "MapChangedOn", MapChangedOn ) ); + return element; + } + + + public void ReparseRanks() { + Rank accessMinRank = Rank.Parse( accessRankString ); + if( accessMinRank != null ) { + accessSecurity.MinRank = accessMinRank; + } else { + accessSecurity.ResetMinRank(); + } + + Rank buildMinRank = Rank.Parse( buildRankString ); + if( buildMinRank != null ) { + buildSecurity.MinRank = buildMinRank; + } else { + buildSecurity.ResetMinRank(); + } + } + + + Map cachedMapHeader; + internal Map MapHeader { + get { + if( cachedMapHeader == null && !LoadingFailed ) { + string fullFileName = Path.Combine( Paths.MapPath, name + ".fcm" ); + LoadingFailed = !MapUtility.TryLoadHeader( fullFileName, out cachedMapHeader ); + } + return cachedMapHeader; + } + } + + + internal string FileName { + get { return Name + MapFileExtension; } + } + + + internal string FullFileName { + get { return Path.Combine( Paths.MapPath, Name + MapFileExtension ); } + } + + + #region Backup + + public static string BackupNameFromValue( TimeSpan value ) { + TimeSpan closestMatch = BackupEnumValues.OrderBy( t => Math.Abs( value.Subtract( t ).Ticks ) ).First(); + return BackupEnumNames[Array.IndexOf( BackupEnumValues, closestMatch )]; + } + + public static TimeSpan BackupValueFromName( string name ) { + return BackupEnumValues[Array.IndexOf( BackupEnumNames, name )]; + } + + public static readonly string[] BackupEnumNames = new[] { + "(default)", + "Never", + "5 Minutes", + "10 Minutes", + "15 Minutes", + "20 Minutes", + "30 Minutes", + "45 Minutes", + "1 Hour", + "2 Hours", + "3 Hours", + "4 Hours", + "6 Hours", + "8 Hours", + "12 Hours", + "24 Hours", + "48 Hours" + }; + + static readonly TimeSpan[] BackupEnumValues = new[] { + TimeSpan.FromSeconds(-1), // default + TimeSpan.Zero, + TimeSpan.FromMinutes(5), + TimeSpan.FromMinutes(10), + TimeSpan.FromMinutes(15), + TimeSpan.FromMinutes(20), + TimeSpan.FromMinutes(30), + TimeSpan.FromMinutes(45), + TimeSpan.FromHours(1), + TimeSpan.FromHours(2), + TimeSpan.FromHours(3), + TimeSpan.FromHours(4), + TimeSpan.FromHours(6), + TimeSpan.FromHours(8), + TimeSpan.FromHours(12), + TimeSpan.FromHours(24), + TimeSpan.FromHours(48) + }; + + #endregion + + + public YesNoAuto BlockDBEnabled { get; set; } + readonly bool blockDBIsPreloaded; + readonly int blockDBLimit; + TimeSpan blockDBTimeLimit; + + + public object Clone() { + return new WorldListEntry( this ); + } + + + // Comparison method used to customize sorting + [UsedImplicitly] + public static object Compare( string propertyName, object a, object b ) { + WorldListEntry entry1 = (WorldListEntry)a; + WorldListEntry entry2 = (WorldListEntry)b; + switch( propertyName ) { + case "Description": + if( entry1.MapHeader == null && entry2.MapHeader == null ) return null; + if( entry1.MapHeader == null ) return -1; + if( entry2.MapHeader == null ) return 1; + int volumeDifference = entry1.MapHeader.Volume - entry2.MapHeader.Volume; + return Math.Min( 1, Math.Max( -1, volumeDifference ) ); + + case "Name": + return StringComparer.OrdinalIgnoreCase.Compare( entry1.name, entry2.name ); + + default: + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/ConfigGUI/app.config b/ConfigGUI/app.config new file mode 100644 index 0000000..a8afe94 --- /dev/null +++ b/ConfigGUI/app.config @@ -0,0 +1,3 @@ + + + diff --git a/ConfigGUI/fcraft_config.ico b/ConfigGUI/fcraft_config.ico new file mode 100644 index 0000000000000000000000000000000000000000..edd39a131c54e432a77de5ac7a208155f1110256 GIT binary patch literal 61798 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 to true in config.xml). + +"Could not connect to server: it's probably down": + Make sure that you added firewall exception for fCraft (if applicable), + and forwarded the port on your router. If you are connecting from same + computer that the server is working on, try connecting to: + http://www.minecraft.net/play.jsp?ip=127.0.0.1&port=____ + (fill in the blank with your server's port number) + +"Could not verify player name": + Verification problems occur when your fCraft server cannot verify identity + of connecting players. Here are some things that may cause or fix + verification problems: + 1. If minecraft.net is offline or slow, wait for it to stabilize. + 2. If minecraft.net is working but you still cant verify name, log out then + log back in. + 3. Try restarting your server. Wait a couple minutes before trying to + connect to a newly-restarted server (to give your server time to + synchronize with minecraft.net). + 4. If you (or your players) are using WoM client's "Resume" function, which + uses cached verification information, use the proper log-in procedure + in WoM. The "Resume" function only works as long as your IP does not + change and as long as the server does not restart. + 5. If you are using WoM and connecting with a bookmark, make sure that the + bookmarked address starts with "http://www.minecraft.net/..." and not + "mc://...". Addresses in the form "mc://" are temporary, and will stop + working whenever the server is restarted. + +Other players cannot connect from the same LAN/network as me: + Minecraft client has a lot of trouble working on LAN. You probably will not + be able to connect via the public URL. There is a workaround: + + 1. Check "Allow connections from LAN without verification" in ConfigGUI. + (or set to true in config.xml). + 2. Find your local IP address. + * In Windows XP+, go to Start -> type "cmd" to open a terminal -> + type "ipconfig". The address you need is labeled "IPv4 Address" + under "Local Area Connection". + * In Unix/Linux, use "ifconfig" utility. + 3. Connect to http://www.minecraft.net/play.jsp?ip=____&port=____ + (fill in the blanks with your server's IP address and port number) + + + +=== List of Files ============================================================= + + ConfigGUI.exe - Graphical interface for editing your server's settings, + rank setup, and world list. Also includes a map coverter + and terrain generator. If you alter configuration while + the server is running, use /reloadconfig command to + apply the changes. Note that some changes (like changes + to the rank list and IRC configuration) require a full + server restart. + ConfigCLI.exe - A simple command-line configuration tool. + + fCraft.dll - Core of the server, used by all other applications. + fCraftGUI.dll - Provides shared functionality for Config and Server GUI. + + ServerCLI.exe - Command-line interface for the server. + ServerGUI.exe - Graphical interface for the server. + + + +=== Command-line Options ====================================================== + +In addition to many settings stored in config.xml, fCraft has several special +options that can only be set via command-line switches: + + --path= Working path (directory) that fCraft should use. If the + given path is relative, it's computed against the + location of fCraft.dll + + --logpath= Path (directory) where the log files should be placed. + If the given path is relative, it's computed against the + working path. + + --mappath= Path (directory) where the map files should be loaded + from/saved to. If the given path is relative, it's + computed against the working path. + + --config= Path (file) of the configuration file, including the + filename (typically "config.xml"). If the given path + is relative, it's computed against the working path. + + --norestart If this flag is present, fCraft will shutdown whenever + it would normally restart (e.g. automatic updates or + /restart command). This may be useful if you are using + an auto-restart script or a process monitor. + + --exitoncrash If this flag is present, fCraft frontends will exit + at once in the event of an unrecoverable crash, instead + of showing a message and prompting for user input. + + --nolog If this flag is present, all logging is disabled. + + --nocolor If this flag is present, ServerCLI will not use any + colors or formatting in its console output. + + + +=== Help & Support ============================================================ + +When you first join the server, promote yourself by typing... + /rank YourNameHere owner +...in the server's console. Replace "owner" if you renamed your highest rank. + +Type "/help" in-game or in server console to get started. Type "/commands" for +a list of available commands. For detailed information, please visit: + http://fcraft.net/wiki + +To request features, report bugs, or receive support, please visit: + http://forums.fcraft.net + +For quick help/support, join #fCraft channel on Esper.net IRC: + irc://irc.esper.net:5555/fCraft + +See CHANGELOG.txt or visit http://www.fcraft.net/wiki/Version_history for +complete information about changes in this release compared to previous +versions of fCraft. + + + +=== Licensing ================================================================= + +fCraft is open-source and free for all uses. fCraft code and binaries are +licensed and distributed under the permissive MIT License, reproduced here: + +---- +Copyright 2009, 2010, 2011 Matvei Stefarov + +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. +---- + +If you create fCraft plugins that do not include any substantial portions of +fCraft's original code, they belong to you and you are free to do absolutely +anything with them. However, if you would like to distribute whole modified +copies of fCraft, please follow the MIT License terms. + +fCraft uses and bundles Devart dotConnect for MySQL, under free license. +Full text of the Devart dotConnect for MySQL license should be included with +your copy of fCraft in LICENSE.dotConnect.txt + +Original Minecraft was developed by Markus "Notch" Persson of Mojang, and is +not affiliated with fCraft in any way. fCraft does not use any code, assets, +or any other files from Minecraft. + + + +=== Credits =================================================================== + +fCraft was developed by Matvei Stefarov (me@matvei.org) in 2009-2011 + +Thanks to fCraft code contributors and modders: + Asiekierka, Dag10, Destroyer, FontPeg, Jonty800, M1_Abrams, Optical-Lza, + Redshift, SystemX17, TkTech, Wootalyzer + +Thanks to people who supported fCraft development through donations: + fCraft.net community, Astelyn, D3M0N, Destoned, DreamPhreak, Pandorum, + Redshift, TkTech, ven000m, wtfmejt, Team9000 and SpecialAttack.net + communities, and others who donated anonymously + +Thanks to people whose code has been ported to fCraft: + Dudecon (Forester), Osici (Omen), vLK (MinerCPP), Tim Van Wassenhove, + Paul Bourke + +Thanks to Minecraft servers that helped test and improve fCraft: + TheOne's Zombie Survival, SpecialAttack.net Freebuild, Team9000 Freebuild, + D3M0Ns FreeBuild, ~The Best Freebuild 24/7~, fCraft Freebuild Official + +Thanks to people who submitted bug reports and feature requests: + Astelyn, Clhdrums87, Darklight, David00143, Dogwatch, Epiclolwut, Fehzor, + Gamma-Metroid, Hellenion, Sunfall, maintrain97, Mavinstar, Unison, + and all others. + +Special thanks for inspiration and suggestions: + CO2, Descension, ElectricFuzzball, Exe, Hearty0, iKJames, LG_Legacy, + PyroPyro, Revenant, Varriount, Voziv, Zaneo, #mcc on Esper.net, + HyveBuild/iCraft team, MinerCPP team, OpenCraft team + +And thank You for using fCraft! diff --git a/ServerCLI/Program.cs b/ServerCLI/Program.cs new file mode 100644 index 0000000..8c6507b --- /dev/null +++ b/ServerCLI/Program.cs @@ -0,0 +1,230 @@ +/* + * Copyright 2009-2012 Matvei Stefarov + * + * 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. + * + */ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; +using System.Net; +using System.ComponentModel; +using fCraft.Events; +using System.Reflection; + +namespace fCraft.ServerCLI { + + static class Program { + static bool useColor = true; + + static void Main( string[] args ) { + Logger.Logged += OnLogged; + Heartbeat.UriChanged += OnHeartbeatUriChanged; + + Console.Title = "fCraft " + Updater.CurrentRelease.VersionString + " - starting..."; + +#if !DEBUG + try { +#endif + Server.InitLibrary( args ); + useColor = !Server.HasArg( ArgKey.NoConsoleColor ); + + Server.InitServer(); + + CheckForUpdates(); + Console.Title = "fCraft " + Updater.CurrentRelease.VersionString + " - " + ConfigKey.ServerName.GetString(); + + if( !ConfigKey.ProcessPriority.IsBlank() ) { + try { + Process.GetCurrentProcess().PriorityClass = ConfigKey.ProcessPriority.GetEnum(); + } catch( Exception ) { + Logger.Log( LogType.Warning, "Program.Main: Could not set process priority, using defaults." ); + } + } + + if( Server.StartServer() ) { + Console.WriteLine( "** Running fCraft version {0}. **", Updater.CurrentRelease.VersionString ); + Console.WriteLine( "** Server is now ready. Type /Shutdown to exit safely. **" ); + + while( !Server.IsShuttingDown ) { + string cmd = Console.ReadLine(); + if( cmd.Equals( "/Clear", StringComparison.OrdinalIgnoreCase ) ) { + Console.Clear(); + } else { + try { + Player.Console.ParseMessage( cmd, true ); + } catch( Exception ex ) { + Logger.LogAndReportCrash( "Error while executing a command from console", "ServerCLI", ex, false ); + } + } + } + + } else { + ReportFailure( ShutdownReason.FailedToStart ); + } +#if !DEBUG + } catch( Exception ex ) { + Logger.LogAndReportCrash( "Unhandled exception in ServerCLI", "ServerCLI", ex, true ); + ReportFailure( ShutdownReason.Crashed ); + } finally { + Console.ResetColor(); + } +#endif + } + + + static void ReportFailure( ShutdownReason reason ) { + Console.Title = String.Format( "fCraft {0} {1}", Updater.CurrentRelease.VersionString, reason ); + if( useColor ) Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine( "** {0} **", reason ); + if( useColor ) Console.ResetColor(); + Server.Shutdown( new ShutdownParams( reason, TimeSpan.Zero, false, false ), true ); + if( !Server.HasArg( ArgKey.ExitOnCrash ) ) { + Console.ReadLine(); + } + } + + + [DebuggerStepThrough] + static void OnLogged( object sender, LogEventArgs e ) { + if( !e.WriteToConsole ) return; + switch( e.MessageType ) { + case LogType.Error: + if(useColor)Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine( e.Message ); + if( useColor ) Console.ResetColor(); + return; + + case LogType.SeriousError: + if( useColor ) Console.ForegroundColor = ConsoleColor.White; + if( useColor ) Console.BackgroundColor = ConsoleColor.Red; + Console.Error.WriteLine( e.Message ); + if( useColor ) Console.ResetColor(); + return; + + case LogType.Warning: + if( useColor ) Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine( e.Message ); + if( useColor ) Console.ResetColor(); + return; + + case LogType.Debug: + case LogType.Trace: + if( useColor ) Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine( e.Message ); + if( useColor ) Console.ResetColor(); + return; + + default: + Console.WriteLine( e.Message ); + return; + } + } + + + static void OnHeartbeatUriChanged( object sender, UriChangedEventArgs e ) { + File.WriteAllText( "externalurl.txt", e.NewUri.ToString(), Encoding.ASCII ); + Console.WriteLine( "** URL: {0} **", e.NewUri ); + Console.WriteLine( "URL is also saved to file externalurl.txt" ); + } + + + #region Updates + + + static readonly AutoResetEvent UpdateDownloadWaiter = new AutoResetEvent( false ); + static bool updateFailed; + + static readonly object progressReportLock = new object(); + static void OnUpdateDownloadProgress( object sender, DownloadProgressChangedEventArgs e ) { + lock( progressReportLock ) { + Console.CursorLeft = 0; + int maxProgress = Console.WindowWidth - 9; + int progress = (int)Math.Round((e.ProgressPercentage / 100f) * (maxProgress - 1)); + Console.Write( "{0,3}% |", e.ProgressPercentage ); + Console.Write( new String( '=', progress ) ); + Console.Write( '>' ); + Console.Write( new String( ' ', maxProgress - progress ) ); + Console.Write( '|' ); + } + } + + + static void OnUpdateDownloadCompleted( object sender, AsyncCompletedEventArgs e ) { + Console.WriteLine(); + if( e.Error != null ) { + updateFailed = true; + if( useColor ) Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine( "Downloading the updater failed: {0}", e.Error ); + if( useColor ) Console.ResetColor(); + } else { + Console.WriteLine( "Update download finished." ); + } + UpdateDownloadWaiter.Set(); + } + + + static void CheckForUpdates() { + UpdaterMode updaterMode = ConfigKey.UpdaterMode.GetEnum(); + if( updaterMode == UpdaterMode.Disabled ) return; + + UpdaterResult update = Updater.CheckForUpdates(); + + if( update.UpdateAvailable ) { + Console.WriteLine( "** A new version of fCraft is available: {0}, released {1:0} day(s) ago. **", + update.LatestRelease.VersionString, + update.LatestRelease.Age.TotalDays ); + if( updaterMode != UpdaterMode.Notify ) { + WebClient client = new WebClient(); + client.DownloadProgressChanged += OnUpdateDownloadProgress; + client.DownloadFileCompleted += OnUpdateDownloadCompleted; + client.DownloadFileAsync( update.DownloadUri, Paths.UpdaterFileName ); + UpdateDownloadWaiter.WaitOne(); + if( updateFailed ) return; + + if( updaterMode == UpdaterMode.Prompt ) { + Console.WriteLine( "Restart the server and update now? y/n" ); + var key = Console.ReadKey(); + if( key.KeyChar == 'y' ) { + RestartForUpdate(); + return; + } else { + Console.WriteLine( "You can update manually by shutting down the server and running " + Paths.UpdaterFileName ); + } + } else { + RestartForUpdate(); + return; + } + } + } + } + + + static void RestartForUpdate() { + string restartArgs = String.Format( "{0} --restart=\"{1}\"", + Server.GetArgString(), + MonoCompat.PrependMono( "ServerGUI.exe" ) ); + MonoCompat.StartDotNetProcess( Paths.UpdaterFileName, restartArgs, true ); + } + + #endregion + } +} \ No newline at end of file diff --git a/ServerCLI/Properties/AssemblyInfo.cs b/ServerCLI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6ea3dfd --- /dev/null +++ b/ServerCLI/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle( "fCraft ServerCLI" )] +[assembly: AssemblyDescription( "Command-line frontend for fCraft server" )] +[assembly: AssemblyConfiguration( "" )] +[assembly: AssemblyCompany( "matvei.org" )] +[assembly: AssemblyProduct( "fCraft ServerCLI" )] +[assembly: AssemblyCopyright( "fCraft is Copyright © Matvei Stefarov 2009-2012 (matvei.org)" )] +[assembly: AssemblyTrademark( "" )] +[assembly: AssemblyCulture( "" )] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible( false )] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid( "e463a25a-d85d-4991-8df0-ed4e4cc7cd09" )] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion( "0.6.1.7" )] +[assembly: AssemblyFileVersion( "0.6.1.7" )] + +[assembly: CLSCompliant( true )] +[assembly: NeutralResourcesLanguage( "en-US" )] \ No newline at end of file diff --git a/ServerCLI/ServerCLI.csproj b/ServerCLI/ServerCLI.csproj new file mode 100644 index 0000000..6971a3f --- /dev/null +++ b/ServerCLI/ServerCLI.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {315FB33C-6C57-4A89-ABFB-C083F2015880} + Exe + Properties + fCraft.ServerCLI + ServerCLI + v3.5 + 512 + fCraft.ServerCLI.Program + Client + fcraft_console.ico + + + true + ..\bin\Debug\ + DEBUG;TRACE + full + AnyCPU + Auto + prompt + + + ..\bin\Release\ + TRACE + true + pdbonly + AnyCPU + Off + prompt + + + + + 3.5 + + + + + + + + + {7FBE7809-6F77-415C-ABEB-A3F627E817B0} + fCraft + + + + + + + + + + + \ No newline at end of file diff --git a/ServerCLI/app.config b/ServerCLI/app.config new file mode 100644 index 0000000..a8afe94 --- /dev/null +++ b/ServerCLI/app.config @@ -0,0 +1,3 @@ + + + diff --git a/ServerCLI/fcraft_console.ico b/ServerCLI/fcraft_console.ico new file mode 100644 index 0000000000000000000000000000000000000000..41628c3802aae66c07bb336c7eec5a1e44007a39 GIT binary patch literal 61798 zcmeI52Y^&Xw)gKCFaYXPQ8De{sw-;-#HG#}4mb=M1{oBU3=)j&bAviK_5IGNsXMoCcTe}gB%rIiZm9ULQ{mRBbLw8t z%kb)Xha7Tv-P38_RuZ_+vfq7Ow3*;ZO3sI_*8LMT^q#2YTK+ z_shKve@YY2JK_;{pXr^_((^7E2X4WP1%{X50TPp^$Jj$*;asm@zka0I!aL=ZQ@o#k z`pL_iJlT8j!3VwE++447=j)Xo>4>UbI{cKox6Lh1`e8@Sp zt{%ww?e0w*H{F=s;_Y11v}w~zva`CVnLg9yLl`y2xT}hvC2lmGj?zS0P{%uKN=iyh z%I4Q!f1NdBTK9YIy1nzwciegle|vG;EuHw=>pO4z6BqpN$-VQ_WlP^1H>Q4l+EsG1 zU!8r6mMr{W;ycG5cg$XU?KOJz=$Br4iHj*yrabh}L*2V~Kj44^1`HT*(*^>_+iVJE++i3#~e-SUwrXJUS1v-vuDq~=9+7`$jHdpuwlchRjZCZS^^NhsBjVC zk2$&_UP~4){@{ZTxWH@T#EEU%wBcgOk|ji^y^ya>f71A|#~phNUW*nj8aIvx>)m_r zy^R_*TDfv%{rdG6E?jugMHhi>|NZwr^uG=P&xe!W&BACMuI}|)KrcAoJ!&S7>ty{Ownl84h#TeoV}>X~Pr>Dsj`y{Bc%mcVY_y!qK@pWV1|fXC+qeJx(@#JD9Qyx^`NjR! z7l)r{ZNXY&#GT(7#AnHpJk?=aRo!t@9NO+_OGDbPr^f%eIW5T zI^rfe+9vv;T6axf+$7fB(ojRLmw8mUx{z%HqT0YI4bY(+d)@vyb-PzEeH4S>Sku>Sk2CN#N{GX*? z&!S^%7`nEhZ|gO^+t9rY{oByN4L#h@R}AGbbg*CP+(zQl&u7UsJ>AgN4Smtj*$utj z(A^F6Fm!lBk2iFAOlHu!eij|y(DMyl-_Z9BonOPz{atv&2%zNxySQn5y4|yk z1G5+i3?qSIDA4>F3=E@zVKh(+UX3d>5-af;6%50|vy2Nf85l|!8A|BbC5#Ov3=SoX z4kh&R63Se{08zpSQNj>W!WdD)AhDfMqJ&}M8ODhM1`5MSVHhe5V})U`FpL(4;leOp z7}7PQWf(FHV}@bSFpL@o{C{QK7{|b|i2*|`V~3t;*PE=Jx{0A<6C;S88ACQPh-_pO z*~BpN2;)c{29g69CF0nhar^lI1{B}ga6j)aXX!uCVyBE7FM?x8yfH@Hf?-@d8D}!F zEwFCh6Omy)j+r|*&wH>(uFhp-&NFaef97eK-j*#}JldLf{q@&-(A%3le!N#$Sm;sj zUW7Rjbs3XdcC?8J(56?E$!~B%lzDn_bc&l(xSw9&O(@Y{jUZSy32ervC1^DbVW z!7U5@`$Es_Gbht?_g$GO_PLU*O2gObRp|BUHPFBJx-Rni^ncjDUsWfkuus2^e)@9? zdlmM{T&4c?$uGZ48}22&x!%z1><%k?E%Ki3C-)7S-`4Z;ZS_|bdiA;Ge)PqMGmUkI z`_VmHyF1T&n)_$lJ=KMz)yVxNz1|(i?5|6gdsh|8{pfKF(mygCY~ID*j~=&n8N@RF!^Or>{WDiX156Ylb!VYjM8)1yGrhZ zc}BN$J8zAoG#{*7XgqJ}_Elsm7i!$pwij<-zi!>e_3OUcu;HtX>nrEKVLb`1U9%eE zELt%Ctyf-r=>B^v_y2IuZm;zF%jV6Sie^U5V?}vFx z?!EWk94p1xq#_UX{CewFn?6u^knSYlQ1nFUPntC8%P+rVZgkvn#}Q@UefKRWD6lRZ znV&3Ju%KQ&=}OGEaAEx5`nl(x!;2>`*Zb@vkGa_NZ4qSFrH02c!ASof|M&+E=bn4+ z@70`q^2s>j^|!zM4fkuWy_QD<&!7A5yN{U>#IfnbrcV^i(xpr3Zak-%PH_vEjT<-0 zWBCA<9ZC&}a0O3K$Te%$0DyUxb#y@z;$OCG8K`KwHhsb$fBf-05}7d`dg!6tl8&W- z#ZO=s3UHxqSmcTDTX#QwdDOakr!D}wZrwVYg-!p(7hfdtZQHhyJGDS`n*y;gvGlRN zanmRKF~=P9!w)|&pCk(#^7-eVC;gjlx`~X)oh-;V5&cO)vd_q{=|fmcA6%e3Q*Z)M zisIs82ySm}`ZwQvGu1tO`0%g4{+ihw!EEU)e)=!{%O=hd^T{WlSo%YDZVAjh{hM#T zk=cjm{qvvy{Lg>>Lp$M`^r@y>Z@raka->|$YPkjyN1r}+e87>Un8Fqn6_GypPd)Wi z!s94&Y$LOC@c;75FVuo<|Fj?6ZTdD%lJyPa1O1md<1e)7{+-di_sCj9}gzTCELJJQd|$!XWFT_h5jK7Be_w`M^k#(FGS=K&>@v*6gga&VrpHuVarr zmRu;vU;0G0ZrMcpA^n`3_E*CdssSw-*}TYw2|ld|{LJ_$0vWk1F0zTktu<-Vgt9a~ z}*-Z0igv69W2fZF1+x3K%Reo^XBKCb58SS z&CVe;(8APGHNQXg)KeNaKJmnp7z`UWJo>Q14y#wcK79fFeR}`**IT#Dm^Kxba`dQ? zBS#G9KZ4`%;Uk6(8$Nv4@L@xT4IMgc$dI8!2M--Gc*x*E{09#jIB4KoZ@o2O0DbR` z*ZIHk`s=U1_Ufzs`}6-FmJ9y=_kR6ec;SV=jvq6EDN5dq52wFB;gbck7tNWqWbVgN z|9KxTnfGyFdH?*v`T0xd=M$!A$-GG;1{6%6^ud@xv!_q`aQyI5Z}sQFRQ$^?t5$wl zwCs~lmlu6nRQg|DRFv$$Y)nGF1UMlqTaq zl!-9>i38_-E7O*ysc8o@jOJ4^z1nK1=L~9j?`pPtNq)6~Ch$Qg!wBWTxa7bJ<-iD4 zt=+I*@@mHrlq1ht_@urtN(MHm+AK_zfr~Oc%g$jQ_9zpjV(Owg?N7`?ikaq|4IdO? zA}DLAhKGxu<+`eDX&&2|Wf`V%Oqr-Yo+2=MK)>vaBBu8%m`P-qgQb-uQTv$ zzPtF#x-yKLVG5{W;N8@pNxj~~ziAj|6nf7yR?Z3YXO5-ed~S|Gji;fy8rhOUqHA@`v-6 zG?>>K*h-ZX2nLtF<^7bqG=fEN3I@TKto|ikht-u|>eAJ}_({FP^C>-q4)C2?*Uru5 z5BI5U68_V`fT}z}(l4cxUw+0v9e-Tjau#d3QCt5~=Ps_uj$lefcB5|ar3S`S+qZ;~ z{2AAUdsKhHCG>&$(ECz4`T5KE!}#m_2|kgxSZ-DMRRhDS@x{;btje|OxFGzdv;)`w zE$^z_tDmMTQ{cRv@mI$`_}B=HtbsYy@RpZ#+^pkgm7`UEcnkwmYhY^C3LbHD^$%}r zU~UcEt%euO&QJ0WmY?yz34BTfMiiOY#RgW_$XEiiYb>{Galr8U@;6CCXfNdz8y*K_ z91^!UeLhGJzx?pQnm?Sd$_Yh&CiI6J*0D$G5{6jg2Jrj$iQL3Jo^|DiOGZw&r3rjy z1V-7wDH}LI#UQdp%PrgT!!T=FGLFPcGwc?mcLKldaG?e^TJwjIHgLL{Km4JAnKp3K z#$`L z+cHNnFx&=?+ram#zsLq*Sq&Vgf!|bj2@mUS;8zXIw?4B3r;JlD;Mzu|9|~q@V#~jU z^3R(Sfl)Sa;zsgy<(ImUYmq_9v(s64U6nz~S@MTzHXDvA!e>immtF9Ov9VCB;M{tM?ROHZ7ez5X{EK1zi(&lL!u=P+{ujgit7QeC zm?eN>)&PoG1o(+nfMVw0XR{8_mxTbsN`PT0z_1VyYvjWpWP>=(MpTkl?4r>LzWvw75b-RqP@oZ5JO9z_1 zJF|$;j#UKZKWK|JYW8&gQzz4bL!<`QDbAt=bjCWp0Z$gw%Fz&@OEfS@lbtkjW7<1j>4Lezd z&FJIoISt0gOR4US3EaCTaPPPy?ir)s_PnEqW@q>CE*S1zfXJ<`rgLC+j_38fV&TF< zuUV^u{@$(3y}joR?ID1hwK_06Qsz#05)t?8p&}y>q_ec8RClxNE7nH4@0DGs?w4~6 zhWGlkRrl=9A@{uwN6gnO>$5{6(c!%lLDj5PM)tOl`~E0gG;8H;j4sy+nO@Hx;{N>V zsPNgBdp$Kg+4^`$nR4}PA>n=Z$A6cdb%u9R`&F$n1MX|Rf6`FedI$B)zTflC{l7MR z*9L1MPyQi0tCQaP2kLj(yO9I4Nk7We?b=}3_wPO<^p7&obJ;i3rpCA*L^|J0doR0n z-$+z=>ppQ^+XhXW2EDBNw6n9@UdZI7Y12!8Lj49)>VEMXi`3coD|LUj%#|q*Dm$g_ z)22-;^KtIq<2r3xSro}b!k4)a=Td`f#DfmEmFef4@x3+Enl|Zp1bQfO%qtDc)Jxpe z%Hw8!{z|FhvQ=L$JBolJu8G&FacdDb@D4cIA(lzGnzFMdg%HoV??BRfbd)H%1?cG9 z-C3RhGVpZ9%6tFNc&7U%1 z%#~Nz=Xs4)VEeYMCyW}xg64t+3qKe)szbXrETZhIx*1$lr?HuK$hmynh(S!O!83XM zXz)N9babkpKsHnGj2}68PsYOn7waam^iQ2WpMLr&E2Aj)bA0m2C#6DF;elk4R^TY> z%F*WE|Nb|NYc^9>VhDo~mP-vX7;cF%9z|`JTIzUgTv=c@BDNp}7Ef(DbW95b_6XGp z->|w3TDk+lWED!y)TvXs?bNB$8*jXU!-x?h?!5C(Tv)D@)Rbtcv>%rGSPej6#1Tae ziL3PC$O=9S-B1I^SYzvj*&-1s4qHaZS~gMW@+m7%M6qDFNF9%ed5j91jEI$pYw1IF zT5ehQw_tGhjh~mS80z{t$Yn={F*9e*WP2qN0b4E`cjxht8mqHx_}~%Dk}0c?J_ehG zrH{pf>O&9+54oftW*w4h;sYL_9VrDBrtxZ`n;`%%hJ!%F;3J zP$`H97>JyZsvt;1-ivtGYI*s-u= zlO2+5Du~WSMx@Enj*!9e*{2Vpd%|GYr}fxlkM-%(2VmSWBBA zAmFi%K7IV!0Vlo~O5{fj;h8^w0rMX_KV)h|Fr+xt#UqbAB4Qz<0$?Ls@^ECs839oB zq`p4<@I!o`e)?&kKK0a7w3ClM`Ur_U+1ZNlpI?*N^6kcUABKMs!ei%$*I(O#0G2+q ze_Q<6BM)?f(K0!*24Vr*U|ipG&po(PFSfrUalsMu_Si)!$0xz7+O-}xVjzRYqQ#4$&y$Zm zn$@x;vTY0!HAsz|**=u*Scj?t6rg|EzEg>iEPbxIvK=sf_+i^ZsjuPSX?a;Ta$b~j zF%Sw=v=+LsrH`GDRXP~T#Wa><$3I;2!Gy8jeg8f5nJ{`7^#z`*ufE#XrbgaqDZ>MZ z7CU6f5QZ8Ah<0~)Wzt7Gb;oP3et-O!?|=Bgr%zVP%fJI=pbx~s@L)o#f(FK%fw6JL ziWT%|god%&PTaO|7(0-nOrtf8!Wi$BD<*6nB*rP=SFbvHGJXS3qDp6||o28Xs zYf#B26!Zj@4~cdKiO@DcVTAf(M$NN;?!bV5)AiTUzo@Ty^X5%`Z~W7J9zXBA^T~~D zC_7m|1<6RW@wNH=O0g8M*+B$L2ir)RHf_Qn?{xbi$?2z^dMdlJAq)MnQKQV`jy;wK z6E$`4!3WWp?L+69D|04Kd>iq?!UYT7A3t{Rz_+^O-t|Dw|8w61J$u}DfA=2u>+!yB z-Fw{Iy$8o`-Fn>9t@}OqcKh=^_ukF_&-dJQ_n&j``g7O2?&_M$zVX~HU32g3()Erz zyWDpB9sE1rcKe??-+BxGKXvYO^DQ@Zy7|VNI$eLmjn{R&{@OqMvBTBZw7;rD+jcpv z+gzUAs&(^o&wJ#*ADS>`IM0ivg@t2=4f<}&#&xT|+_-jC?0-Ypab=XtI-;!PkK=|l z{8#c{zvj#JtKFY#b+ld_zgoYBKN)S>u$I4EuV1t6o6UpYe1*xt^5sQC-+HZRVZnm@ z8H)=(iuvaycJaNqNa??5_6*b@`ajug@QZyuTe|S&$9oR>+f&O67rfNF$C%e&KuhGu z|NUR46Z7ZJ88v+9J7Y(`J!Vuj{Kt+O!JmxuVx(M*9yxN@kU6vS8Fy10)vGO-U4r1D zS>uM9sfCp>Q+XjI{sk#F-b z1q&8tAt*LTW_3>kYm<6rt)bSP9X5n;C9E3CnHo=b+ zI*p(Dk~*s_%R*Vg{W8I}QBnFM@YcORvhXXyW>(@Av*ah~ir@grxB9X8X{TnPBXSLY z@07%0NkYR*dI-XO8n6hW?h+kwcvL<++J3Bn-0s1WbY*X&U|q{L7kKkP05S<$5sHb_oB&?vIu<m&i&P#E;b?O^bPeLC~)IbnU{v3fpy`hlF$3<$6cyZv|jZ1g}DP z#o*{Kn4FP4cn-Vx2W=1!Yib%r$}H9j0aP|`SW48rg|2 zYiwGMApHb~qrby1i}O;aVi4sU+3zUfTsw2vdP=_uV+iB7!Vx9E)*ZS6F>zcPRYiZ7 zzMQ4rQ6|uLlL{)Np}H4M{G=Yj_@%wcURK$!DSLEfFS+a;XPq{t994;5=r8R^+KXUC z$Wy9vW80GRcWueFe<`E9msR5UstC9tSW!0@Ui_;PzohT1^k>_#y5wzU~5LKtQf(x^bwg@Czo{qc3d*-3G-XWf4s7xfXu@I{ZR^c}IaO zJ8S(0We0<{gO)|yT^&>uzqALz5FY>K`73^IoDlj14WcRam-pTZZqYxIeY6rMOn-^z z=q!1;a>P5Qf*)Ogm^9oTW-HQFrHQC8(kGpfJ{Zi~(O>)pzw-;y-{BWJNckk3Sl4b` zoOt->&tIVPfA}v=TX+NskF;Lf1Ns%Z?^g1+dR(exp?;`YXjpk4v9uqnYN5&&I>$ro zsLv+Ev-%DM!Q|RPaQ=fvg3Wse=`Z{jni!gfqrde3WJcziQda4Aaz-`7FRP1-=*6I* z;p21TC!#?OEB+{IsDGH=j_&U4=#SEd&wrt}k%dNpL34vK>vV&NOw&SQXM`>?Hn?^p zb1gBl*IxW&jwxviO{MJ$eZy@yk$ZXV#NiE}9ey|eg#WFF7(NR{4}<1_LDfU|Eh7i? z-$x;7xOt%~2TC7`JDh%1-=h+u`Jxm8{Rb-lh~9(JA59E{YKZQ;M?S0jt)-6TJp^uU z=$`fRJ`16%j6ZHXscP(!zN0@%Bqh>jP$Q{_o&@-Z8^Mq2hQ5y+X|lh69x=DRr$YL! zqrc-{suR27-^cO}D4mobr8lUZ)D!xnc%tn`@T1ER_5J}w;r={8<|<-PLJ@^GeeVUD z2!8*q>N;@!8zq$zr051Uj41k}s-pOj=_~z(_F`o&BkuvBuPCn}&oi`EjEsxuT<9Kq zMCtmD0C|4Oy9#6;D&vymDOScEsV`@Dq^xf2LaD{lAN=TTWTBW~&};!m%NYF9mZT2Q zDKJ457pVvOzrJSxjRk!l0on@wyV+472)vKrVtMbvFTeFRdc z=)UM25WN>IzdOr(%02HHcJfAnCoQ1mcpo{(Rbk6-AHGKJD#>O-EFsKgj_VhlPl zYUTMPIOSP{&V&CR1#uHA{U0q5U2Bl_2B|A&Q3Q#4#{)_b`mPH1t_67~l8hbbUub(-FSsS1;0|M! zIuYFRehR@Yb))ZXD8-I?jgfcW$op7iE-QSo;77@(jQ)bZl>R~d(r%;-au)p3hU8w} zr{Vm>P3lGPJ1h7d-QC%h0qqn2-5Np%=`&IW_s$Pgar8YG@}8XFyFcU`53YU1qrbF& zG;^R~w*TH0X)}V=on1MkEMle23x4-54ygxeN3J}sK3rW$JCb?`)`irclp|OMR|jbC z7)M%Wj-Kdf;{U{;?eO(+7Eg>St#-tGP=B?Luf1Q!(kHII{Lb{je3v1TgA1NGtWE&dh;$A=q=x05Sq*Q)u>UXLEA^?GSY5P z_|d-W<|Zipl%VxvQ2VKe-cJ^aKL*VogX)h#_s5|8W6=9CsQ;9n?=$HB7*v4tj1rJR z3&@}bWY7aLC;}NYfeflZ23;V7GLS(V$e<2n&<8Ro1Q|4f4EjK7Q3^6>1({pK{;9J; zG0319WKaz<=mr^-gACe12K69=eh}ZC$YE}Cxj{w9pd(~Z5-LGUNaqje30;b!P%)Z9 z#i$Auqra2Tq6-wqZ)I_e`#K4?Qmy!*K!hb679FBul!(f#+?V>fFmgtHNUh*On@Fv= zZN#Ef^b1-=#mrsnqF2-k#Ug`dkwLY{phlFy=B3~s3?tVSx9A?}J6O;@GN>OJ^p6Y* zNCpigg9?&C2g#s>WCruD8q|>LF(*D%6p_$G8igv7C?eGgi;|KkDk&CwJ!xHfuB{8at3HTT&3UHtg|0Mr2cIPXyYN&QO$5I=7ILLtc@vA+$lPlECjRTC)3 zUjyJDm;REsB#_8o{G7jpjYq$#@E3Yy3&rF_u7b}R+RsOpUJ3mt{-E?!|I%1xC*n^$ z;ZjYw%rS7K{*51|4riuHe0UQ+eW_un_YXko8j+kAgX zgV>lI_HUddelq`^%|Ai>$5>s8HVw*NcXxeZecldMj!EqQRIdCAGs<~5;B`n$mF{K^ zb{r>iQy@)&GzHQWs1ypU`0TU3eIDE8 z{zzhM@j3$AKb9ud>5`^@by7PvuL{vWmIsMV zFg8rAODI+_K+r3ZdoWBIe~=nnr$V%(QBqH&O6ecMndmkaZLCY8xU~&`>d0-0()i=n zY=y`e3~aCCrWx0<(&=w=2||jSZ_qIqxR&6rP$^>Zr(TLGqW|uJKgCi6>7Qa08;oO> z(mzEowH$xaQpDm<8pQ_SSPk?~3Z_=!-vKa*qWDBE=c3Z-p9oAMcd(V?RSt6NmBt?; zQ6gd{a<2yZ2eVFeT}~;2Q9{A&ZPEa7V_C;g`ze|?lZ_AT=L$v%rl06K7@q4CgV|WY zz@c=K-iGimU+^)?@$3WRld0@FiA_J4SMWM%MpYCb7C(yr>;t0|>{ekTX)xa)V0#_( zt&!`v_$h)d1)CGlV`KY(#a0XPFUg-4T)+KvVZH_LAMLAB*$E<>B4SL9&^(ywXNwDx z;%5s9rI3vzF(#N)ja;w$VtFd~0UsAXJA~K-%-*yb0Z*oDKVP;p_}S6JMjrG(8M4^H z!e$%xnpJ06DukbXR2bQX6l46?2D1-|;;?gvjcDjRQ9?Fz%jEB0Nn z6^tEMYzCuf{=235mNHxNs6_W3$8O%7)=dN8_>W z#}Xgp5Z^bH0{UZY71U<@G#kMCh9dawr@ ze!u^w(*A3f{$H*6qMY$3jlZhL&-Gt^S}I?eGD@!{}0!Vt2=iN`^UR?VH2I>#VOCN zChFhe375gSmv;`w3E!6&H=bWjmfzv2NEs4^P2}$KNOVq@zvlXXxK2{3!(?HS`G@nY z4fXFZrGh%ZpPf4Gj4-N);f zI}*pG6OQZLck~>u|FhFozqKsiE)1s|&Oh0GIKJE`Gx6d$w{TqNzN6=OM@7w!;;OEAiOSDb2mS6ATVV9taPr;OFs4Mb2#2f9_vMveXyE7|-x8DW zfXVm1TzPhf^0Ub|M*hi8UoE#QzdN(pNZr}#q2EE<9m>xp;R=ky747hRv%{-ys#6$hQmSTb%5H*6&!#H!N$ay~Ha&JEaqje=-I-D}6v{kV^U4 zyXb$5lC7}ie%DmK3o0~XE45nrHmm!NBO9vqyP5JlmT%F@w^*fJ$#)#X{Y~!KP3?b& z+I_QD+{908-14vfJYo4=-@@ao^-*C$|56(cpJ5 z-FH%h-x+0Jw*ReEM=$wSr+hC}zBej;P`;rqG;`nO-kI_T+rQ92EW5U&zC-HTztq3f zyOf)~!%_ox`CZwi{LTs;cJ=yq^&UJ+8<2T(uzBlZ?FUQU{?7}x}!^h?ucHygKAHEUGW_-hLe8XP+DzWjN|KheM*|7fG z?of7K2VlgyvXplpEI%&_P<~L{_7uxo282&fn=}Q|6i8DbO@TB8_DBjOTEk4{9>lUCG!9OR{F!u literal 0 HcmV?d00001 diff --git a/ServerGUI/ConsoleBox.cs b/ServerGUI/ConsoleBox.cs new file mode 100644 index 0000000..b55cfc4 --- /dev/null +++ b/ServerGUI/ConsoleBox.cs @@ -0,0 +1,64 @@ +// Copyright 2009-2012 Matvei Stefarov +using System.Collections.Generic; +using System.Windows.Forms; +using System; + +namespace fCraft.ServerGUI { + sealed class ConsoleBox : TextBox { + const int WM_KEYDOWN = 0x100; + const int WM_SYSKEYDOWN = 0x104; + public event Action OnCommand; + readonly List log = new List(); + int logPointer; + + protected override bool ProcessCmdKey( ref Message msg, Keys keyData ) { + if( !Enabled ) return base.ProcessCmdKey( ref msg, keyData ); + switch( keyData ) { + case Keys.Up: + if( msg.Msg == WM_SYSKEYDOWN || msg.Msg == WM_KEYDOWN ) { + if( log.Count == 0 ) return true; + if( logPointer == -1 ) { + logPointer = log.Count - 1; + } else if( logPointer > 0 ) { + logPointer--; + } + Text = log[logPointer]; + SelectAll(); + } + return true; + + case Keys.Down: + if( msg.Msg == WM_SYSKEYDOWN || msg.Msg == WM_KEYDOWN ) { + if( log.Count == 0 || logPointer == -1 ) return true; + if( logPointer < log.Count - 1 ) { + logPointer++; + } + Text = log[logPointer]; + SelectAll(); + } + return true; + + case Keys.Enter: + if( msg.Msg == WM_SYSKEYDOWN || msg.Msg == WM_KEYDOWN ) { + if( Text.Trim().Length > 0 ) { + log.Add( Text ); + if( log.Count > 100 ) log.RemoveAt( 0 ); + logPointer = -1; + if( OnCommand != null ) OnCommand(); + } + } + return true; + + case Keys.Escape: + if( msg.Msg == WM_SYSKEYDOWN || msg.Msg == WM_KEYDOWN ) { + logPointer = log.Count; + Text = ""; + } + return base.ProcessCmdKey( ref msg, keyData ); + + default: + return base.ProcessCmdKey( ref msg, keyData ); + } + } + } +} \ No newline at end of file diff --git a/ServerGUI/MainForm.Designer.cs b/ServerGUI/MainForm.Designer.cs new file mode 100644 index 0000000..63a9783 --- /dev/null +++ b/ServerGUI/MainForm.Designer.cs @@ -0,0 +1,150 @@ +namespace fCraft.ServerGUI { + partial class MainForm { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && ( components != null ) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof( MainForm ) ); + this.logBox = new System.Windows.Forms.TextBox(); + this.uriDisplay = new System.Windows.Forms.TextBox(); + this.URLLabel = new System.Windows.Forms.Label(); + this.playerList = new System.Windows.Forms.ListBox(); + this.playerListLabel = new System.Windows.Forms.Label(); + this.bPlay = new System.Windows.Forms.Button(); + this.console = new fCraft.ServerGUI.ConsoleBox(); + this.SuspendLayout(); + // + // logBox + // + this.logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.logBox.Font = new System.Drawing.Font( "Lucida Console", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.logBox.Location = new System.Drawing.Point( 12, 38 ); + this.logBox.Multiline = true; + this.logBox.Name = "logBox"; + this.logBox.ReadOnly = true; + this.logBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.logBox.Size = new System.Drawing.Size( 610, 388 ); + this.logBox.TabIndex = 3; + // + // uriDisplay + // + this.uriDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.uriDisplay.Enabled = false; + this.uriDisplay.Location = new System.Drawing.Point( 95, 12 ); + this.uriDisplay.Name = "uriDisplay"; + this.uriDisplay.ReadOnly = true; + this.uriDisplay.Size = new System.Drawing.Size( 473, 20 ); + this.uriDisplay.TabIndex = 1; + this.uriDisplay.Text = "Waiting for first heartbeat..."; + this.uriDisplay.WordWrap = false; + // + // URLLabel + // + this.URLLabel.AutoSize = true; + this.URLLabel.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.URLLabel.Location = new System.Drawing.Point( 12, 15 ); + this.URLLabel.Name = "URLLabel"; + this.URLLabel.Size = new System.Drawing.Size( 77, 13 ); + this.URLLabel.TabIndex = 5; + this.URLLabel.Text = "Server URL:"; + // + // playerList + // + this.playerList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.playerList.FormattingEnabled = true; + this.playerList.IntegralHeight = false; + this.playerList.Location = new System.Drawing.Point( 628, 38 ); + this.playerList.Name = "playerList"; + this.playerList.Size = new System.Drawing.Size( 144, 388 ); + this.playerList.TabIndex = 4; + // + // playerListLabel + // + this.playerListLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.playerListLabel.AutoSize = true; + this.playerListLabel.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.playerListLabel.Location = new System.Drawing.Point( 710, 15 ); + this.playerListLabel.Name = "playerListLabel"; + this.playerListLabel.Size = new System.Drawing.Size( 62, 13 ); + this.playerListLabel.TabIndex = 6; + this.playerListLabel.Text = "Player list"; + // + // bPlay + // + this.bPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bPlay.Enabled = false; + this.bPlay.Location = new System.Drawing.Point( 574, 10 ); + this.bPlay.Name = "bPlay"; + this.bPlay.Size = new System.Drawing.Size( 48, 23 ); + this.bPlay.TabIndex = 2; + this.bPlay.Text = "Play"; + this.bPlay.UseVisualStyleBackColor = true; + this.bPlay.Click += new System.EventHandler( this.bPlay_Click ); + // + // console + // + this.console.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.console.Enabled = false; + this.console.Location = new System.Drawing.Point( 13, 433 ); + this.console.Name = "console"; + this.console.Size = new System.Drawing.Size( 759, 20 ); + this.console.TabIndex = 0; + this.console.Text = "Please wait, starting server..."; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size( 784, 464 ); + this.Controls.Add( this.bPlay ); + this.Controls.Add( this.console ); + this.Controls.Add( this.playerListLabel ); + this.Controls.Add( this.playerList ); + this.Controls.Add( this.URLLabel ); + this.Controls.Add( this.uriDisplay ); + this.Controls.Add( this.logBox ); + this.Icon = ((System.Drawing.Icon)(resources.GetObject( "$this.Icon" ))); + this.MinimumSize = new System.Drawing.Size( 500, 150 ); + this.Name = "MainForm"; + this.Text = "fCraft"; + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox logBox; + private System.Windows.Forms.TextBox uriDisplay; + private System.Windows.Forms.Label URLLabel; + private System.Windows.Forms.ListBox playerList; + private System.Windows.Forms.Label playerListLabel; + private ConsoleBox console; + private System.Windows.Forms.Button bPlay; + } +} + diff --git a/ServerGUI/MainForm.cs b/ServerGUI/MainForm.cs new file mode 100644 index 0000000..a8cf5a6 --- /dev/null +++ b/ServerGUI/MainForm.cs @@ -0,0 +1,255 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Windows.Forms; +using fCraft.Events; +using fCraft.GUI; +using System.Threading; + +namespace fCraft.ServerGUI { + + public sealed partial class MainForm : Form { + volatile bool shutdownPending, startupComplete, shutdownComplete; + const int MaxLinesInLog = 2000; + + public MainForm() { + InitializeComponent(); + Shown += StartUp; + console.OnCommand += console_Enter; + } + + + #region Startup + Thread startupThread; + + void StartUp( object sender, EventArgs a ) { + Logger.Logged += OnLogged; + Heartbeat.UriChanged += OnHeartbeatUriChanged; + Server.PlayerListChanged += OnPlayerListChanged; + Server.ShutdownEnded += OnServerShutdownEnded; + Text = "fCraft " + Updater.CurrentRelease.VersionString + " - starting..."; + startupThread = new Thread( StartupThread ); + startupThread.Name = "fCraft ServerGUI Startup"; + startupThread.Start(); + } + + + void StartupThread() { +#if !DEBUG + try { +#endif + Server.InitLibrary( Environment.GetCommandLineArgs() ); + if( shutdownPending ) return; + + Server.InitServer(); + if( shutdownPending ) return; + + BeginInvoke( (Action)OnInitSuccess ); + + // check for updates + UpdaterMode updaterMode = ConfigKey.UpdaterMode.GetEnum(); + if( updaterMode != UpdaterMode.Disabled ) { + UpdaterResult update = Updater.CheckForUpdates(); + if( shutdownPending ) return; + if( update.UpdateAvailable ) { + if( updaterMode == UpdaterMode.Notify ) { + String updateMsg = String.Format( "An fCraft update is available! Visit www.fCraft.net to download. " + + "Local version: {0}. Latest available version: {1}.", + Updater.CurrentRelease.VersionString, + update.LatestRelease.VersionString ); + Logger.LogToConsole( updateMsg ); + } else { + new UpdateWindow( update ).ShowDialog(); + } + } + } + + // set process priority + if( !ConfigKey.ProcessPriority.IsBlank() ) { + try { + Process.GetCurrentProcess().PriorityClass = ConfigKey.ProcessPriority.GetEnum(); + } catch( Exception ) { + Logger.Log( LogType.Warning, + "MainForm.StartServer: Could not set process priority, using defaults." ); + } + } + + if( shutdownPending ) return; + if( Server.StartServer() ) { + startupComplete = true; + BeginInvoke( (Action)OnStartupSuccess ); + } else { + BeginInvoke( (Action)OnStartupFailure ); + } +#if !DEBUG + } catch( Exception ex ) { + Logger.LogAndReportCrash( "Unhandled exception in ServerGUI.StartUp", "ServerGUI", ex, true ); + Shutdown( ShutdownReason.Crashed, Server.HasArg( ArgKey.ExitOnCrash ) ); + } +#endif + } + + + void OnInitSuccess() { + Text = "fCraft " + Updater.CurrentRelease.VersionString + " - " + ConfigKey.ServerName.GetString(); + } + + + void OnStartupSuccess() { + if( !ConfigKey.HeartbeatEnabled.Enabled() ) { + uriDisplay.Text = "Heartbeat disabled. See externalurl.txt"; + } + console.Enabled = true; + console.Text = ""; + } + + + void OnStartupFailure() { + Shutdown( ShutdownReason.FailedToStart, Server.HasArg( ArgKey.ExitOnCrash ) ); + } + + #endregion + + + #region Shutdown + + protected override void OnFormClosing( FormClosingEventArgs e ) { + if( startupThread != null && !shutdownComplete ) { + Shutdown( ShutdownReason.ProcessClosing, true ); + e.Cancel = true; + } else { + base.OnFormClosing( e ); + } + } + + + void Shutdown( ShutdownReason reason, bool quit ) { + if( shutdownPending ) return; + shutdownPending = true; + console.Enabled = false; + console.Text = "Shutting down..."; + Text = "fCraft " + Updater.CurrentRelease.VersionString + " - shutting down..."; + uriDisplay.Enabled = false; + if( !startupComplete ) { + startupThread.Join(); + } + Server.Shutdown( new ShutdownParams( reason, TimeSpan.Zero, quit, false ), false ); + } + + + void OnServerShutdownEnded( object sender, ShutdownEventArgs e ) { + try { + BeginInvoke( (Action)delegate { + shutdownComplete = true; + switch( e.ShutdownParams.Reason ) { + case ShutdownReason.FailedToInitialize: + case ShutdownReason.FailedToStart: + case ShutdownReason.Crashed: + if( Server.HasArg( ArgKey.ExitOnCrash ) ) { + Application.Exit(); + } + break; + default: + Application.Exit(); + break; + } + } ); + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + } + + #endregion + + + public void OnLogged( object sender, LogEventArgs e ) { + if( !e.WriteToConsole ) return; + try { + if( shutdownComplete ) return; + if( logBox.InvokeRequired ) { + BeginInvoke( (EventHandler)OnLogged, sender, e ); + } else { + logBox.AppendText( e.Message + Environment.NewLine ); + if( logBox.Lines.Length > MaxLinesInLog ) { + logBox.Text = "----- cut off, see fCraft.log for complete log -----" + + Environment.NewLine + + logBox.Text.Substring( logBox.GetFirstCharIndexFromLine( 50 ) ); + } + logBox.SelectionStart = logBox.Text.Length; + logBox.ScrollToCaret(); + if( !Server.IsRunning || shutdownPending ) logBox.Refresh(); + } + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + } + + + public void OnHeartbeatUriChanged( object sender, UriChangedEventArgs e ) { + try { + if( shutdownPending ) return; + if( uriDisplay.InvokeRequired ) { + BeginInvoke( (EventHandler)OnHeartbeatUriChanged, + sender, e ); + } else { + uriDisplay.Text = e.NewUri.ToString(); + uriDisplay.Enabled = true; + bPlay.Enabled = true; + } + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + } + + + public void OnPlayerListChanged( object sender, EventArgs e ) { + try { + if( shutdownPending ) return; + if( playerList.InvokeRequired ) { + BeginInvoke( (EventHandler)OnPlayerListChanged, null, EventArgs.Empty ); + } else { + playerList.Items.Clear(); + Player[] playerListCache = Server.Players.OrderBy( p => p.Info.Rank.Index ).ToArray(); + foreach( Player player in playerListCache ) { + playerList.Items.Add( player.Info.Rank.Name + " - " + player.Name ); + } + } + } catch( ObjectDisposedException ) { + } catch( InvalidOperationException ) { } + } + + + private void console_Enter() { + string[] separator = { Environment.NewLine }; + string[] lines = console.Text.Trim().Split( separator, StringSplitOptions.RemoveEmptyEntries ); + foreach( string line in lines ) { +#if !DEBUG + try { +#endif + if( line.Equals( "/Clear", StringComparison.OrdinalIgnoreCase ) ) { + logBox.Clear(); + } else if( line.Equals( "/credits", StringComparison.OrdinalIgnoreCase ) ) { + new AboutWindow().Show(); + } else { + Player.Console.ParseMessage( line, true ); + } +#if !DEBUG + } catch( Exception ex ) { + Logger.LogToConsole( "Error occured while trying to execute last console command: " ); + Logger.LogToConsole( ex.GetType().Name + ": " + ex.Message ); + Logger.LogAndReportCrash( "Exception executing command from console", "ServerGUI", ex, false ); + } +#endif + } + console.Text = ""; + } + + + private void bPlay_Click( object sender, EventArgs e ) { + try { + Process.Start( uriDisplay.Text ); + } catch( Exception ) { + MessageBox.Show( "Could not open server URL. Please copy/paste it manually." ); + } + } + } +} \ No newline at end of file diff --git a/ServerGUI/MainForm.resx b/ServerGUI/MainForm.resx new file mode 100644 index 0000000..4e40898 --- /dev/null +++ b/ServerGUI/MainForm.resx @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB+gIJ+gIJ+gIJ+gIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD4j/gAP+cAAOwT/wA/1wAA++3+//+vAADX//v//38AAO/7/v/+/wAA//7///+/AACP+/3//X8AAP/+ + /v/6/wAA//3/f/f/AAD//3+37f8AAP/9/9/7/wAA//9/7/f/AAD//v//r/8AAP////Zf/wAA//7//n// + AAD////3r/8AAP///+3X/wAA//6/n+v/AAD+AWBz9P8AAP8AoA/5fwAA///f//5/AAD///////8AAP// + 3////wAA///f////AAD//7////8AAP//7////wAA//+/////AAD//+////8AAP//3////wAA///f/H// + AAD///f///8AAP////3//wAA///r////AAD///T3//8AAP//+x3//wAA///8B///AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAMAAAAGAAAAABACAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAfX6AAn1+gAJ9foACfX6AAn1+gAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAfX6AAn1+gAJ9foACfX6AAn1+gAJ9foACfX6AAn1+gAJ9foACfX6AAn1+gAJ9foACfX6AAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKBfwKLiH0BAAAAAAAAAAAAAAAAAAAAAAAAAACLh34Cfn6AAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAISDfwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgoF/AYuIfQOJhn4CAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoR9AQAAAAAAAAAAUF6IHk5chS5OXIQwTlyDK05cgxcAAAAAAAAAAIeE + fgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATlqIHU9chSBOXIUfTlyFIE5chSBOXIUgTlyFIE5c + hSBOXIUgTlyFH05chR9PXYMhTliEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + fgIAAAAAYYvKCVKR7n89kf+9PI3/1TSJ/+Exh//eMIb/yiiD/7A2e99IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB+gIEBAAAAAAAAAAAAAAAAa4avHWmGsChnhbAkZYOwDwAAAAAAAAAAiIB0AQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJ/ewMAAAAAAAAAAAAAAAAAAAAAAAAAAId/ + dAN/f38CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfoCCAn6A + ggJ+gIICfoCCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA+I/4AD/nAADsE/8AP9cAAPvt + /v//rwAA1//7//9/AADv+/7//v8AAP/+////vwAAj/v9//1/AAD//v7/+v8AAP/9/3/3/wAA//9/t+3/ + AAD//f/f+/8AAP//f+/3/wAA//7//6//AAD////2X/8AAP/+//5//wAA////96//AAD////t1/8AAP/+ + v5/r/wAA/gFgc/T/AAD/AKAP+X8AAP//3//+fwAA////////AAD//9////8AAP//3////wAA//+///// + AAD//+////8AAP//v////wAA///v////AAD//9////8AAP//3/x//wAA///3////AAD////9//8AAP// + 6////wAA///09///AAD///sd//8AAP///Af//wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAAKAAAAEAAAACAAAAAAQAEAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAo + oAD4+PgAAFjwAAhQ2AAAQMAAMHDwAAA40AA4aNgAGGjwAAAwwAAAQNgAKFjYAABg+AAAMLAAOID4AAhA + 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAABp + AAAAAAAAAAAAAAAAAACQAAAAAADd2ZmZndAAAAAAAP9pmZmZmZmZmQAAAAAACZkAAAAACZmWb7v/aQAA + AAAAu/Zmb///////aQAAAAAJlpnQAACZZm+7d3e/bQAAAAC79m+7d3d3d3f2AAAAAJmWZmkABmZm/7d3 + d3f20AAAALv2Zvt3d3d3d78AAAAJlmZvYAAGZmZvAAAAd3+QAAAAu/aQ0AAAAAAAAAAAAJlmZvYAAAZm + ZmAAAAAHc2kAAAAHv20AAAAAAAAAAAAJmWZvYAAA//ZpkAAAAAd7+QAAAAd/aQAAAAAAAAAAAGlmZmYA + AACj9pmQAAAAAHf2kAAAB3v5AAAAAAAAAAAAlmZmZgAAALuzZgAAAAAAu6aQAAAAdz+QAAAAAAAAAAmW + Zv/wAAAAd3c2AAAAAAC3v5AAAAB3c2kAAAAAAAAAmWZv/wAAAAAHd7AAAAAAAAe6bQAAAAd3ppAAAAAA + AAmWZvrwAAAAAAAAAAAAAAAAB7ppAAAAAHdzaQAAAAAAmWZv+gAAAAAAAAAAAAAAAAALs6ndAAAAB3c2 + kAAAAAZmaqqgAAAAAAAAAAAAAAAAAAuzqdAAAAAAd3OpAAAABmaqqqAAAAAAAAAAAAAAAAAAC7Om0AAA + AAAHdzqQAABm+qqqAAAAAAAAAAAAAAAAAAAAe6bQAAAAAAB3c/kABm+qqqAAAAAAAAAAAAAAAAAAAAB7 + P5DQAAAAAAd7ptCW+qqqAAAAAAAAAAAAAAAAAAAAALs6ndAAAAAAAHc6kJaqqqAAAAAAAAAAAAAAAAAA + AAAAuzpJ0AAAAAAAezpJb6pJAAAAAAAAAAAAAAAAAAAAAAC7Ok3QAAAAAAAzOqozqpAAAAAAAAAAAAAA + AAAAAAAAAAWzTQAAAAAAAKqqo7WzQAAAAAAAAAAAAAAAAAAAAAAAAzqpAAAAAADZSqqqNVU0AAAAAAAA + AAAAAAAACQAAAADUqqQAAAAAAAn6qqow5VP9AAAAAAAAAAAAAAA6Td3d3ZSqr03d3d2UT6qqqgAOVTqQ + AAAAAAAAAAAAAIiv////qqqqqv+qqqqqooI6AADlWKQAAAAAAAAAAAAAhYiIiIiIIiIiKIiIiIiIiIAA + AAXlUk0AAAAAAAAAAABVVVVVVVVYIiiFVVVVVVWIAAAAAF5VKkRAAAAAAAAAAA7lVVVVXuWCMoVVVVVV + VQAAAAAABe5YIgAAAAAAAAAAAAAAAAAAVYL0MAAAAAAAAAAAAAAADuWAAAAAAAAAAAAAAAAAAABYgvRA + AAAAAAAAAAAAAAAA5QAAAAAAAAAAAAAAAAAAAFWCpEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BVw0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFWDRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAVYL0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIgjRAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAFWCNEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYw0QAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAOWCREAAAAAAAEREQAAAAAAAAAAAAAAAAAAAAAAAAAAAVYI0QAAAAAAMNERAAAAA + AAAAAAAAAAAAAAAAAAAAAABVjDRAAAAAAAjDM0AAAAAAAAAAAAAAAAAAAAAAAAAAAA5YNEQAAAAADMwi + MAAAAAAAAAAAAAAAAAAAAAAAAAAADljDRAAAAADMzCMwAAAAAAAAAAAAAAAAAAAAAAAAAAAO5YNEQAAA + ACzMIzAAAAAAAAAAAAAAAAAAAAAAAAAAAADujDREAAAzMswiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7o + w0REREMszMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu6MM0RDM8zMwAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA7ujMIyLMyIzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7uiIiIiIiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAADu7u7u7liAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u7u4AAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP////////////////////////////////////////////////////////////////wA + /+AAA/+P+AA/wAAD/wfwAB/AAAP+A+AAD8AAA/4DwAAHwAAD/AOAAAfAAAP4B4D8A8B///APgf4D4D// + 4B8B/gHgP//APwH/AeAf/8A/A/8B8A//gH8D/wHwB/8A/4f/gfgD/gH///+A/AH8A////4D+APgH//// + gP8AeAf///+A/4AwD////8D/wCAf////wH/gAD/////Af/AAf////8B/8AD/////wH/wAH/////gf/AA + P////+B/wAAf//+AAAAAEAf//wAAAAA4A///AAAAADwB//8AAAAAfgD//wAAAAD/AH//gAAAA/+A//// + 8B///+H////wH///8/////Af////////+B/////////4H/////////gP////////+A/////////4D/// + //////wP/////////Af/B//////8B/4H//////wH/gf//////gP+B//////+A/wH//////4B/Af///// + /wDwD///////AAAP//////+AAB///////8AAH///////4AA////////wAH////////wB//////////// + ////////////////////////////////////////////////////////KAAAAEAAAACAAAAAAQAYAAAA + AAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqpQAnmwAn + mgAlkwAlkgAlkAAkjgAkjQAkjQAlkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArqAAn + 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 + AAAAAAAAAAAAAAAAADd68ihx8g9g8ABR4ABGxABAswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADR58ypz8hVl8QBU5wBKzABCtwAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC118ylz8xdn8gBX7gBO1wBGwABHwwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy9CVx9BZn8wBZ + 8gBR3ABJxgBIxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACl19ix39hts9QVe9ABS3QBKxQBHvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAADd/9yZ09gxk9QBU4ABKxQBDtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmB+C16+BZr9wBZ6wBOzwBGugBKwwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMygBGuABFtQBDsgBFtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADF9+St5+Bdt+ABd9ABU3ABMyABM + yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVh9wBW4wBNyQBGuABGuQBEswAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACx7+TB9+R5y + +Qdj+ABW4ABLwwBGtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJq+ANh+ABY5QBS1QBQ0ABN + yQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAD6H+i99+hZu+gBY5ABNyABGtwBKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVj+Qlm + +QFh+QBf9wBa6ABY5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAADqE+jJ/+h1y+gBg+ABU2QBKwABGtwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAVj+Qlm+Qpm+QBg+ABc7wBX4QBX4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOB+zuG+y19+xVu+gBZ5ABNxgBEsABEsAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABd8ARj+gRj+gJi+gBb6gBW3wBW3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+J/DaD/CJ3/AJj+wBW + 3QBLwABErgBErQAAAAAAAAAAAAAAAABQzQBT1QBZ5ABe8QBg9wJj+wBf8gBd7wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADeF + /T6J/TSD/Rx0/ABi+QBX3ABLwABFsABCpwBDqgBHtABLwABNxABS0QBb6ABg9QJk/ABi+QBg9QBg9QAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAESN/kSN/jSE/Rt0/QBk/ABa5ABU1ABOxgBMvwBNwwBSzwBV1wBb5wBh9QJl/Qhp + /QJl/QBi9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEeP/kWO/jaF/R52/Qxr/QBi+QBf7wBb5wBc6ABf8QBi + +QFl/Qhp/RJv/RZx/Qxr/QRm/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEiQ/kmR/j6K/jCC/iN6/h13 + /hRx/hJw/hNw/hdz/ht1/h94/h53/h13/hBv/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqS + /lGW/kmR/kKN/j2K/jaG/jSE/jOE/jOE/jSE/il+/iN6/h94/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAESP/0iR/0iR/0WP/0KO/z+M/zmI/ziI/zCD/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP////////////////////////////////////////////////////////////////// + /////////wP/8AAH///8AP/gAAf/j/gAP/AAB/+P8Hgf8AAP/w/h/h/x///+H+P/D/D///w/w/+H8P// + +H/H/4f4///w/8f/x/h///D/x//H+D//4f///8P8H//D////w/4P/4f////D/wf/D////+P/g/4P//// + 4//B/B/////h/+D8P////+H/8Ph/////4f/4cP/////x//hh//////H//AH/////8f/8Af/////w//wg + //////D/+Hh/////8P/wfD///8AAAAD+H///wAAAAP8H///AAAAD/4P//8AAAB//4f////h////z//// + /H/////////8f/////////w//////////D/////////8P/////////4//////////j/////////+P/// + //////4f/////////h//////////H/////////8f/4///////w//D///////j/8f//////+H/x////// + /8P+H///////wfw////////gAD////////AAf///////+AH/////////B/////////////////////// + ////////////////////////////////////////////////////////KAAAAEAAAACAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqpQMAJ5s0ACeabgAlk4wAJZKXACWQlwAkjowAJI10ACSNQAAl + kgkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArqDQAJ5huACWSdAAl + kXQAJZF0ACWRdAAlkXQAJZF0ACWRdAAlkXQAJZF0ACWRdAAlkXQAJZF0ACWRdAAlkXQAJI50ACOLTQAl + kgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqpwIAJpYaACaVAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtsToAK6nrACmi/gApov8AKaP/ACmj/wAp + ov8AKaD/ACea/wAmlf4AJI72ACOJbgAmlAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU4 + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgZOoCF17p6AJP5/8ASNb/AEjU/wBI1P8ASNT/AEjU/wBI + 1P8ASNT/AEra/wBM4v8ATub/AE7n/wBO5/8ATub/AEvf/wBJ2f8ASNf/AEjX/wBJ2P8ASdj/AEnY/wBJ + 2f8AStr/AEzh/wBN5f8ATuf/Ak/n/wlU6P8UXOn/DVfo/wNQ58QATeQBAAAAAAAAAAAAAAAAAAAAAEmB + 7ilGf+7zOHXs/x5j6v8AS9//AEHB/wA6rP8ANp/hADeiDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI2frAihr7OghZuv/FF3q/xNc + 6v8TXOr/E1zq/xNc6v8TXOr/E1zq/xNc6v8RW+r/DFfp/wRS6P8AT+j/AE/o/wBP6P8CUOj/CVXp/xFb + 6v8TXOr/E1zq/xNc6v8TXOr/E1zq/xRd6v8XX+r/HWPr/yJm6/8gZev/HmTr/hxi67oQWukCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAARn/uD0iB7uA9ee7/KWvs/wlV6f8ARcz/AD2y/wA4pPQAOKQSAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFy + 7QE+e+7HPXru/zZ17v80dO3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/NXTu/zBx7f8eZez/CVbq/wBQ + 6f8FU+n/EFvq/ydr7P8ycu3/NHTt/zR07f80dO3/NHTt/zR07f80dO3/M3Pt/zZ17v43du78KWzt5SJn + 7EYeZewBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFf+8DSYLvtz977v4wce3/DVnq/wBK + 2P8ARMX/AETFwQBExwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASYPwA0uE8GdEf/B6Qn7vekJ+73pCfu96Qn7vekJ+73pCfu96Qn7vekiC + 8HpJg/DKNnbu/xdh7P8AUer/AFDn/wJS6vciaO2XOHfvekJ+73pCfu96Qn7vekJ+73pCfu96QX3vejp5 + 72E4d+9GOXjvGi9x7gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF9 + 7wFKg/CGRYDw/TV17v8XYez/BVTq/QJS6k0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPXzxGjBz8PwYY+7/AFPs/wBM1/8ASM33AFDlDAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEyG8lpFgvH5K3Dv+xRg7XQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClv8AcnbvD0GGTv/wJV7f8ATNf/AETA/QBG + xCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ4HyDzR38RYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAscvEBMXbx2SFr + 8P8JW+//AE7Z/wBFwP8AQbdGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADd68pEocfL/D2Dw/wBR4P8ARsT/AECzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0efNTKnPy/xVl8f8AVOf/AErM/wBCt9AAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALXXzKSlz8/4XZ/L/AFfu/wBO + 1/8ARsDzAEfDBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZy + 9A8lcfT6Fmfz/wBZ8v8AUdz/AEnG/ABIxBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApdfYDLHf27xts9f8FXvT/AFLd/wBKxf4AR706AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADd/98EmdPb/DGT1/wBU4P8ASsX/AEO0dAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5gfhnLXr4/xZr + 9/8AWev/AE7P/wBGutAASsMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAATMoPAEa4OgBFtUAAQ7IuAEW1AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAESN/hJEjf73NIT9/xt0/f8AZPz/AFrk/wBU1P8ATsb/AEy//wBNw/8AUs//AFXX/wBb + 5/8AYfX/AmX9/whp/f8CZf3/AGL3qgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR4/+LkWO/vo2hf3/Hnb9/wxr/f8AYvn/AF/v/wBb + 5/8AXOj/AF/x/wBi+f8BZf3/CGn9/xJv/f8Wcf3/DGv94QRm/QMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIkP4pSZH+8T6K + /v8wgv7/I3r+/x13/v8Ucf7/EnD+/xNw/v8Xc/7/G3X+/x94/v8ed/7+HXf+0hBv/gcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEqS/gdRlv6ASZH+9kKN/v49iv7/Nob+/zSE/v8zhP7/M4T+/zSE/v4pfv7vI3r+Wh94 + /gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAESP/wlIkf86SJH/Z0WP/4BCjv+GP4z/ejmI + /1o4iP8pMIP/BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////////////////////// + //////////////////////////////8D//AAB////AD/4AAH/4/4AD/wAAf/j/B4H/AAD/8P4f4f8f// + /h/j/w/w///8P8P/h/D///h/x/+H+P//8P/H/8f4f//w/8f/x/g//+H////D/B//w////8P+D/+H//// + w/8H/w/////j/4P+D////+P/wfwf////4f/g/D/////h//D4f////+H/+HD/////8f/4Yf/////x//wB + //////H//AH/////8P/8IP/////w//h4f/////D/8Hw////AAAAA/h///8AAAAD/B///wAAAA/+D///A + AAAf/+H////4f///8/////x//////////H/////////8P/////////w//////////D/////////+P/// + //////4//////////j/////////+H/////////4f/////////x//////////H/+P//////8P/w////// + /4//H///////h/8f///////D/h///////8H8P///////4AA////////wAH////////gB/////////wf/ + /////////////////////////////////////////////////////////////////////////////w== + + + \ No newline at end of file diff --git a/ServerGUI/Program.cs b/ServerGUI/Program.cs new file mode 100644 index 0000000..413034a --- /dev/null +++ b/ServerGUI/Program.cs @@ -0,0 +1,44 @@ +/* + * Copyright 2009-2012 Matvei Stefarov + * + * 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. + * + */ +using System; +using System.Windows.Forms; + +namespace fCraft.ServerGUI { + static class Program { + + [STAThread] + static void Main() { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault( false ); +#if DEBUG + Application.Run( new MainForm() ); +#else + try { + Application.Run( new MainForm() ); + } catch( Exception ex ) { + Logger.LogAndReportCrash( "Unhandled exception in ServerGUI", "ServerGUI", ex, true ); + } +#endif + } + } +} \ No newline at end of file diff --git a/ServerGUI/Properties/AssemblyInfo.cs b/ServerGUI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1761f68 --- /dev/null +++ b/ServerGUI/Properties/AssemblyInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle( "fCraft ServerGUI" )] +[assembly: AssemblyDescription( "Graphical frontend for fCraft server" )] +[assembly: AssemblyConfiguration( "" )] +[assembly: AssemblyCompany( "matvei.org" )] +[assembly: AssemblyProduct( "fCraft ServerGUI" )] +[assembly: AssemblyCopyright( "fCraft is Copyright © Matvei Stefarov 2009-2012 (matvei.org)" )] +[assembly: AssemblyTrademark( "" )] +[assembly: AssemblyCulture( "" )] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible( false )] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid( "3d303d04-1235-4c51-975c-b9d2d5966113" )] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion( "0.6.1.7" )] +[assembly: AssemblyFileVersion( "0.6.1.7" )] + +[assembly: CLSCompliant( false )] +[assembly: NeutralResourcesLanguage( "en-US" )] \ No newline at end of file diff --git a/ServerGUI/ServerGUI.csproj b/ServerGUI/ServerGUI.csproj new file mode 100644 index 0000000..0c6f397 --- /dev/null +++ b/ServerGUI/ServerGUI.csproj @@ -0,0 +1,94 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4D4EEAA2-190C-496D-B014-0EB68CA4E535} + WinExe + Properties + fCraft.ServerGUI + ServerGUI + v3.5 + 512 + fCraft.ServerGUI.Program + Client + f2k.ico + + + true + ..\bin\Debug\ + DEBUG;TRACE + full + AnyCPU + Auto + prompt + + + ..\bin\Release\ + TRACE + true + pdbonly + AnyCPU + Off + prompt + + + + + 3.5 + + + + + + + + + Component + + + + + MainForm.cs + + + UpdateWindow.cs + + + + Form + + + MainForm.cs + + + Form + + + UpdateWindow.cs + + + + + {AFAEE6CC-8B4F-40CD-9623-7FFDC8E52222} + fCraftGUI + + + {7FBE7809-6F77-415C-ABEB-A3F627E817B0} + fCraft + + + + + + + + \ No newline at end of file diff --git a/ServerGUI/UpdateWindow.Designer.cs b/ServerGUI/UpdateWindow.Designer.cs new file mode 100644 index 0000000..e8e2ec2 --- /dev/null +++ b/ServerGUI/UpdateWindow.Designer.cs @@ -0,0 +1,178 @@ +namespace fCraft.ServerGUI { + partial class UpdateWindow { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) { + if( disposing && (components != null) ) { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.lHeader = new System.Windows.Forms.Label(); + this.bCancel = new System.Windows.Forms.Button(); + this.bUpdateNow = new System.Windows.Forms.Button(); + this.bUpdateLater = new System.Windows.Forms.Button(); + this.progress = new System.Windows.Forms.ProgressBar(); + this.lProgress = new System.Windows.Forms.Label(); + this.lVersion = new System.Windows.Forms.Label(); + this.tChangeLog = new System.Windows.Forms.TextBox(); + this.xShowDetails = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // lHeader + // + this.lHeader.AutoSize = true; + this.lHeader.Font = new System.Drawing.Font( "Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)) ); + this.lHeader.Location = new System.Drawing.Point( 12, 9 ); + this.lHeader.Name = "lHeader"; + this.lHeader.Size = new System.Drawing.Size( 187, 13 ); + this.lHeader.TabIndex = 5; + this.lHeader.Text = "An update to fCraft is available!"; + // + // bCancel + // + this.bCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.bCancel.Location = new System.Drawing.Point( 472, 279 ); + this.bCancel.Name = "bCancel"; + this.bCancel.Size = new System.Drawing.Size( 100, 23 ); + this.bCancel.TabIndex = 4; + this.bCancel.Text = "Cancel"; + this.bCancel.UseVisualStyleBackColor = true; + this.bCancel.Click += new System.EventHandler( this.bCancel_Click ); + // + // bUpdateNow + // + this.bUpdateNow.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bUpdateNow.Enabled = false; + this.bUpdateNow.Location = new System.Drawing.Point( 260, 279 ); + this.bUpdateNow.Name = "bUpdateNow"; + this.bUpdateNow.Size = new System.Drawing.Size( 100, 23 ); + this.bUpdateNow.TabIndex = 2; + this.bUpdateNow.Text = "Restart Now"; + this.bUpdateNow.UseVisualStyleBackColor = true; + this.bUpdateNow.Click += new System.EventHandler( this.bUpdateNow_Click ); + // + // bUpdateLater + // + this.bUpdateLater.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bUpdateLater.Enabled = false; + this.bUpdateLater.Location = new System.Drawing.Point( 366, 279 ); + this.bUpdateLater.Name = "bUpdateLater"; + this.bUpdateLater.Size = new System.Drawing.Size( 100, 23 ); + this.bUpdateLater.TabIndex = 3; + this.bUpdateLater.Text = "Update Later"; + this.bUpdateLater.UseVisualStyleBackColor = true; + this.bUpdateLater.Click += new System.EventHandler( this.bUpdateLater_Click ); + // + // progress + // + this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.progress.Location = new System.Drawing.Point( 436, 12 ); + this.progress.Name = "progress"; + this.progress.Size = new System.Drawing.Size( 136, 23 ); + this.progress.TabIndex = 7; + // + // lProgress + // + this.lProgress.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.lProgress.AutoSize = true; + this.lProgress.Location = new System.Drawing.Point( 433, 38 ); + this.lProgress.Name = "lProgress"; + this.lProgress.Size = new System.Drawing.Size( 100, 13 ); + this.lProgress.TabIndex = 8; + this.lProgress.Text = "Downloading ({0}%)"; + // + // lVersion + // + this.lVersion.AutoSize = true; + this.lVersion.Location = new System.Drawing.Point( 12, 25 ); + this.lVersion.Name = "lVersion"; + this.lVersion.Size = new System.Drawing.Size( 266, 26 ); + this.lVersion.TabIndex = 6; + this.lVersion.Text = "Currently installed version: {0}\r\nNewest available version: {1} (released {2:0} d" + + "ays ago)"; + // + // tChangeLog + // + this.tChangeLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tChangeLog.Location = new System.Drawing.Point( 12, 70 ); + this.tChangeLog.Multiline = true; + this.tChangeLog.Name = "tChangeLog"; + this.tChangeLog.ReadOnly = true; + this.tChangeLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.tChangeLog.Size = new System.Drawing.Size( 560, 203 ); + this.tChangeLog.TabIndex = 0; + // + // xShowDetails + // + this.xShowDetails.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.xShowDetails.AutoSize = true; + this.xShowDetails.Location = new System.Drawing.Point( 12, 283 ); + this.xShowDetails.Name = "xShowDetails"; + this.xShowDetails.Size = new System.Drawing.Size( 86, 17 ); + this.xShowDetails.TabIndex = 1; + this.xShowDetails.Text = "Show details"; + this.xShowDetails.UseVisualStyleBackColor = true; + this.xShowDetails.CheckedChanged += new System.EventHandler( this.xShowDetails_CheckedChanged ); + // + // UpdateWindow + // + this.AcceptButton = this.bUpdateLater; + this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F ); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.bCancel; + this.ClientSize = new System.Drawing.Size( 584, 314 ); + this.Controls.Add( this.xShowDetails ); + this.Controls.Add( this.tChangeLog ); + this.Controls.Add( this.lVersion ); + this.Controls.Add( this.bUpdateLater ); + this.Controls.Add( this.bUpdateNow ); + this.Controls.Add( this.bCancel ); + this.Controls.Add( this.lProgress ); + this.Controls.Add( this.lHeader ); + this.Controls.Add( this.progress ); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size( 500, 130 ); + this.Name = "UpdateWindow"; + this.ShowIcon = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "fCraft Updater"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler( this.UpdateWindow_FormClosing ); + this.ResumeLayout( false ); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lHeader; + private System.Windows.Forms.Button bCancel; + private System.Windows.Forms.Button bUpdateNow; + private System.Windows.Forms.Button bUpdateLater; + private System.Windows.Forms.ProgressBar progress; + private System.Windows.Forms.Label lProgress; + private System.Windows.Forms.Label lVersion; + private System.Windows.Forms.TextBox tChangeLog; + private System.Windows.Forms.CheckBox xShowDetails; + } +} \ No newline at end of file diff --git a/ServerGUI/UpdateWindow.cs b/ServerGUI/UpdateWindow.cs new file mode 100644 index 0000000..69a010a --- /dev/null +++ b/ServerGUI/UpdateWindow.cs @@ -0,0 +1,112 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.ComponentModel; +using System.Net; +using System.Windows.Forms; +using System.Text; +using System.IO; + +namespace fCraft.ServerGUI { + public sealed partial class UpdateWindow : Form { + readonly UpdaterResult updateResult; + readonly string updaterFullPath; + readonly WebClient downloader = new WebClient(); + readonly bool autoUpdate; + bool closeFormWhenDownloaded; + + public UpdateWindow( UpdaterResult update ) { + InitializeComponent(); + updaterFullPath = Path.Combine( Paths.WorkingPath, Paths.UpdaterFileName ); + updateResult = update; + autoUpdate = (ConfigKey.UpdaterMode.GetEnum() == UpdaterMode.Auto); + CreateDetailedChangeLog(); + lVersion.Text = String.Format( lVersion.Text, + Updater.CurrentRelease.VersionString, + updateResult.LatestRelease.VersionString, + updateResult.LatestRelease.Age.TotalDays ); + Shown += Download; + } + + + void Download( object caller, EventArgs args ) { + xShowDetails.Focus(); + downloader.DownloadProgressChanged += DownloadProgress; + downloader.DownloadFileCompleted += DownloadComplete; + downloader.DownloadFileAsync( updateResult.DownloadUri, updaterFullPath ); + } + + + void DownloadProgress( object sender, DownloadProgressChangedEventArgs e ) { + Invoke( (Action)delegate { + progress.Value = e.ProgressPercentage; + lProgress.Text = "Downloading (" + e.ProgressPercentage + "%)"; + } ); + } + + + void DownloadComplete( object sender, AsyncCompletedEventArgs e ) { + if( closeFormWhenDownloaded ) { + Close(); + } else { + progress.Value = 100; + if( e.Cancelled || e.Error != null ) { + MessageBox.Show( e.Error.ToString(), "Error occured while trying to download " + Paths.UpdaterFileName ); + } else if( autoUpdate ) { + bUpdateNow_Click( null, null ); + } else { + bUpdateNow.Enabled = true; + bUpdateLater.Enabled = true; + } + } + } + + + private void bCancel_Click( object sender, EventArgs e ) { + Close(); + } + + private void bUpdateNow_Click( object sender, EventArgs e ) { + string args = Server.GetArgString() + + String.Format( "--restart=\"{0}\"", MonoCompat.PrependMono( "ServerGUI.exe" ) ); + MonoCompat.StartDotNetProcess( updaterFullPath, args, true ); + Server.Shutdown( new ShutdownParams( ShutdownReason.Restarting, TimeSpan.Zero, true, false ), false ); + } + + + void CreateDetailedChangeLog() { + StringBuilder sb = new StringBuilder(); + foreach( ReleaseInfo release in updateResult.History ) { + sb.AppendFormat( "{0} - {1:0} days ago - {2}", + release.VersionString, + release.Age.TotalDays, + String.Join( ", ", release.FlagsList ) ); + sb.AppendLine(); + if( xShowDetails.Checked ) { + sb.AppendFormat( " {0}", String.Join( Environment.NewLine + " ", release.ChangeLog ) ); + } else { + sb.AppendFormat( " {0}", release.Summary ); + } + sb.AppendLine().AppendLine(); + } + tChangeLog.Text = sb.ToString(); + } + + private void xShowDetails_CheckedChanged( object sender, EventArgs e ) { + CreateDetailedChangeLog(); + } + + private void bUpdateLater_Click( object sender, EventArgs e ) { + Updater.RunAtShutdown = true; + Logger.Log( LogType.SystemActivity, + "An fCraft update will be applied next time the server is shut down or restarted." ); + Close(); + } + + private void UpdateWindow_FormClosing( object sender, FormClosingEventArgs e ) { + if( !downloader.IsBusy ) return; + downloader.CancelAsync(); + closeFormWhenDownloaded = true; + e.Cancel = true; + } + } +} \ No newline at end of file diff --git a/ServerGUI/UpdateWindow.resx b/ServerGUI/UpdateWindow.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/ServerGUI/UpdateWindow.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ServerGUI/app.config b/ServerGUI/app.config new file mode 100644 index 0000000..a8afe94 --- /dev/null +++ b/ServerGUI/app.config @@ -0,0 +1,3 @@ + + + diff --git a/ServerGUI/f2k.ico b/ServerGUI/f2k.ico new file mode 100644 index 0000000000000000000000000000000000000000..e3ed811f55cf6e80931eb4f239793052b23d550c GIT binary patch literal 61798 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 + * + * 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. + * + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Threading; +using System.Linq; +using System.Xml.Linq; +using fCraft.UpdateInstaller.Properties; + + +namespace fCraft.UpdateInstaller { + static class Program { + const string ConfigFileNameDefault = "config.xml", + BackupFileNameFormat = "fCraftData_{0:yyyyMMdd'_'HH'-'mm'-'ss}_BeforeUpdate.zip"; + + public const string DataBackupDirectory = "databackups"; + + static readonly string[] FilesToBackup = new[]{ + "PlayerDB.txt", + "config.xml", + "ipbans.txt", + "worlds.xml" + }; + + static readonly string[] LegacyFiles = new[]{ + "fCraftConsole.exe", + "fCraftUI.exe", + "ConfigTool.exe", + "fCraftWinService.exe" + }; + + + static int Main( string[] args ) { + string restartTarget = null; + string configFileName = ConfigFileNameDefault; + + // Set path + string defaultPath = Path.GetFullPath( Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location ) ); + Directory.SetCurrentDirectory( defaultPath ); + + // Parse command-line arguments + List argsList = new List(); + foreach( string arg in args ) { + Console.WriteLine( arg ); + if( arg.StartsWith( "--path=", StringComparison.OrdinalIgnoreCase ) ) { + Directory.SetCurrentDirectory( arg.Substring( arg.IndexOf( '=' ) + 1 ).TrimQuotes() ); + argsList.Add( arg ); + } else if( arg.StartsWith( "--config=", StringComparison.OrdinalIgnoreCase ) ) { + configFileName = arg.Substring( arg.IndexOf( '=' ) + 1 ).TrimQuotes(); + argsList.Add( arg ); + } else if( arg.StartsWith( "--restart=", StringComparison.OrdinalIgnoreCase ) ) { + restartTarget = arg.Substring( arg.IndexOf( '=' ) + 1 ).TrimQuotes(); + } else if( arg != "&" ) { + argsList.Add( arg ); + } + } + + // Parse update settings + string runBefore = null, + runAfter = null; + bool doBackup = true; + + try { + if( File.Exists( configFileName ) ) { + XDocument doc = XDocument.Load( configFileName ); + if( doc.Root != null ) { + XElement elRunBefore = doc.Root.Element( "RunBeforeUpdate" ); + if( elRunBefore != null ) runBefore = elRunBefore.Value; + XElement elRunAfter = doc.Root.Element( "RunAfterUpdate" ); + if( elRunAfter != null ) runAfter = elRunAfter.Value; + XElement elDoBackup = doc.Root.Element( "BackupBeforeUpdate" ); + if( elDoBackup != null && !String.IsNullOrEmpty( elDoBackup.Value ) ) { + if( !Boolean.TryParse( elDoBackup.Value, out doBackup ) ) { + doBackup = true; + } + } + } + } + } catch( Exception ex ) { + Console.Error.WriteLine( "Error reading fCraft config: {0}", ex ); + } + + // Backup data files (if requested) + if( doBackup ) DoBackup(); + + // Run pre-update script (if any) + if( !String.IsNullOrEmpty( runBefore ) ) { + Console.WriteLine( "Executing pre-update script..." ); + try { + Process preUpdateProcess = Process.Start( runBefore, "" ); + if( preUpdateProcess != null ) preUpdateProcess.WaitForExit(); + } catch( Exception ex ) { + Console.Error.WriteLine( "Failed to run pre-update process, aborting update application: {0}", ex ); + return (int)ReturnCodes.FailedToRunPreUpdateCommand; + } + } + + + // Apply the update + using( MemoryStream ms = new MemoryStream( Resources.Payload ) ) { + using( ZipStorer zs = ZipStorer.Open( ms, FileAccess.Read ) ) { + + var allFiles = zs.ReadCentralDir().Select( entry => entry.FilenameInZip ).Union( LegacyFiles ); + + // ensure that fcraft files are writable + bool allPassed; + do { + allPassed = true; + foreach( var fileName in allFiles ) { + try { + FileInfo fi = new FileInfo( fileName ); + if( !fi.Exists ) continue; + using( fi.OpenWrite() ) { } + + } catch( Exception ex ) { + if( ex is IOException ) { + Console.WriteLine( "Waiting for fCraft-related applications to close..." ); + } else { + Console.Error.WriteLine( "ERROR: could not write to {0}: {1} - {2}", + fileName, ex.GetType().Name, ex.Message ); + Console.WriteLine(); + } + allPassed = false; + Thread.Sleep( 1000 ); + break; + } + } + } while( !allPassed ); + + // delete legacy files + foreach( var legacyFile in LegacyFiles ) { + try { + File.Delete( legacyFile ); + } catch( Exception ex ) { + Console.Error.WriteLine( " ERROR: {0} {1}", ex.GetType().Name, ex.Message ); + } + } + + // extract files + foreach( var entry in zs.ReadCentralDir() ) { + Console.WriteLine( "Extracting {0}", entry.FilenameInZip ); + try { + using( FileStream fs = File.Create( entry.FilenameInZip ) ) { + zs.ExtractFile( entry, fs ); + } + } catch( Exception ex ) { + Console.Error.WriteLine( " ERROR: {0} {1}", ex.GetType().Name, ex.Message ); + } + } + } + } + + // Run post-update script + if( !String.IsNullOrEmpty( runAfter ) ) { + Console.WriteLine( "Executing post-update script..." ); + try { + Process postUpdateProcess = Process.Start( runAfter, "" ); + if( postUpdateProcess != null ) postUpdateProcess.WaitForExit(); + } catch( Exception ex ) { + Console.Error.WriteLine( "Failed to run post-update process, aborting restart: {0}", ex ); + return (int)ReturnCodes.FailedToRunPostUpdateCommand; + } + } + + Console.WriteLine( "fCraft update complete." ); + + // Restart fCraft (if requested) + if( restartTarget != null ) { + if( restartTarget == "fCraftConsole.exe" ) { + restartTarget = "ServerCLI.exe"; + } else if( restartTarget == "fCraftUI.exe" ) { + restartTarget = "ServerGUI.exe"; + } + + if( !File.Exists( restartTarget ) ) { + Console.Error.WriteLine( "Restart target not found, quitting: {0}", restartTarget ); + return (int)ReturnCodes.RestartTargetNotFound; + } + string argString = String.Join( " ", argsList.ToArray() ); + Console.WriteLine( "Starting: {0} {1}", restartTarget, argString ); + switch( Environment.OSVersion.Platform ) { + case PlatformID.MacOSX: + case PlatformID.Unix: + Process.Start( "mono", "\"" + restartTarget + "\" " + argString + " &" ); + break; + default: + Process.Start( restartTarget, argString ); + break; + } + } + + return (int)ReturnCodes.Ok; + } + + + static void DoBackup() { + if( !Directory.Exists( DataBackupDirectory ) ) { + Directory.CreateDirectory( DataBackupDirectory ); + } + string backupFileName = String.Format( BackupFileNameFormat, DateTime.Now ); // localized + backupFileName = Path.Combine( DataBackupDirectory, backupFileName ); + using( FileStream fs = File.Create( backupFileName ) ) { + using( ZipStorer backupZip = ZipStorer.Create( fs, "" ) ) { + foreach( string dataFileName in FilesToBackup ) { + if( File.Exists( dataFileName ) ) { + backupZip.AddFile( ZipStorer.Compression.Deflate, dataFileName, dataFileName, "" ); + } + } + } + } + } + + + static string TrimQuotes( this string str ) { + if( str.StartsWith( "\"" ) && str.EndsWith( "\"" ) ) { + return str.Substring( 1, str.Length - 2 ); + } else { + return str; + } + } + } + + enum ReturnCodes { + Ok = 0, + FailedToRunPreUpdateCommand = 1, + FailedToRunPostUpdateCommand = 2, + RestartTargetNotFound = 3 + } +} \ No newline at end of file diff --git a/UpdateInstaller/Properties/AssemblyInfo.cs b/UpdateInstaller/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..003a2be --- /dev/null +++ b/UpdateInstaller/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle( "fCraft UpdateInstaller" )] +[assembly: AssemblyDescription( "fCraft self-extracting updater" )] +[assembly: AssemblyConfiguration( "" )] +[assembly: AssemblyCompany( "matvei.org" )] +[assembly: AssemblyProduct( "fCraft UpdateInstaller" )] +[assembly: AssemblyCopyright( "fCraft is Copyright © Matvei Stefarov 2009-2012 (matvei.org)" )] +[assembly: AssemblyTrademark( "" )] +[assembly: AssemblyCulture( "" )] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible( false )] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid( "b228bbdb-289f-49d7-934a-2701bec9e179" )] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion( "0.6.1.7" )] +[assembly: AssemblyFileVersion( "0.6.1.7" )] \ No newline at end of file diff --git a/UpdateInstaller/Properties/Resources.Designer.cs b/UpdateInstaller/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5945d9e --- /dev/null +++ b/UpdateInstaller/Properties/Resources.Designer.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.5446 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace fCraft.UpdateInstaller.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("fCraft.UpdateInstaller.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static byte[] Payload { + get { + object obj = ResourceManager.GetObject("Payload", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/UpdateInstaller/Properties/Resources.resx b/UpdateInstaller/Properties/Resources.resx new file mode 100644 index 0000000..a7c25c0 --- /dev/null +++ b/UpdateInstaller/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Payload.zip;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/UpdateInstaller/UpdateInstaller.csproj b/UpdateInstaller/UpdateInstaller.csproj new file mode 100644 index 0000000..70da777 --- /dev/null +++ b/UpdateInstaller/UpdateInstaller.csproj @@ -0,0 +1,130 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {478256DE-BD19-4046-9449-A12FF90EE6A8} + Exe + Properties + fCraft.UpdateInstaller + UpdateInstaller + v3.5 + 512 + Client + fCraft.UpdateInstaller.Program + true + fcraft_updater.ico + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + OnOutputUpdated + + + true + ..\bin\Debug\ + DEBUG;TRACE + full + AnyCPU + Off + prompt + + + ..\bin\Release\ + TRACE + true + pdbonly + AnyCPU + Off + prompt + + + + + 3.5 + + + + 3.5 + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + False + .NET Framework Client Profile + true + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + + "$(TargetDir)UpdateBuilder.exe" + + \ No newline at end of file diff --git a/UpdateInstaller/ZipStorer.cs b/UpdateInstaller/ZipStorer.cs new file mode 100644 index 0000000..c191988 --- /dev/null +++ b/UpdateInstaller/ZipStorer.cs @@ -0,0 +1,698 @@ +// ZipStorer, by Jaime Olivares +// Website: zipstorer.codeplex.com +// Version: 2.35 (March 14, 2010) +using System.Collections.Generic; +using System.Text; + +namespace System.IO.Compression { + /// + /// Unique class for compression/decompression file. Represents a Zip file. + /// + public sealed class ZipStorer : IDisposable { + /// Compression method enumeration. + public enum Compression : ushort { + /// Uncompressed storage + Store = 0, + /// Deflate compression method + Deflate = 8 + } + + /// + /// Represents an entry in Zip file directory + /// + public struct ZipFileEntry { + /// Compression method + public Compression Method; + /// Full path and filename as stored in Zip + public string FilenameInZip; + /// Original file size + public uint FileSize; + /// Compressed file size + public uint CompressedSize; + /// Offset of header information inside Zip storage + public uint HeaderOffset; + /// Offset of file inside Zip storage + public uint FileOffset; + /// Size of header information + public uint HeaderSize; + /// 32-bit checksum of entire file + public uint Crc32; + /// Last modification time of file + public DateTime ModifyTime; + /// User comment for file + public string Comment; + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + + /// Overriden method + /// Filename in Zip + public override string ToString() { + return FilenameInZip; + } + } + + #region Public fields + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + /// Force deflate algotithm even if it inflates the stored file. Off by default. + public bool ForceDeflating; + #endregion + + #region Private fields + // List of files to store + private readonly List files = new List(); + // Filename of storage file + private string fileName; + // Stream object of storage file + private Stream zipFileStream; + // General comment + private string comment = ""; + // Central dir image + private byte[] centralDirImage; + // Existing files in zip + private ushort existingFiles; + // File access for Open method + private FileAccess access; + // Static CRC32 Table + private readonly static UInt32[] CrcTable; + // Default filename encoder + private readonly static Encoding DefaultEncoding = Encoding.GetEncoding( 437 ); + #endregion + + #region Public methods + // Static constructor. Just invoked once in order to create the CRC32 lookup table. + static ZipStorer() { + // Generate CRC32 table + CrcTable = new UInt32[256]; + for( int i = 0; i < CrcTable.Length; i++ ) { + UInt32 c = (UInt32)i; + for( int j = 0; j < 8; j++ ) { + if( ( c & 1 ) != 0 ) + c = 3988292384 ^ ( c >> 1 ); + else + c >>= 1; + } + CrcTable[i] = c; + } + } + /// + /// Method to create a new storage file + /// + /// Full path of Zip file to create + /// General comment for Zip file + /// A valid ZipStorer object + public static ZipStorer Create( string filename, string fileComment ) { + Stream stream = new FileStream( filename, FileMode.Create, FileAccess.ReadWrite ); + + ZipStorer zip = Create( stream, fileComment ); + zip.comment = fileComment; + zip.fileName = filename; + + return zip; + } + /// + /// Method to create a new zip storage in a stream + /// + /// + /// + /// A valid ZipStorer object + public static ZipStorer Create( Stream stream, string fileComment ) { + ZipStorer zip = new ZipStorer { comment = fileComment, zipFileStream = stream, access = FileAccess.Write }; + + return zip; + } + + + /// + /// Method to open an existing storage file + /// + /// Full path of Zip file to open + /// File access mode as used in FileStream constructor + /// A valid ZipStorer object + public static ZipStorer Open( string filename, FileAccess fileAccess ) { + Stream stream = new FileStream( filename, FileMode.Open, fileAccess == FileAccess.Read ? FileAccess.Read : FileAccess.ReadWrite ); + + ZipStorer zip = Open( stream, fileAccess ); + zip.fileName = filename; + + return zip; + } + /// + /// Method to open an existing storage from stream + /// + /// Already opened stream with zip contents + /// File access mode for stream operations + /// A valid ZipStorer object + public static ZipStorer Open( Stream stream, FileAccess fileFileAccess ) { + if( !stream.CanSeek && fileFileAccess != FileAccess.Read ) + throw new InvalidOperationException( "Stream cannot seek" ); + + ZipStorer zip = new ZipStorer { zipFileStream = stream, access = fileFileAccess }; + + if( zip.ReadFileInfo() ) + return zip; + + throw new InvalidDataException(); + } + /// + /// Add full contents of a file into the Zip storage + /// + /// Compression method + /// Full path of file to add to Zip storage + /// Filename and path as desired in Zip directory + /// Comment for stored file + public void AddFile( Compression method, string pathname, string filenameInZip, string fileComment ) { + if( access == FileAccess.Read ) + throw new InvalidOperationException( "Writing is not alowed" ); + + FileStream stream = new FileStream( pathname, FileMode.Open, FileAccess.Read ); + AddStream( method, filenameInZip, stream, File.GetLastWriteTime( pathname ), fileComment ); + stream.Close(); + } + /// + /// Add full contents of a stream into the Zip storage + /// + /// Compression method + /// Filename and path as desired in Zip directory + /// Stream object containing the data to store in Zip + /// Modification time of the data to store + /// Comment for stored file + public void AddStream( Compression method, string filenameInZip, Stream source, DateTime modTime, string fileComment ) { + if( access == FileAccess.Read ) + throw new InvalidOperationException( "Writing is not alowed" ); + + /*long offset; + if( Files.Count == 0 ) + offset = 0; + else { + ZipFileEntry last = Files[Files.Count - 1]; + offset = last.HeaderOffset + last.HeaderSize; + }*/ + + // Prepare the fileinfo + ZipFileEntry zfe = new ZipFileEntry { + Method = method, + EncodeUTF8 = EncodeUTF8, + FilenameInZip = NormalizedFilename( filenameInZip ), + Comment = ( fileComment ?? "" ), + Crc32 = 0, + HeaderOffset = (uint)zipFileStream.Position, + ModifyTime = modTime + }; + + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + + // Write local header + WriteLocalHeader( ref zfe ); + zfe.FileOffset = (uint)zipFileStream.Position; + + // Write file to zip (store) + Store( ref zfe, source ); + source.Close(); + + UpdateCrcAndSizes( ref zfe ); + + files.Add( zfe ); + } + /// + /// Updates central directory (if pertinent) and close the Zip storage + /// + /// This is a required step, unless automatic dispose is used + public void Close() { + if( access != FileAccess.Read ) { + uint centralOffset = (uint)zipFileStream.Position; + uint centralSize = 0; + + if( centralDirImage != null ) + zipFileStream.Write( centralDirImage, 0, centralDirImage.Length ); + + for( int i = 0; i < files.Count; i++ ) { + long pos = zipFileStream.Position; + WriteCentralDirRecord( files[i] ); + centralSize += (uint)( zipFileStream.Position - pos ); + } + + if( centralDirImage != null ) + WriteEndRecord( centralSize + (uint)centralDirImage.Length, centralOffset ); + else + WriteEndRecord( centralSize, centralOffset ); + } + + if( zipFileStream == null ) return; + + zipFileStream.Flush(); + zipFileStream.Dispose(); + zipFileStream = null; + } + /// + /// Read all the file records in the central directory + /// + /// List of all entries in directory + public List ReadCentralDir() { + if( centralDirImage == null ) + throw new InvalidOperationException( "Central directory currently does not exist" ); + + List result = new List(); + + for( int pointer = 0; pointer < centralDirImage.Length; ) { + uint signature = BitConverter.ToUInt32( centralDirImage, pointer ); + if( signature != 0x02014b50 ) + break; + + bool encodeUTF8 = ( BitConverter.ToUInt16( centralDirImage, pointer + 8 ) & 0x0800 ) != 0; + ushort method = BitConverter.ToUInt16( centralDirImage, pointer + 10 ); + uint modifyTime = BitConverter.ToUInt32( centralDirImage, pointer + 12 ); + uint crc32 = BitConverter.ToUInt32( centralDirImage, pointer + 16 ); + uint comprSize = BitConverter.ToUInt32( centralDirImage, pointer + 20 ); + uint fileSize = BitConverter.ToUInt32( centralDirImage, pointer + 24 ); + ushort filenameSize = BitConverter.ToUInt16( centralDirImage, pointer + 28 ); + ushort extraSize = BitConverter.ToUInt16( centralDirImage, pointer + 30 ); + ushort commentSize = BitConverter.ToUInt16( centralDirImage, pointer + 32 ); + uint headerOffset = BitConverter.ToUInt32( centralDirImage, pointer + 42 ); + uint headerSize = (uint)( 46 + filenameSize + extraSize + commentSize ); + + Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + + ZipFileEntry zfe = new ZipFileEntry { + Method = (Compression)method, + FilenameInZip = + encoder.GetString( centralDirImage, pointer + 46, + filenameSize ), + FileOffset = GetFileOffset( headerOffset ), + FileSize = fileSize, + CompressedSize = comprSize, + HeaderOffset = headerOffset, + HeaderSize = headerSize, + Crc32 = crc32, + ModifyTime = DosTimeToDateTime( modifyTime ) + }; + if( commentSize > 0 ) + zfe.Comment = encoder.GetString( centralDirImage, pointer + 46 + filenameSize + extraSize, commentSize ); + + result.Add( zfe ); + pointer += ( 46 + filenameSize + extraSize + commentSize ); + } + + return result; + } + /// + /// Copy the contents of a stored file into a physical file + /// + /// Entry information of file to extract + /// Name of file to store uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile( ZipFileEntry zfe, string filename ) { + // Make sure the parent directory exist + string path = Path.GetDirectoryName( filename ); + + if( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + // Check it is directory. If so, do nothing + if( Directory.Exists( filename ) ) + return true; + + Stream output = new FileStream( filename, FileMode.Create, FileAccess.Write ); + bool result = ExtractFile( zfe, output ); + if( result ) + output.Close(); + + File.SetCreationTime( filename, zfe.ModifyTime ); + File.SetLastWriteTime( filename, zfe.ModifyTime ); + + return result; + } + /// + /// Copy the contents of a stored file into an opened stream + /// + /// Entry information of file to extract + /// Stream to store the uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile( ZipFileEntry zfe, Stream stream ) { + if( !stream.CanWrite ) + throw new InvalidOperationException( "Stream cannot be written" ); + + // check signature + byte[] signature = new byte[4]; + zipFileStream.Seek( zfe.HeaderOffset, SeekOrigin.Begin ); + zipFileStream.Read( signature, 0, 4 ); + if( BitConverter.ToUInt32( signature, 0 ) != 0x04034b50 ) + return false; + + // Select input stream for inflating or just reading + Stream inStream; + if( zfe.Method == Compression.Store ) { + inStream = zipFileStream; + } else if( zfe.Method == Compression.Deflate ) { + inStream = new DeflateStream( zipFileStream, CompressionMode.Decompress, true ); + } else { + return false; + } + + // Buffered copy + byte[] buffer = new byte[16384]; + zipFileStream.Seek( zfe.FileOffset, SeekOrigin.Begin ); + uint bytesPending = zfe.FileSize; + while( bytesPending > 0 ) { + int bytesRead = inStream.Read( buffer, 0, (int)Math.Min( bytesPending, buffer.Length ) ); + stream.Write( buffer, 0, bytesRead ); + bytesPending -= (uint)bytesRead; + } + stream.Flush(); + + if( zfe.Method == Compression.Deflate ) + inStream.Dispose(); + return true; + } + /// + /// Removes one of many files in storage. It creates a new Zip file. + /// + /// Reference to the current Zip object + /// List of Entries to remove from storage + /// True if success, false if not + /// This method only works for storage of type FileStream + public static bool RemoveEntries( ref ZipStorer zip, List zfes ) { + if( !( zip.zipFileStream is FileStream ) ) + throw new InvalidOperationException( "RemoveEntries is allowed just over streams of type FileStream" ); + + + //Get full list of entries + List fullList = zip.ReadCentralDir(); + + //In order to delete we need to create a copy of the zip file excluding the selected items + string tempZipName = Path.GetTempFileName(); + string tempEntryName = Path.GetTempFileName(); + + try { + ZipStorer tempZip = Create( tempZipName, string.Empty ); + + foreach( ZipFileEntry zfe in fullList ) { + if( !zfes.Contains( zfe ) ) { + if( zip.ExtractFile( zfe, tempEntryName ) ) { + tempZip.AddFile( zfe.Method, tempEntryName, zfe.FilenameInZip, zfe.Comment ); + } + } + } + zip.Close(); + tempZip.Close(); + + if( File.Exists( zip.fileName ) ) { + File.Replace( tempZipName, zip.fileName, null, true ); + } else { + File.Move( tempZipName, zip.fileName ); + } + + zip = Open( zip.fileName, zip.access ); + } catch { + return false; + } finally { + if( File.Exists( tempZipName ) ) + File.Delete( tempZipName ); + if( File.Exists( tempEntryName ) ) + File.Delete( tempEntryName ); + } + return true; + } + #endregion + + #region Private methods + // Calculate the file offset by reading the corresponding local header + private uint GetFileOffset( uint headerOffset ) { + byte[] buffer = new byte[2]; + + zipFileStream.Seek( headerOffset + 26, SeekOrigin.Begin ); + zipFileStream.Read( buffer, 0, 2 ); + ushort filenameSize = BitConverter.ToUInt16( buffer, 0 ); + zipFileStream.Read( buffer, 0, 2 ); + ushort extraSize = BitConverter.ToUInt16( buffer, 0 ); + + return (uint)( 30 + filenameSize + extraSize + headerOffset ); + } + /* Local file header: + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + + filename (variable size) + extra field (variable size) + */ + private void WriteLocalHeader( ref ZipFileEntry zfe ) { + long pos = zipFileStream.Position; + Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes( zfe.FilenameInZip ); + + zipFileStream.Write( new byte[] { 80, 75, 3, 4, 20, 0 }, 0, 6 ); // No extra header + zipFileStream.Write( BitConverter.GetBytes( (ushort)( zfe.EncodeUTF8 ? 0x0800 : 0 ) ), 0, 2 ); // filename and comment encoding + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + zipFileStream.Write( BitConverter.GetBytes( DateTimeToDosTime( zfe.ModifyTime ) ), 0, 4 ); // zipping date and time + zipFileStream.Write( new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 12 ); // unused CRC, un/compressed size, updated later + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedFilename.Length ), 0, 2 ); // filename length + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // extra length + + zipFileStream.Write( encodedFilename, 0, encodedFilename.Length ); + zfe.HeaderSize = (uint)( zipFileStream.Position - pos ); + } + /* Central directory's File header: + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + filename (variable size) + extra field (variable size) + file comment (variable size) + */ + private void WriteCentralDirRecord( ZipFileEntry zfe ) { + Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes( zfe.FilenameInZip ); + byte[] encodedComment = encoder.GetBytes( zfe.Comment ); + + zipFileStream.Write( new byte[] { 80, 75, 1, 2, 23, 0xB, 20, 0 }, 0, 8 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)( zfe.EncodeUTF8 ? 0x0800 : 0 ) ), 0, 2 ); // filename and comment encoding + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + zipFileStream.Write( BitConverter.GetBytes( DateTimeToDosTime( zfe.ModifyTime ) ), 0, 4 ); // zipping date and time + zipFileStream.Write( BitConverter.GetBytes( zfe.Crc32 ), 0, 4 ); // file CRC + zipFileStream.Write( BitConverter.GetBytes( zfe.CompressedSize ), 0, 4 ); // compressed file size + zipFileStream.Write( BitConverter.GetBytes( zfe.FileSize ), 0, 4 ); // uncompressed file size + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedFilename.Length ), 0, 2 ); // Filename in zip + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // extra length + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedComment.Length ), 0, 2 ); + + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // disk=0 + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // file type: binary + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // Internal file attributes + zipFileStream.Write( BitConverter.GetBytes( (ushort)0x8100 ), 0, 2 ); // External file attributes (normal/readable) + zipFileStream.Write( BitConverter.GetBytes( zfe.HeaderOffset ), 0, 4 ); // Offset of header + + zipFileStream.Write( encodedFilename, 0, encodedFilename.Length ); + zipFileStream.Write( encodedComment, 0, encodedComment.Length ); + } + /* End of central dir record: + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in + the central dir on this disk 2 bytes + total number of entries in + the central dir 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + zipfile comment length 2 bytes + zipfile comment (variable size) + */ + private void WriteEndRecord( uint size, uint offset ) { + Encoding encoder = EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedComment = encoder.GetBytes( comment ); + + zipFileStream.Write( new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)files.Count + existingFiles ), 0, 2 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)files.Count + existingFiles ), 0, 2 ); + zipFileStream.Write( BitConverter.GetBytes( size ), 0, 4 ); + zipFileStream.Write( BitConverter.GetBytes( offset ), 0, 4 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedComment.Length ), 0, 2 ); + zipFileStream.Write( encodedComment, 0, encodedComment.Length ); + } + // Copies all source file into storage file + private void Store( ref ZipFileEntry zfe, Stream source ) { + byte[] buffer = new byte[16384]; + int bytesRead; + uint totalRead = 0; + Stream outStream; + + long posStart = zipFileStream.Position; + long sourceStart = source.Position; + + if( zfe.Method == Compression.Store ) + outStream = zipFileStream; + else + outStream = new DeflateStream( zipFileStream, CompressionMode.Compress, true ); + + zfe.Crc32 = 0 ^ 0xffffffff; + + do { + bytesRead = source.Read( buffer, 0, buffer.Length ); + totalRead += (uint)bytesRead; + if( bytesRead > 0 ) { + outStream.Write( buffer, 0, bytesRead ); + + for( uint i = 0; i < bytesRead; i++ ) { + zfe.Crc32 = CrcTable[( zfe.Crc32 ^ buffer[i] ) & 0xFF] ^ ( zfe.Crc32 >> 8 ); + } + } + } while( bytesRead == buffer.Length ); + + if( totalRead > 0 ) + outStream.Flush(); // fix for "Internal error Flush" under Mono + + if( zfe.Method == Compression.Deflate ) + outStream.Dispose(); + + zfe.Crc32 ^= 0xffffffff; + zfe.FileSize = totalRead; + zfe.CompressedSize = (uint)( zipFileStream.Position - posStart ); + + // Verify for real compression + if( zfe.Method == Compression.Deflate && !ForceDeflating && source.CanSeek && zfe.CompressedSize > zfe.FileSize ) { + // Start operation again with Store algorithm + zfe.Method = Compression.Store; + zipFileStream.Position = posStart; + zipFileStream.SetLength( posStart ); + source.Position = sourceStart; + Store( ref zfe, source ); + } + } + /* DOS Date and time: + MS-DOS date. The date is a packed value with the following format. Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + MS-DOS time. The time is a packed value with the following format. Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + */ + private static uint DateTimeToDosTime( DateTime dt ) { + return (uint)( + ( dt.Second / 2 ) | ( dt.Minute << 5 ) | ( dt.Hour << 11 ) | + ( dt.Day << 16 ) | ( dt.Month << 21 ) | ( ( dt.Year - 1980 ) << 25 ) ); + } + private static DateTime DosTimeToDateTime( uint dt ) { + return new DateTime( + (int)( dt >> 25 ) + 1980, + (int)( dt >> 21 ) & 15, + (int)( dt >> 16 ) & 31, + (int)( dt >> 11 ) & 31, + (int)( dt >> 5 ) & 63, + (int)( dt & 31 ) * 2 ); + } + + /* CRC32 algorithm + The 'magic number' for the CRC is 0xdebb20e3. + The proper CRC pre and post conditioning + is used, meaning that the CRC register is + pre-conditioned with all ones (a starting value + of 0xffffffff) and the value is post-conditioned by + taking the one's complement of the CRC residual. + If bit 3 of the general purpose flag is set, this + field is set to zero in the local header and the correct + value is put in the data descriptor and in the central + directory. + */ + private void UpdateCrcAndSizes( ref ZipFileEntry zfe ) { + long lastPos = zipFileStream.Position; // remember position + + zipFileStream.Position = zfe.HeaderOffset + 8; + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + + zipFileStream.Position = zfe.HeaderOffset + 14; + zipFileStream.Write( BitConverter.GetBytes( zfe.Crc32 ), 0, 4 ); // Update CRC + zipFileStream.Write( BitConverter.GetBytes( zfe.CompressedSize ), 0, 4 ); // Compressed size + zipFileStream.Write( BitConverter.GetBytes( zfe.FileSize ), 0, 4 ); // Uncompressed size + + zipFileStream.Position = lastPos; // restore position + } + // Replaces backslashes with slashes to store in zip header + private static string NormalizedFilename( string filename ) { + string normalizedFilename = filename.Replace( '\\', '/' ); + + int pos = normalizedFilename.IndexOf( ':' ); + if( pos >= 0 ) + normalizedFilename = normalizedFilename.Remove( 0, pos + 1 ); + + return normalizedFilename.Trim( '/' ); + } + // Reads the end-of-central-directory record + private bool ReadFileInfo() { + if( zipFileStream.Length < 22 ) + return false; + + try { + zipFileStream.Seek( -17, SeekOrigin.End ); + BinaryReader br = new BinaryReader( zipFileStream ); + do { + zipFileStream.Seek( -5, SeekOrigin.Current ); + UInt32 sig = br.ReadUInt32(); + if( sig != 0x06054b50 ) continue; + + zipFileStream.Seek( 6, SeekOrigin.Current ); + + UInt16 entries = br.ReadUInt16(); + Int32 centralSize = br.ReadInt32(); + UInt32 centralDirOffset = br.ReadUInt32(); + UInt16 commentSize = br.ReadUInt16(); + + // check if comment field is the very last data in file + if( zipFileStream.Position + commentSize != zipFileStream.Length ) + return false; + + // Copy entire central directory to a memory buffer + existingFiles = entries; + centralDirImage = new byte[centralSize]; + zipFileStream.Seek( centralDirOffset, SeekOrigin.Begin ); + zipFileStream.Read( centralDirImage, 0, centralSize ); + + // Leave the pointer at the begining of central dir, to append new files + zipFileStream.Seek( centralDirOffset, SeekOrigin.Begin ); + return true; + } while( zipFileStream.Position > 0 ); + } catch { } + + return false; + } + #endregion + + #region IDisposable Members + /// + /// Closes the Zip file stream + /// + public void Dispose() { + Close(); + } + #endregion + } +} \ No newline at end of file diff --git a/UpdateInstaller/app.config b/UpdateInstaller/app.config new file mode 100644 index 0000000..a8afe94 --- /dev/null +++ b/UpdateInstaller/app.config @@ -0,0 +1,3 @@ + + + diff --git a/UpdateInstaller/fcraft_updater.ico b/UpdateInstaller/fcraft_updater.ico new file mode 100644 index 0000000000000000000000000000000000000000..c2b0540b5c4010ae715d73e21fe1e5dd133ee0e8 GIT binary patch literal 61798 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 n!hc=cZGFIk3;(rkxAnpQxBoMHz`C} + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E5719EAF-5ECF-4BBD-842B-39DDFBCE8FE7} + Exe + Properties + fCraft.UpdateBuilder + UpdateBuilder + v3.5 + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + fCraft.UpdateBuilder.Program + Client + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + true + + + + + 3.5 + + + + + + + + + + False + .NET Framework Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/UpdaterBuilder/ZipStorer.cs b/UpdaterBuilder/ZipStorer.cs new file mode 100644 index 0000000..d740d29 --- /dev/null +++ b/UpdaterBuilder/ZipStorer.cs @@ -0,0 +1,700 @@ +// ZipStorer, by Jaime Olivares +// Website: zipstorer.codeplex.com +// Version: 2.35 (March 14, 2010) +using System.Collections.Generic; +using System.Text; + +namespace System.IO.Compression { + /// + /// Unique class for compression/decompression file. Represents a Zip file. + /// + public sealed class ZipStorer : IDisposable { + /// + /// Compression method enumeration + /// + public enum Compression : ushort { + /// Uncompressed storage + Store = 0, + /// Deflate compression method + Deflate = 8 + } + + /// + /// Represents an entry in Zip file directory + /// + public struct ZipFileEntry { + /// Compression method + public Compression Method; + /// Full path and filename as stored in Zip + public string FilenameInZip; + /// Original file size + public uint FileSize; + /// Compressed file size + public uint CompressedSize; + /// Offset of header information inside Zip storage + public uint HeaderOffset; + /// Offset of file inside Zip storage + public uint FileOffset; + /// Size of header information + public uint HeaderSize; + /// 32-bit checksum of entire file + public uint Crc32; + /// Last modification time of file + public DateTime ModifyTime; + /// User comment for file + public string Comment; + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + + /// Overriden method + /// Filename in Zip + public override string ToString() { + return FilenameInZip; + } + } + + #region Public fields + /// True if UTF8 encoding for filename and comments, false if default (CP 437) + public bool EncodeUTF8; + /// Force deflate algotithm even if it inflates the stored file. Off by default. + public bool ForceDeflating; + #endregion + + #region Private fields + // List of files to store + private readonly List files = new List(); + // Filename of storage file + private string fileName; + // Stream object of storage file + private Stream zipFileStream; + // General comment + private string comment = ""; + // Central dir image + private byte[] centralDirImage; + // Existing files in zip + private ushort existingFiles; + // File access for Open method + private FileAccess access; + // Static CRC32 Table + private readonly static UInt32[] CrcTable; + // Default filename encoder + private readonly static Encoding DefaultEncoding = Encoding.GetEncoding( 437 ); + #endregion + + #region Public methods + // Static constructor. Just invoked once in order to create the CRC32 lookup table. + static ZipStorer() { + // Generate CRC32 table + CrcTable = new UInt32[256]; + for( int i = 0; i < CrcTable.Length; i++ ) { + UInt32 c = (UInt32)i; + for( int j = 0; j < 8; j++ ) { + if( ( c & 1 ) != 0 ) + c = 3988292384 ^ ( c >> 1 ); + else + c >>= 1; + } + CrcTable[i] = c; + } + } + /// + /// Method to create a new storage file + /// + /// Full path of Zip file to create + /// General comment for Zip file + /// A valid ZipStorer object + public static ZipStorer Create( string filename, string fileComment ) { + Stream stream = new FileStream( filename, FileMode.Create, FileAccess.ReadWrite ); + + ZipStorer zip = Create( stream, fileComment ); + zip.comment = fileComment; + zip.fileName = filename; + + return zip; + } + /// + /// Method to create a new zip storage in a stream + /// + /// + /// + /// A valid ZipStorer object + public static ZipStorer Create( Stream stream, string fileComment ) { + ZipStorer zip = new ZipStorer { comment = fileComment, zipFileStream = stream, access = FileAccess.Write }; + + return zip; + } + + + /// + /// Method to open an existing storage file + /// + /// Full path of Zip file to open + /// File access mode as used in FileStream constructor + /// A valid ZipStorer object + public static ZipStorer Open( string filename, FileAccess fileAccess ) { + Stream stream = new FileStream( filename, FileMode.Open, fileAccess == FileAccess.Read ? FileAccess.Read : FileAccess.ReadWrite ); + + ZipStorer zip = Open( stream, fileAccess ); + zip.fileName = filename; + + return zip; + } + /// + /// Method to open an existing storage from stream + /// + /// Already opened stream with zip contents + /// File access mode for stream operations + /// A valid ZipStorer object + public static ZipStorer Open( Stream stream, FileAccess fileFileAccess ) { + if( !stream.CanSeek && fileFileAccess != FileAccess.Read ) + throw new InvalidOperationException( "Stream cannot seek" ); + + ZipStorer zip = new ZipStorer { zipFileStream = stream, access = fileFileAccess }; + + if( zip.ReadFileInfo() ) + return zip; + + throw new InvalidDataException(); + } + /// + /// Add full contents of a file into the Zip storage + /// + /// Compression method + /// Full path of file to add to Zip storage + /// Filename and path as desired in Zip directory + /// Comment for stored file + public void AddFile( Compression method, string pathname, string filenameInZip, string fileComment ) { + if( access == FileAccess.Read ) + throw new InvalidOperationException( "Writing is not alowed" ); + + FileStream stream = new FileStream( pathname, FileMode.Open, FileAccess.Read ); + AddStream( method, filenameInZip, stream, File.GetLastWriteTime( pathname ), fileComment ); + stream.Close(); + } + /// + /// Add full contents of a stream into the Zip storage + /// + /// Compression method + /// Filename and path as desired in Zip directory + /// Stream object containing the data to store in Zip + /// Modification time of the data to store + /// Comment for stored file + public void AddStream( Compression method, string filenameInZip, Stream source, DateTime modTime, string fileComment ) { + if( access == FileAccess.Read ) + throw new InvalidOperationException( "Writing is not alowed" ); + + /*long offset; + if( Files.Count == 0 ) + offset = 0; + else { + ZipFileEntry last = Files[Files.Count - 1]; + offset = last.HeaderOffset + last.HeaderSize; + }*/ + + // Prepare the fileinfo + ZipFileEntry zfe = new ZipFileEntry { + Method = method, + EncodeUTF8 = EncodeUTF8, + FilenameInZip = NormalizedFilename( filenameInZip ), + Comment = ( fileComment ?? "" ), + Crc32 = 0, + HeaderOffset = (uint)zipFileStream.Position, + ModifyTime = modTime + }; + + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + + // Write local header + WriteLocalHeader( ref zfe ); + zfe.FileOffset = (uint)zipFileStream.Position; + + // Write file to zip (store) + Store( ref zfe, source ); + source.Close(); + + UpdateCrcAndSizes( ref zfe ); + + files.Add( zfe ); + } + /// + /// Updates central directory (if pertinent) and close the Zip storage + /// + /// This is a required step, unless automatic dispose is used + public void Close() { + if( access != FileAccess.Read ) { + uint centralOffset = (uint)zipFileStream.Position; + uint centralSize = 0; + + if( centralDirImage != null ) + zipFileStream.Write( centralDirImage, 0, centralDirImage.Length ); + + for( int i = 0; i < files.Count; i++ ) { + long pos = zipFileStream.Position; + WriteCentralDirRecord( files[i] ); + centralSize += (uint)( zipFileStream.Position - pos ); + } + + if( centralDirImage != null ) + WriteEndRecord( centralSize + (uint)centralDirImage.Length, centralOffset ); + else + WriteEndRecord( centralSize, centralOffset ); + } + + if( zipFileStream == null ) return; + + zipFileStream.Flush(); + zipFileStream.Dispose(); + zipFileStream = null; + } + /// + /// Read all the file records in the central directory + /// + /// List of all entries in directory + public List ReadCentralDir() { + if( centralDirImage == null ) + throw new InvalidOperationException( "Central directory currently does not exist" ); + + List result = new List(); + + for( int pointer = 0; pointer < centralDirImage.Length; ) { + uint signature = BitConverter.ToUInt32( centralDirImage, pointer ); + if( signature != 0x02014b50 ) + break; + + bool encodeUTF8 = ( BitConverter.ToUInt16( centralDirImage, pointer + 8 ) & 0x0800 ) != 0; + ushort method = BitConverter.ToUInt16( centralDirImage, pointer + 10 ); + uint modifyTime = BitConverter.ToUInt32( centralDirImage, pointer + 12 ); + uint crc32 = BitConverter.ToUInt32( centralDirImage, pointer + 16 ); + uint comprSize = BitConverter.ToUInt32( centralDirImage, pointer + 20 ); + uint fileSize = BitConverter.ToUInt32( centralDirImage, pointer + 24 ); + ushort filenameSize = BitConverter.ToUInt16( centralDirImage, pointer + 28 ); + ushort extraSize = BitConverter.ToUInt16( centralDirImage, pointer + 30 ); + ushort commentSize = BitConverter.ToUInt16( centralDirImage, pointer + 32 ); + uint headerOffset = BitConverter.ToUInt32( centralDirImage, pointer + 42 ); + uint headerSize = (uint)( 46 + filenameSize + extraSize + commentSize ); + + Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + + ZipFileEntry zfe = new ZipFileEntry { + Method = (Compression)method, + FilenameInZip = + encoder.GetString( centralDirImage, pointer + 46, + filenameSize ), + FileOffset = GetFileOffset( headerOffset ), + FileSize = fileSize, + CompressedSize = comprSize, + HeaderOffset = headerOffset, + HeaderSize = headerSize, + Crc32 = crc32, + ModifyTime = DosTimeToDateTime( modifyTime ) + }; + if( commentSize > 0 ) + zfe.Comment = encoder.GetString( centralDirImage, pointer + 46 + filenameSize + extraSize, commentSize ); + + result.Add( zfe ); + pointer += ( 46 + filenameSize + extraSize + commentSize ); + } + + return result; + } + /// + /// Copy the contents of a stored file into a physical file + /// + /// Entry information of file to extract + /// Name of file to store uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile( ZipFileEntry zfe, string filename ) { + // Make sure the parent directory exist + string path = Path.GetDirectoryName( filename ); + + if( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + // Check it is directory. If so, do nothing + if( Directory.Exists( filename ) ) + return true; + + Stream output = new FileStream( filename, FileMode.Create, FileAccess.Write ); + bool result = ExtractFile( zfe, output ); + if( result ) + output.Close(); + + File.SetCreationTime( filename, zfe.ModifyTime ); + File.SetLastWriteTime( filename, zfe.ModifyTime ); + + return result; + } + /// + /// Copy the contents of a stored file into an opened stream + /// + /// Entry information of file to extract + /// Stream to store the uncompressed data + /// True if success, false if not. + /// Unique compression methods are Store and Deflate + public bool ExtractFile( ZipFileEntry zfe, Stream stream ) { + if( !stream.CanWrite ) + throw new InvalidOperationException( "Stream cannot be written" ); + + // check signature + byte[] signature = new byte[4]; + zipFileStream.Seek( zfe.HeaderOffset, SeekOrigin.Begin ); + zipFileStream.Read( signature, 0, 4 ); + if( BitConverter.ToUInt32( signature, 0 ) != 0x04034b50 ) + return false; + + // Select input stream for inflating or just reading + Stream inStream; + if( zfe.Method == Compression.Store ) { + inStream = zipFileStream; + } else if( zfe.Method == Compression.Deflate ) { + inStream = new DeflateStream( zipFileStream, CompressionMode.Decompress, true ); + } else { + return false; + } + + // Buffered copy + byte[] buffer = new byte[16384]; + zipFileStream.Seek( zfe.FileOffset, SeekOrigin.Begin ); + uint bytesPending = zfe.FileSize; + while( bytesPending > 0 ) { + int bytesRead = inStream.Read( buffer, 0, (int)Math.Min( bytesPending, buffer.Length ) ); + stream.Write( buffer, 0, bytesRead ); + bytesPending -= (uint)bytesRead; + } + stream.Flush(); + + if( zfe.Method == Compression.Deflate ) + inStream.Dispose(); + return true; + } + /// + /// Removes one of many files in storage. It creates a new Zip file. + /// + /// Reference to the current Zip object + /// List of Entries to remove from storage + /// True if success, false if not + /// This method only works for storage of type FileStream + public static bool RemoveEntries( ref ZipStorer zip, List zfes ) { + if( !( zip.zipFileStream is FileStream ) ) + throw new InvalidOperationException( "RemoveEntries is allowed just over streams of type FileStream" ); + + + //Get full list of entries + List fullList = zip.ReadCentralDir(); + + //In order to delete we need to create a copy of the zip file excluding the selected items + string tempZipName = Path.GetTempFileName(); + string tempEntryName = Path.GetTempFileName(); + + try { + ZipStorer tempZip = Create( tempZipName, string.Empty ); + + foreach( ZipFileEntry zfe in fullList ) { + if( !zfes.Contains( zfe ) ) { + if( zip.ExtractFile( zfe, tempEntryName ) ) { + tempZip.AddFile( zfe.Method, tempEntryName, zfe.FilenameInZip, zfe.Comment ); + } + } + } + zip.Close(); + tempZip.Close(); + + if( File.Exists( zip.fileName ) ) { + File.Replace( tempZipName, zip.fileName, null, true ); + } else { + File.Move( tempZipName, zip.fileName ); + } + + zip = Open( zip.fileName, zip.access ); + } catch { + return false; + } finally { + if( File.Exists( tempZipName ) ) + File.Delete( tempZipName ); + if( File.Exists( tempEntryName ) ) + File.Delete( tempEntryName ); + } + return true; + } + #endregion + + #region Private methods + // Calculate the file offset by reading the corresponding local header + private uint GetFileOffset( uint headerOffset ) { + byte[] buffer = new byte[2]; + + zipFileStream.Seek( headerOffset + 26, SeekOrigin.Begin ); + zipFileStream.Read( buffer, 0, 2 ); + ushort filenameSize = BitConverter.ToUInt16( buffer, 0 ); + zipFileStream.Read( buffer, 0, 2 ); + ushort extraSize = BitConverter.ToUInt16( buffer, 0 ); + + return (uint)( 30 + filenameSize + extraSize + headerOffset ); + } + /* Local file header: + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + + filename (variable size) + extra field (variable size) + */ + private void WriteLocalHeader( ref ZipFileEntry zfe ) { + long pos = zipFileStream.Position; + Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes( zfe.FilenameInZip ); + + zipFileStream.Write( new byte[] { 80, 75, 3, 4, 20, 0 }, 0, 6 ); // No extra header + zipFileStream.Write( BitConverter.GetBytes( (ushort)( zfe.EncodeUTF8 ? 0x0800 : 0 ) ), 0, 2 ); // filename and comment encoding + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + zipFileStream.Write( BitConverter.GetBytes( DateTimeToDosTime( zfe.ModifyTime ) ), 0, 4 ); // zipping date and time + zipFileStream.Write( new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 12 ); // unused CRC, un/compressed size, updated later + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedFilename.Length ), 0, 2 ); // filename length + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // extra length + + zipFileStream.Write( encodedFilename, 0, encodedFilename.Length ); + zfe.HeaderSize = (uint)( zipFileStream.Position - pos ); + } + /* Central directory's File header: + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + filename length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + filename (variable size) + extra field (variable size) + file comment (variable size) + */ + private void WriteCentralDirRecord( ZipFileEntry zfe ) { + Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedFilename = encoder.GetBytes( zfe.FilenameInZip ); + byte[] encodedComment = encoder.GetBytes( zfe.Comment ); + + zipFileStream.Write( new byte[] { 80, 75, 1, 2, 23, 0xB, 20, 0 }, 0, 8 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)( zfe.EncodeUTF8 ? 0x0800 : 0 ) ), 0, 2 ); // filename and comment encoding + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + zipFileStream.Write( BitConverter.GetBytes( DateTimeToDosTime( zfe.ModifyTime ) ), 0, 4 ); // zipping date and time + zipFileStream.Write( BitConverter.GetBytes( zfe.Crc32 ), 0, 4 ); // file CRC + zipFileStream.Write( BitConverter.GetBytes( zfe.CompressedSize ), 0, 4 ); // compressed file size + zipFileStream.Write( BitConverter.GetBytes( zfe.FileSize ), 0, 4 ); // uncompressed file size + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedFilename.Length ), 0, 2 ); // Filename in zip + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // extra length + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedComment.Length ), 0, 2 ); + + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // disk=0 + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // file type: binary + zipFileStream.Write( BitConverter.GetBytes( (ushort)0 ), 0, 2 ); // Internal file attributes + zipFileStream.Write( BitConverter.GetBytes( (ushort)0x8100 ), 0, 2 ); // External file attributes (normal/readable) + zipFileStream.Write( BitConverter.GetBytes( zfe.HeaderOffset ), 0, 4 ); // Offset of header + + zipFileStream.Write( encodedFilename, 0, encodedFilename.Length ); + zipFileStream.Write( encodedComment, 0, encodedComment.Length ); + } + /* End of central dir record: + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in + the central dir on this disk 2 bytes + total number of entries in + the central dir 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + zipfile comment length 2 bytes + zipfile comment (variable size) + */ + private void WriteEndRecord( uint size, uint offset ) { + Encoding encoder = EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding; + byte[] encodedComment = encoder.GetBytes( comment ); + + zipFileStream.Write( new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)files.Count + existingFiles ), 0, 2 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)files.Count + existingFiles ), 0, 2 ); + zipFileStream.Write( BitConverter.GetBytes( size ), 0, 4 ); + zipFileStream.Write( BitConverter.GetBytes( offset ), 0, 4 ); + zipFileStream.Write( BitConverter.GetBytes( (ushort)encodedComment.Length ), 0, 2 ); + zipFileStream.Write( encodedComment, 0, encodedComment.Length ); + } + // Copies all source file into storage file + private void Store( ref ZipFileEntry zfe, Stream source ) { + byte[] buffer = new byte[16384]; + int bytesRead; + uint totalRead = 0; + Stream outStream; + + long posStart = zipFileStream.Position; + long sourceStart = source.Position; + + if( zfe.Method == Compression.Store ) + outStream = zipFileStream; + else + outStream = new DeflateStream( zipFileStream, CompressionMode.Compress, true ); + + zfe.Crc32 = 0 ^ 0xffffffff; + + do { + bytesRead = source.Read( buffer, 0, buffer.Length ); + totalRead += (uint)bytesRead; + if( bytesRead > 0 ) { + outStream.Write( buffer, 0, bytesRead ); + + for( uint i = 0; i < bytesRead; i++ ) { + zfe.Crc32 = CrcTable[( zfe.Crc32 ^ buffer[i] ) & 0xFF] ^ ( zfe.Crc32 >> 8 ); + } + } + } while( bytesRead == buffer.Length ); + + if( totalRead > 0 ) + outStream.Flush(); // fix for "Internal error Flush" under Mono + + if( zfe.Method == Compression.Deflate ) + outStream.Dispose(); + + zfe.Crc32 ^= 0xffffffff; + zfe.FileSize = totalRead; + zfe.CompressedSize = (uint)( zipFileStream.Position - posStart ); + + // Verify for real compression + if( zfe.Method == Compression.Deflate && !ForceDeflating && source.CanSeek && zfe.CompressedSize > zfe.FileSize ) { + // Start operation again with Store algorithm + zfe.Method = Compression.Store; + zipFileStream.Position = posStart; + zipFileStream.SetLength( posStart ); + source.Position = sourceStart; + Store( ref zfe, source ); + } + } + /* DOS Date and time: + MS-DOS date. The date is a packed value with the following format. Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + MS-DOS time. The time is a packed value with the following format. Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) + */ + private static uint DateTimeToDosTime( DateTime dt ) { + return (uint)( + ( dt.Second / 2 ) | ( dt.Minute << 5 ) | ( dt.Hour << 11 ) | + ( dt.Day << 16 ) | ( dt.Month << 21 ) | ( ( dt.Year - 1980 ) << 25 ) ); + } + private static DateTime DosTimeToDateTime( uint dt ) { + return new DateTime( + (int)( dt >> 25 ) + 1980, + (int)( dt >> 21 ) & 15, + (int)( dt >> 16 ) & 31, + (int)( dt >> 11 ) & 31, + (int)( dt >> 5 ) & 63, + (int)( dt & 31 ) * 2 ); + } + + /* CRC32 algorithm + The 'magic number' for the CRC is 0xdebb20e3. + The proper CRC pre and post conditioning + is used, meaning that the CRC register is + pre-conditioned with all ones (a starting value + of 0xffffffff) and the value is post-conditioned by + taking the one's complement of the CRC residual. + If bit 3 of the general purpose flag is set, this + field is set to zero in the local header and the correct + value is put in the data descriptor and in the central + directory. + */ + private void UpdateCrcAndSizes( ref ZipFileEntry zfe ) { + long lastPos = zipFileStream.Position; // remember position + + zipFileStream.Position = zfe.HeaderOffset + 8; + zipFileStream.Write( BitConverter.GetBytes( (ushort)zfe.Method ), 0, 2 ); // zipping method + + zipFileStream.Position = zfe.HeaderOffset + 14; + zipFileStream.Write( BitConverter.GetBytes( zfe.Crc32 ), 0, 4 ); // Update CRC + zipFileStream.Write( BitConverter.GetBytes( zfe.CompressedSize ), 0, 4 ); // Compressed size + zipFileStream.Write( BitConverter.GetBytes( zfe.FileSize ), 0, 4 ); // Uncompressed size + + zipFileStream.Position = lastPos; // restore position + } + // Replaces backslashes with slashes to store in zip header + private static string NormalizedFilename( string filename ) { + string normalizedFilename = filename.Replace( '\\', '/' ); + + int pos = normalizedFilename.IndexOf( ':' ); + if( pos >= 0 ) + normalizedFilename = normalizedFilename.Remove( 0, pos + 1 ); + + return normalizedFilename.Trim( '/' ); + } + // Reads the end-of-central-directory record + private bool ReadFileInfo() { + if( zipFileStream.Length < 22 ) + return false; + + try { + zipFileStream.Seek( -17, SeekOrigin.End ); + BinaryReader br = new BinaryReader( zipFileStream ); + do { + zipFileStream.Seek( -5, SeekOrigin.Current ); + UInt32 sig = br.ReadUInt32(); + if( sig != 0x06054b50 ) continue; + + zipFileStream.Seek( 6, SeekOrigin.Current ); + + UInt16 entries = br.ReadUInt16(); + Int32 centralSize = br.ReadInt32(); + UInt32 centralDirOffset = br.ReadUInt32(); + UInt16 commentSize = br.ReadUInt16(); + + // check if comment field is the very last data in file + if( zipFileStream.Position + commentSize != zipFileStream.Length ) + return false; + + // Copy entire central directory to a memory buffer + existingFiles = entries; + centralDirImage = new byte[centralSize]; + zipFileStream.Seek( centralDirOffset, SeekOrigin.Begin ); + zipFileStream.Read( centralDirImage, 0, centralSize ); + + // Leave the pointer at the begining of central dir, to append new files + zipFileStream.Seek( centralDirOffset, SeekOrigin.Begin ); + return true; + } while( zipFileStream.Position > 0 ); + } catch { } + + return false; + } + #endregion + + #region IDisposable Members + /// + /// Closes the Zip file stream + /// + public void Dispose() { + Close(); + } + #endregion + } +} \ No newline at end of file diff --git a/UpdaterBuilder/app.config b/UpdaterBuilder/app.config new file mode 100644 index 0000000..a8afe94 --- /dev/null +++ b/UpdaterBuilder/app.config @@ -0,0 +1,3 @@ + + + diff --git a/f2k.ico b/f2k.ico new file mode 100644 index 0000000000000000000000000000000000000000..e3ed811f55cf6e80931eb4f239793052b23d550c GIT binary patch literal 61798 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 +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using JetBrains.Annotations; + +namespace fCraft.AutoRank { + public static class AutoRankManager { + + internal static readonly TimeSpan TickInterval = TimeSpan.FromSeconds( 60 ); + + public static readonly List Criteria = new List(); + + public const string TagName = "fCraftAutoRankConfig"; + + /// Whether any criteria are defined. + public static bool HasCriteria { + get { return Criteria.Count > 0; } + } + + + /// Adds a new criterion to the list. Throws an ArgumentException on duplicates. + public static void Add( [NotNull] Criterion criterion ) { + if( criterion == null ) throw new ArgumentNullException( "criterion" ); + if( Criteria.Contains( criterion ) ) throw new ArgumentException( "This criterion has already been added." ); + Criteria.Add( criterion ); + } + + + /// Checks whether a given player is due for a promotion or demotion. + /// PlayerInfo to check. + /// Null if no rank change is needed, or a rank to promote/demote to. + [CanBeNull] + public static Rank Check( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + // ReSharper disable LoopCanBeConvertedToQuery + for( int i = 0; i < Criteria.Count; i++ ) { + if( Criteria[i].FromRank == info.Rank && + !info.IsBanned && + Criteria[i].Condition.Eval( info ) ) { + + return Criteria[i].ToRank; + } + } + // ReSharper restore LoopCanBeConvertedToQuery + return null; + } + + + internal static void TaskCallback( SchedulerTask schedulerTask ) { + if( !ConfigKey.AutoRankEnabled.Enabled() ) return; + PlayerInfo[] onlinePlayers = Server.Players.Select( p => p.Info ).ToArray(); + MaintenanceCommands.DoAutoRankAll( Player.AutoRank, onlinePlayers, false, "~AutoRank" ); + } + + + public static bool Init() { + Criteria.Clear(); + + if( File.Exists( Paths.AutoRankFileName ) ) { + try { + XDocument doc = XDocument.Load( Paths.AutoRankFileName ); + if( doc.Root == null ) return false; + foreach( XElement el in doc.Root.Elements( "Criterion" ) ) { + try { + Add( new Criterion( el ) ); + } catch( Exception ex ) { + Logger.Log( LogType.Error, + "AutoRank.Init: Could not parse an AutoRank criterion: {0}", ex ); + } + } + if( Criteria.Count == 0 ) { + Logger.Log( LogType.Warning, "AutoRank.Init: No criteria loaded." ); + } + return true; + } catch( Exception ex ) { + Logger.Log( LogType.Error, + "AutoRank.Init: Could not parse the AutoRank file: {0}", ex ); + return false; + } + } else { + Logger.Log( LogType.Warning, "AutoRank.Init: autorank.xml not found. No criteria loaded." ); + return false; + } + } + } + + + #region Enums + + /// Operators used to compare PlayerInfo fields. + public enum ComparisonOp { + + /// EQuals to + Eq, + + /// Not EQual to + Neq, + + /// Greater Than + Gt, + + /// Greater Than or Equal + Gte, + + /// Less Than + Lt, + + /// Less Than or Equal + Lte + } + + + /// Enumeration of quantifiable PlayerInfo fields (or field combinations) that may be used with AutoRank conditions. + public enum ConditionField { + /// Time since first login (first time the player connected), in seconds. + /// For players who have been entered into PlayerDB but have never logged in, this is a huge value. + TimeSinceFirstLogin, + + /// Time since most recent login, in seconds. + /// For players who have been entered into PlayerDB but have never logged in, this is a huge value. + TimeSinceLastLogin, + + /// Time since player was last seen (0 if the player is online, otherwise time since last logout, in seconds). + /// For players who have been entered into PlayerDB but have never logged in, this is a huge value. + LastSeen, + + /// Total time spent on the server (including current session) in seconds. + /// For players who have been entered into PlayerDB but have never logged in, this is 0. + TotalTime, + + /// Number of blocks that were built manually (by clicking). + /// Does not include drawn or pasted blocks. + BlocksBuilt, + + /// Number of blocks deleted manually (by clicking). + /// Does not include drawn or cut blocks. + BlocksDeleted, + + /// Number of blocks changed (built + deleted) manually (by clicking). + /// Does not include drawn or cut/paste blocks. + BlocksChanged, + + /// Number of blocks affected by drawing commands, replacement, and cut/paste. + BlocksDrawn, + + /// Number of separate visits/sessions on this server. + TimesVisited, + + /// Number of messages written in chat. + /// Includes normal chat, PMs, rank chat, /Staff, /Say, and /Me messages. + MessagesWritten, + + /// Number of times kicked by other players or by console. + /// Does not include any kind of automated kicks (AFK kicks, anti-grief or anti-spam, server shutdown, etc). + TimesKicked, + + /// Time since last promotion or demotion, in seconds. + /// For new players (who still have the default rank) this is a huge value. + TimeSinceRankChange, + + /// Time since the player has been kicked by other players or by console. + /// Does not reset from any kind of automated kicks (AFK kicks, anti-grief or anti-spam, server shutdown, etc). + TimeSinceLastKick + } + + #endregion +} \ No newline at end of file diff --git a/fCraft/AutoRank/Conditions.cs b/fCraft/AutoRank/Conditions.cs new file mode 100644 index 0000000..0f47f3c --- /dev/null +++ b/fCraft/AutoRank/Conditions.cs @@ -0,0 +1,355 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using JetBrains.Annotations; + +namespace fCraft.AutoRank { + + /// Base class for all AutoRank conditions. + public abstract class Condition { + public abstract bool Eval( PlayerInfo info ); + + public static Condition Parse( XElement el ) { + switch( el.Name.ToString() ) { + case "AND": + return new ConditionAND( el ); + case "OR": + return new ConditionOR( el ); + case "NOR": + return new ConditionNOR( el ); + case "NAND": + return new ConditionNAND( el ); + case "ConditionIntRange": + return new ConditionIntRange( el ); + case "ConditionRankChangeType": + return new ConditionRankChangeType( el ); + case "ConditionPreviousRank": + return new ConditionPreviousRank( el ); + default: + throw new FormatException(); + } + } + + public abstract XElement Serialize(); + } + + + /// Class for checking ranges of countable PlayerInfo fields (see ConditionField enum). + public sealed class ConditionIntRange : Condition { + public ConditionField Field { get; set; } + public ComparisonOp Comparison { get; set; } + public int Value { get; set; } + + public ConditionIntRange() { + Comparison = ComparisonOp.Eq; + } + + public ConditionIntRange( [NotNull] XElement el ) + : this() { + // ReSharper disable PossibleNullReferenceException + if( el == null ) throw new ArgumentNullException( "el" ); + Field = (ConditionField)Enum.Parse( typeof( ConditionField ), el.Attribute( "field" ).Value, true ); + Value = Int32.Parse( el.Attribute( "val" ).Value ); + if( el.Attribute( "op" ) != null ) { + Comparison = (ComparisonOp)Enum.Parse( typeof( ComparisonOp ), el.Attribute( "op" ).Value, true ); + } + // ReSharper restore PossibleNullReferenceException + } + + + public ConditionIntRange( ConditionField field, ComparisonOp comparison, int value ) { + Field = field; + Comparison = comparison; + Value = value; + } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + long givenValue; + switch( Field ) { + case ConditionField.TimeSinceFirstLogin: + givenValue = (int)info.TimeSinceFirstLogin.TotalSeconds; + break; + case ConditionField.TimeSinceLastLogin: + givenValue = (int)info.TimeSinceLastLogin.TotalSeconds; + break; + case ConditionField.LastSeen: + givenValue = (int)info.TimeSinceLastSeen.TotalSeconds; + break; + case ConditionField.BlocksBuilt: + givenValue = info.BlocksBuilt; + break; + case ConditionField.BlocksDeleted: + givenValue = info.BlocksDeleted; + break; + case ConditionField.BlocksChanged: + givenValue = info.BlocksBuilt + info.BlocksDeleted; + break; + case ConditionField.BlocksDrawn: + givenValue = info.BlocksDrawn; + break; + case ConditionField.TimesVisited: + givenValue = info.TimesVisited; + break; + case ConditionField.MessagesWritten: + givenValue = info.MessagesWritten; + break; + case ConditionField.TimesKicked: + givenValue = info.TimesKicked; + break; + case ConditionField.TotalTime: + givenValue = (int)info.TotalTime.TotalSeconds; + break; + case ConditionField.TimeSinceRankChange: + givenValue = (int)info.TimeSinceRankChange.TotalSeconds; + break; + case ConditionField.TimeSinceLastKick: + givenValue = (int)info.TimeSinceLastKick.TotalSeconds; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch( Comparison ) { + case ComparisonOp.Lt: + return (givenValue < Value); + case ComparisonOp.Lte: + return (givenValue <= Value); + case ComparisonOp.Gte: + return (givenValue >= Value); + case ComparisonOp.Gt: + return (givenValue > Value); + case ComparisonOp.Eq: + return (givenValue == Value); + case ComparisonOp.Neq: + return (givenValue != Value); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override XElement Serialize() { + XElement el = new XElement( "ConditionIntRange" ); + el.Add( new XAttribute( "field", Field ) ); + el.Add( new XAttribute( "val", Value ) ); + el.Add( new XAttribute( "op", Comparison ) ); + return el; + } + + public override string ToString() { + return String.Format( "ConditionIntRange( {0} {1} {2} )", + Field, + Comparison, + Value ); + } + } + + + /// Checks what caused player's last rank change (see RankChangeType enum). + public sealed class ConditionRankChangeType : Condition { + public RankChangeType Type { get; set; } + + public ConditionRankChangeType( RankChangeType type ) { + Type = type; + } + + public ConditionRankChangeType( [NotNull] XElement el ) { + if( el == null ) throw new ArgumentNullException( "el" ); + // ReSharper disable PossibleNullReferenceException + Type = (RankChangeType)Enum.Parse( typeof( RankChangeType ), el.Attribute( "val" ).Value, true ); + // ReSharper restore PossibleNullReferenceException + } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + return (info.RankChangeType == Type); + } + + public override XElement Serialize() { + XElement el = new XElement( "ConditionRankChangeType" ); + el.Add( new XAttribute( "val", Type.ToString() ) ); + return el; + } + } + + + /// Checks what rank the player held previously. + public sealed class ConditionPreviousRank : Condition { + public Rank Rank { get; set; } + public ComparisonOp Comparison { get; set; } + + public ConditionPreviousRank( [NotNull] Rank rank, ComparisonOp comparison ) { + if( rank == null ) throw new ArgumentNullException( "rank" ); + if( !Enum.IsDefined( typeof( ComparisonOp ), comparison ) ) { + throw new ArgumentOutOfRangeException( "comparison", "Unknown comparison type" ); + } + Rank = rank; + Comparison = comparison; + } + + public ConditionPreviousRank( [NotNull] XElement el ) { + // ReSharper disable PossibleNullReferenceException + if( el == null ) throw new ArgumentNullException( "el" ); + Rank = Rank.Parse( el.Attribute( "val" ).Value ); + Comparison = (ComparisonOp)Enum.Parse( typeof( ComparisonOp ), el.Attribute( "op" ).Value, true ); + // ReSharper restore PossibleNullReferenceException + } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + Rank prevRank = info.PreviousRank ?? info.Rank; + switch( Comparison ) { + case ComparisonOp.Lt: + return (prevRank < Rank); + case ComparisonOp.Lte: + return (prevRank <= Rank); + case ComparisonOp.Gte: + return (prevRank >= Rank); + case ComparisonOp.Gt: + return (prevRank > Rank); + case ComparisonOp.Eq: + return (prevRank == Rank); + case ComparisonOp.Neq: + return (prevRank != Rank); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override XElement Serialize() { + XElement el = new XElement( "ConditionPreviousRank" ); + el.Add( new XAttribute( "val", Rank.FullName ) ); + el.Add( new XAttribute( "op", Comparison.ToString() ) ); + return el; + } + } + + + #region Condition Sets + + /// Base class for condition sets/combinations. + public class ConditionSet : Condition { + protected ConditionSet() { + Conditions = new List(); + } + + public List Conditions { get; private set; } + + protected ConditionSet( [NotNull] IEnumerable conditions ) { + if( conditions == null ) throw new ArgumentNullException( "conditions" ); + Conditions = conditions.ToList(); + } + + protected ConditionSet( [NotNull] XContainer el ) + : this() { + if( el == null ) throw new ArgumentNullException( "el" ); + foreach( XElement cel in el.Elements() ) { + Add( Parse( cel ) ); + } + } + + public override bool Eval( PlayerInfo info ) { + throw new NotImplementedException(); + } + + public void Add( [NotNull] Condition condition ) { + if( condition == null ) throw new ArgumentNullException( "condition" ); + Conditions.Add( condition ); + } + + public override XElement Serialize() { + throw new NotImplementedException(); + } + } + + + /// Logical AND - true if ALL conditions are true. + public sealed class ConditionAND : ConditionSet { + public ConditionAND() { } + public ConditionAND( IEnumerable conditions ) : base( conditions ) { } + public ConditionAND( XContainer el ) : base( el ) { } + + public override bool Eval( PlayerInfo info ) { + return Conditions == null || Conditions.All( t => t.Eval( info ) ); + } + + + public override XElement Serialize() { + XElement el = new XElement( "AND" ); + foreach( Condition cond in Conditions ) { + el.Add( cond.Serialize() ); + } + return el; + } + } + + + /// Logical AND - true if NOT ALL of the conditions are true. + public sealed class ConditionNAND : ConditionSet { + public ConditionNAND() { } + public ConditionNAND( IEnumerable conditions ) : base( conditions ) { } + public ConditionNAND( XContainer el ) : base( el ) { } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + return Conditions == null || Conditions.Any( t => !t.Eval( info ) ); + } + + + public override XElement Serialize() { + XElement el = new XElement( "NAND" ); + foreach( Condition cond in Conditions ) { + el.Add( cond.Serialize() ); + } + return el; + } + } + + + /// Logical AND - true if ANY of the conditions are true. + public sealed class ConditionOR : ConditionSet { + public ConditionOR() { } + public ConditionOR( IEnumerable conditions ) : base( conditions ) { } + public ConditionOR( XContainer el ) : base( el ) { } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + return Conditions == null || Conditions.Any( t => t.Eval( info ) ); + } + + + public override XElement Serialize() { + XElement el = new XElement( "OR" ); + foreach( Condition cond in Conditions ) { + el.Add( cond.Serialize() ); + } + return el; + } + } + + + /// Logical AND - true if NONE of the conditions are true. + public sealed class ConditionNOR : ConditionSet { + public ConditionNOR() { } + public ConditionNOR( IEnumerable conditions ) : base( conditions ) { } + public ConditionNOR( XContainer el ) : base( el ) { } + + public override bool Eval( [NotNull] PlayerInfo info ) { + if( info == null ) throw new ArgumentNullException( "info" ); + return Conditions == null || Conditions.All( t => !t.Eval( info ) ); + } + + + public override XElement Serialize() { + XElement el = new XElement( "NOR" ); + foreach( Condition cond in Conditions ) { + el.Add( cond.Serialize() ); + } + return el; + } + } + + #endregion +} \ No newline at end of file diff --git a/fCraft/AutoRank/Criterion.cs b/fCraft/AutoRank/Criterion.cs new file mode 100644 index 0000000..c2366d8 --- /dev/null +++ b/fCraft/AutoRank/Criterion.cs @@ -0,0 +1,68 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Linq; +using System.Xml.Linq; +using JetBrains.Annotations; + +namespace fCraft.AutoRank { + public sealed class Criterion : ICloneable { + public Rank FromRank { get; set; } + public Rank ToRank { get; set; } + public ConditionSet Condition { get; set; } + + public Criterion() { } + + public Criterion( [NotNull] Criterion other ) { + if( other == null ) throw new ArgumentNullException( "other" ); + FromRank = other.FromRank; + ToRank = other.ToRank; + Condition = other.Condition; + } + + public Criterion( [NotNull] Rank fromRank, [NotNull] Rank toRank, [NotNull] ConditionSet condition ) { + if( fromRank == null ) throw new ArgumentNullException( "fromRank" ); + if( toRank == null ) throw new ArgumentNullException( "toRank" ); + if( condition == null ) throw new ArgumentNullException( "condition" ); + FromRank = fromRank; + ToRank = toRank; + Condition = condition; + } + + public Criterion( [NotNull] XElement el ) { + if( el == null ) throw new ArgumentNullException( "el" ); + + // ReSharper disable PossibleNullReferenceException + FromRank = Rank.Parse( el.Attribute( "fromRank" ).Value ); + // ReSharper restore PossibleNullReferenceException + if( FromRank == null ) throw new FormatException( "Could not parse \"fromRank\"" ); + + // ReSharper disable PossibleNullReferenceException + ToRank = Rank.Parse( el.Attribute( "toRank" ).Value ); + // ReSharper restore PossibleNullReferenceException + if( ToRank == null ) throw new FormatException( "Could not parse \"toRank\"" ); + + Condition = (ConditionSet)AutoRank.Condition.Parse( el.Elements().First() ); + } + + public object Clone() { + return new Criterion( this ); + } + + public override string ToString() { + return String.Format( "Criteria( {0} from {1} to {2} )", + (FromRank < ToRank ? "promote" : "demote"), + FromRank.Name, + ToRank.Name ); + } + + public XElement Serialize() { + XElement el = new XElement( "Criterion" ); + el.Add( new XAttribute( "fromRank", FromRank.FullName ) ); + el.Add( new XAttribute( "toRank", ToRank.FullName ) ); + if( Condition != null ) { + el.Add( Condition.Serialize() ); + } + return el; + } + } +} diff --git a/fCraft/Commands/BuildingCommands.cs b/fCraft/Commands/BuildingCommands.cs new file mode 100644 index 0000000..28206ed --- /dev/null +++ b/fCraft/Commands/BuildingCommands.cs @@ -0,0 +1,1780 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Linq; +using fCraft.Drawing; +using fCraft.MapConversion; +using JetBrains.Annotations; + +namespace fCraft { + /// Commands for placing specific blocks (solid, water, grass), + /// and switching block placement modes (paint, bind). + static class BuildingCommands { + + public static int MaxUndoCount = 2000000; + + const string GeneralDrawingHelp = " Use &H/Cancel&S to cancel selection mode. " + + "Use &H/Undo&S to stop and undo the last command."; + + internal static void Init() { + CommandManager.RegisterCommand( CdBind ); + CommandManager.RegisterCommand( CdGrass ); + CommandManager.RegisterCommand( CdLava ); + CommandManager.RegisterCommand( CdPaint ); + CommandManager.RegisterCommand( CdSolid ); + CommandManager.RegisterCommand( CdWater ); + + CommandManager.RegisterCommand( CdCancel ); + CommandManager.RegisterCommand( CdMark ); + CommandManager.RegisterCommand( CdUndo ); + CommandManager.RegisterCommand( CdRedo ); + + CommandManager.RegisterCommand( CdReplace ); + CommandManager.RegisterCommand( CdReplaceNot ); + CommandManager.RegisterCommand( CdReplaceBrush ); + CdReplace.Help += GeneralDrawingHelp; + CdReplaceNot.Help += GeneralDrawingHelp; + CdReplaceBrush.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdCopySlot ); + CommandManager.RegisterCommand( CdCopy ); + CommandManager.RegisterCommand( CdCut ); + CommandManager.RegisterCommand( CdPaste ); + CommandManager.RegisterCommand( CdPasteNot ); + CommandManager.RegisterCommand( CdPasteX ); + CommandManager.RegisterCommand( CdPasteNotX ); + CommandManager.RegisterCommand( CdMirror ); + CommandManager.RegisterCommand( CdRotate ); + CdCut.Help += GeneralDrawingHelp; + CdPaste.Help += GeneralDrawingHelp; + CdPasteNot.Help += GeneralDrawingHelp; + CdPasteX.Help += GeneralDrawingHelp; + CdPasteNotX.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdRestore ); + CdRestore.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdCuboid ); + CommandManager.RegisterCommand( CdCuboidWireframe ); + CommandManager.RegisterCommand( CdCuboidHollow ); + CdCuboid.Help += GeneralDrawingHelp; + CdCuboidHollow.Help += GeneralDrawingHelp; + CdCuboidWireframe.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdEllipsoid ); + CommandManager.RegisterCommand( CdEllipsoidHollow ); + CdEllipsoid.Help += GeneralDrawingHelp; + CdEllipsoidHollow.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdLine ); + CommandManager.RegisterCommand( CdTriangle ); + CommandManager.RegisterCommand( CdTriangleWireframe ); + CdLine.Help += GeneralDrawingHelp; + CdTriangle.Help += GeneralDrawingHelp; + CdTriangleWireframe.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdSphere ); + CommandManager.RegisterCommand( CdSphereHollow ); + CommandManager.RegisterCommand( CdTorus ); + CdSphere.Help += GeneralDrawingHelp; + CdSphereHollow.Help += GeneralDrawingHelp; + CdTorus.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdFill2D ); + CdFill2D.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdUndoArea ); + CommandManager.RegisterCommand( CdUndoPlayer ); + CdUndoArea.Help += GeneralDrawingHelp; + + CommandManager.RegisterCommand( CdStatic ); + + //CommandManager.RegisterCommand( CdTree ); + } + + + #region DrawOperations & Brushes + + static readonly CommandDescriptor CdCuboid = new CommandDescriptor { + Name = "Cuboid", + Aliases = new[] { "blb", "c", "z" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Fills a rectangular area (cuboid) with blocks.", + Handler = CuboidHandler + }; + + static void CuboidHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new CuboidDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdCuboidWireframe = new CommandDescriptor { + Name = "CuboidW", + Aliases = new[] { "cubw", "cw", "bfb" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Draws a wireframe box (a frame) around the selected rectangular area.", + Handler = CuboidWireframeHandler + }; + + static void CuboidWireframeHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new CuboidWireframeDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdCuboidHollow = new CommandDescriptor { + Name = "CuboidH", + Aliases = new[] { "cubh", "ch", "h", "bhb" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Surrounds the selected rectangular area with a box of blocks. " + + "Unless two blocks are specified, leaves the inside untouched.", + Handler = CuboidHollowHandler + }; + + static void CuboidHollowHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new CuboidHollowDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdEllipsoid = new CommandDescriptor { + Name = "Ellipsoid", + Aliases = new[] { "e" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Fills an ellipsoid-shaped area (elongated sphere) with blocks.", + Handler = EllipsoidHandler + }; + + static void EllipsoidHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new EllipsoidDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdEllipsoidHollow = new CommandDescriptor { + Name = "EllipsoidH", + Aliases = new[] { "eh" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Surrounds the selected an ellipsoid-shaped area (elongated sphere) with a shell of blocks.", + Handler = EllipsoidHollowHandler + }; + + static void EllipsoidHollowHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new EllipsoidHollowDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdSphere = new CommandDescriptor { + Name = "Sphere", + Aliases = new[] { "sp", "spheroid" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw, Permission.DrawAdvanced }, + RepeatableSelection = true, + Help = "Fills a spherical area with blocks. " + + "The first mark denotes the CENTER of the sphere, and " + + "distance to the second mark denotes the radius.", + Handler = SphereHandler + }; + + static void SphereHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new SphereDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdSphereHollow = new CommandDescriptor { + Name = "SphereH", + Aliases = new[] { "sph", "hsphere" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw, Permission.DrawAdvanced }, + RepeatableSelection = true, + Help = "Surrounds a spherical area with a shell of blocks. " + + "The first mark denotes the CENTER of the sphere, and " + + "distance to the second mark denotes the radius.", + Handler = SphereHollowHandler + }; + + static void SphereHollowHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new SphereHollowDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdLine = new CommandDescriptor { + Name = "Line", + Aliases = new[] { "ln" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Draws a continuous line between two points with blocks. " + + "Marks do not have to be aligned.", + Handler = LineHandler + }; + + static void LineHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new LineDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdTriangleWireframe = new CommandDescriptor { + Name = "TriangleW", + Aliases = new[] { "tw" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Draws lines between three points, to form a triangle.", + Handler = TriangleWireframeHandler + }; + + static void TriangleWireframeHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new TriangleWireframeDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdTriangle = new CommandDescriptor { + Name = "Triangle", + Aliases = new[] { "t" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Help = "Draws a triangle between three points.", + Handler = TriangleHandler + }; + + static void TriangleHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new TriangleDrawOperation( player ) ); + } + + + + static readonly CommandDescriptor CdTorus = new CommandDescriptor { + Name = "Torus", + Aliases = new[] { "donut", "bagel" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw, Permission.DrawAdvanced }, + RepeatableSelection = true, + Help = "Draws a horizontally-oriented torus. The first mark denotes the CENTER of the torus, horizontal " + + "distance to the second mark denotes the ring radius, and the vertical distance to the second mark denotes the " + + "tube radius", + Handler = TorusHandler + }; + + static void TorusHandler( Player player, Command cmd ) { + DrawOperationBegin( player, cmd, new TorusDrawOperation( player ) ); + } + + + + static void DrawOperationBegin( Player player, Command cmd, DrawOperation op ) { + // try to create instance of player's currently selected brush + // all command parameters are passed to the brush + IBrushInstance brush = player.Brush.MakeInstance( player, cmd, op ); + + // MakeInstance returns null if there were problems with syntax, abort + if( brush == null ) return; + op.Brush = brush; + player.SelectionStart( op.ExpectedMarks, DrawOperationCallback, op, Permission.Draw ); + player.Message( "{0}: Click {1} blocks or use &H/Mark&S to make a selection.", + op.Description, op.ExpectedMarks ); + } + + + static void DrawOperationCallback( Player player, Vector3I[] marks, object tag ) { + DrawOperation op = (DrawOperation)tag; + if( !op.Prepare( marks ) ) return; + if( !player.CanDraw( op.BlocksTotalEstimate ) ) { + player.MessageNow( "You are only allowed to run draw commands that affect up to {0} blocks. This one would affect {1} blocks.", + player.Info.Rank.DrawLimit, + op.Bounds.Volume ); + op.Cancel(); + return; + } + player.Message( "{0}: Processing ~{1} blocks.", + op.Description, op.BlocksTotalEstimate ); + op.Begin(); + } + + #endregion + + + #region Fill + + static readonly CommandDescriptor CdFill2D = new CommandDescriptor { + Name = "Fill2D", + Aliases = new[] { "f2d" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw, Permission.DrawAdvanced }, + RepeatableSelection = true, + Help = "Fills a continuous area with blocks, in 2D. " + + "Takes just 1 mark, and replaces blocks of the same type as the block you clicked. " + + "Works similar to \"Paint Bucket\" tool in Photoshop. " + + "Direction of effect is determined by where the player is looking.", + Handler = Fill2DHandler + }; + + static void Fill2DHandler( Player player, Command cmd ) { + Fill2DDrawOperation op = new Fill2DDrawOperation( player ); + op.ReadParams( cmd ); + player.SelectionStart( 1, Fill2DCallback, op, Permission.Draw ); + player.Message( "{0}: Click a block to start filling.", op.Description ); + } + + + static void Fill2DCallback( Player player, Vector3I[] marks, object tag ) { + DrawOperation op = (DrawOperation)tag; + if( !op.Prepare( marks ) ) return; + if( player.WorldMap.GetBlock( marks[0] ) == Block.Air ) { + player.Confirm( Fill2DConfirmCallback, op, "{0}: Replace air?", op.Description ); + } else { + Fill2DConfirmCallback( player, op, false ); + } + } + + + static void Fill2DConfirmCallback( Player player, object tag, bool fromConsole ) { + Fill2DDrawOperation op = (Fill2DDrawOperation)tag; + player.Message( "{0}: Filling in a {1}x{1} area...", + op.Description, player.Info.Rank.FillLimit ); + op.Begin(); + } + + #endregion + + + #region Block Commands + + static readonly CommandDescriptor CdSolid = new CommandDescriptor { + Name = "Solid", + Aliases = new[] { "s" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.PlaceAdmincrete }, + Help = "Toggles the admincrete placement mode. When enabled, any stone block you place is replaced with admincrete.", + Handler = SolidHandler + }; + + static void SolidHandler( Player player, Command cmd ) { + if( player.GetBind( Block.Stone ) == Block.Admincrete ) { + player.ResetBind( Block.Stone ); + player.Message( "Solid: OFF" ); + } else { + player.Bind( Block.Stone, Block.Admincrete ); + player.Message( "Solid: ON. Stone blocks are replaced with admincrete." ); + } + } + + + + static readonly CommandDescriptor CdPaint = new CommandDescriptor { + Name = "Paint", + Aliases = new[] { "p" }, + Category = CommandCategory.Building, + Help = "When paint mode is on, any block you delete will be replaced with the block you are holding. " + + "Paint command toggles this behavior on and off.", + Handler = PaintHandler + }; + + static void PaintHandler( Player player, Command cmd ) { + player.IsPainting = !player.IsPainting; + if( player.IsPainting ) { + player.Message( "Paint mode: ON" ); + } else { + player.Message( "Paint mode: OFF" ); + } + } + + + + static readonly CommandDescriptor CdGrass = new CommandDescriptor { + Name = "Grass", + Aliases = new[] { "g" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.PlaceGrass }, + Help = "Toggles the grass placement mode. When enabled, any dirt block you place is replaced with a grass block.", + Handler = GrassHandler + }; + + static void GrassHandler( Player player, Command cmd ) { + if( player.GetBind( Block.Dirt ) == Block.Grass ) { + player.ResetBind( Block.Dirt ); + player.Message( "Grass: OFF" ); + } else { + player.Bind( Block.Dirt, Block.Grass ); + player.Message( "Grass: ON. Dirt blocks are replaced with grass." ); + } + } + + + + static readonly CommandDescriptor CdWater = new CommandDescriptor { + Name = "Water", + Aliases = new[] { "w" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.PlaceWater }, + Help = "Toggles the water placement mode. When enabled, any blue or cyan block you place is replaced with water.", + Handler = WaterHandler + }; + + static void WaterHandler( Player player, Command cmd ) { + if( player.GetBind( Block.Aqua ) == Block.Water || + player.GetBind( Block.Cyan ) == Block.Water || + player.GetBind( Block.Blue ) == Block.Water ) { + player.ResetBind( Block.Aqua, Block.Cyan, Block.Blue ); + player.Message( "Water: OFF" ); + } else { + player.Bind( Block.Aqua, Block.Water ); + player.Bind( Block.Cyan, Block.Water ); + player.Bind( Block.Blue, Block.Water ); + player.Message( "Water: ON. Blue blocks are replaced with water." ); + } + } + + + + static readonly CommandDescriptor CdLava = new CommandDescriptor { + Name = "Lava", + Aliases = new[] { "l" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.PlaceLava }, + Help = "Toggles the lava placement mode. When enabled, any red block you place is replaced with lava.", + Handler = LavaHandler + }; + + static void LavaHandler( Player player, Command cmd ) { + if( player.GetBind( Block.Red ) == Block.Lava ) { + player.ResetBind( Block.Red ); + player.Message( "Lava: OFF" ); + } else { + player.Bind( Block.Red, Block.Lava ); + player.Message( "Lava: ON. Red blocks are replaced with lava." ); + } + } + + + + static readonly CommandDescriptor CdBind = new CommandDescriptor { + Name = "Bind", + Category = CommandCategory.Building, + Permissions = new[] { Permission.Build }, + Help = "Assigns one blocktype to another. " + + "Allows to build blocktypes that are not normally buildable directly: admincrete, lava, water, grass, double step. " + + "Calling &H/Bind BlockType&S without second parameter resets the binding. If used with no params, ALL bindings are reset.", + Usage = "/Bind OriginalBlockType ReplacementBlockType", + Handler = BindHandler + }; + + static void BindHandler( Player player, Command cmd ) { + string originalBlockName = cmd.Next(); + if( originalBlockName == null ) { + player.Message( "All bindings have been reset." ); + player.ResetAllBinds(); + return; + } + Block originalBlock = Map.GetBlockByName( originalBlockName ); + if( originalBlock == Block.Undefined ) { + player.Message( "Bind: Unrecognized block name: {0}", originalBlockName ); + return; + } + + string replacementBlockName = cmd.Next(); + if( replacementBlockName == null ) { + if( player.GetBind( originalBlock ) != originalBlock ) { + player.Message( "{0} is no longer bound to {1}", + originalBlock, + player.GetBind( originalBlock ) ); + player.ResetBind( originalBlock ); + } else { + player.Message( "{0} is not bound to anything.", + originalBlock ); + } + return; + } + + if( cmd.HasNext ) { + CdBind.PrintUsage( player ); + return; + } + + Block replacementBlock = Map.GetBlockByName( replacementBlockName ); + if( replacementBlock == Block.Undefined ) { + player.Message( "Bind: Unrecognized block name: {0}", replacementBlockName ); + } else { + Permission permission = Permission.Build; + switch( replacementBlock ) { + case Block.Grass: + permission = Permission.PlaceGrass; + break; + case Block.Admincrete: + permission = Permission.PlaceAdmincrete; + break; + case Block.Water: + permission = Permission.PlaceWater; + break; + case Block.Lava: + permission = Permission.PlaceLava; + break; + } + if( player.Can( permission ) ) { + player.Bind( originalBlock, replacementBlock ); + player.Message( "{0} is now replaced with {1}", originalBlock, replacementBlock ); + } else { + player.Message( "&WYou do not have {0} permission.", permission ); + } + } + } + + #endregion + + + static void DrawOneBlock( [NotNull] Player player, [NotNull] Map map, Block drawBlock, Vector3I coord, + BlockChangeContext context, ref int blocks, ref int blocksDenied, UndoState undoState ) { + if( player == null ) throw new ArgumentNullException( "player" ); + + if( !map.InBounds( coord ) ) return; + Block block = map.GetBlock( coord ); + if( block == drawBlock ) return; + + if( player.CanPlace( map, coord, drawBlock, context ) != CanPlaceResult.Allowed ) { + blocksDenied++; + return; + } + + map.QueueUpdate( new BlockUpdate( null, coord, drawBlock ) ); + Player.RaisePlayerPlacedBlockEvent( player, map, coord, block, drawBlock, context ); + + if( !undoState.IsTooLargeToUndo ) { + if( !undoState.Add( coord, block ) ) { + player.MessageNow( "NOTE: This draw command is too massive to undo." ); + player.LastDrawOp = null; + } + } + blocks++; + } + + + static void DrawingFinished( [NotNull] Player player, string verb, int blocks, int blocksDenied ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( blocks == 0 ) { + if( blocksDenied > 0 ) { + player.MessageNow( "No blocks could be {0} due to permission issues.", verb.ToLower() ); + } else { + player.MessageNow( "No blocks were {0}.", verb.ToLower() ); + } + } else { + if( blocksDenied > 0 ) { + player.MessageNow( "{0} {1} blocks ({2} blocks skipped due to permission issues)... " + + "The map is now being updated.", verb, blocks, blocksDenied ); + } else { + player.MessageNow( "{0} {1} blocks... The map is now being updated.", verb, blocks ); + } + } + if( blocks > 0 ) { + player.Info.ProcessDrawCommand( blocks ); + Server.RequestGC(); + } + } + + + #region Replace + + static void ReplaceHandlerInternal( IBrush factory, Player player, Command cmd ) { + CuboidDrawOperation op = new CuboidDrawOperation( player ); + IBrushInstance brush = factory.MakeInstance( player, cmd, op ); + if( brush == null ) return; + op.Brush = brush; + + player.SelectionStart( 2, DrawOperationCallback, op, Permission.Draw ); + player.MessageNow( "{0}: Click 2 blocks or use &H/Mark&S to make a selection.", + op.Brush.InstanceDescription ); + } + + + static readonly CommandDescriptor CdReplace = new CommandDescriptor { + Name = "Replace", + Aliases = new[] { "r" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection=true, + Usage = "/Replace BlockToReplace [AnotherOne, ...] ReplacementBlock", + Help = "Replaces all blocks of specified type(s) in an area.", + Handler = ReplaceHandler + }; + + static void ReplaceHandler( Player player, Command cmd ) { + var replaceBrush = ReplaceBrushFactory.Instance.MakeBrush( player, cmd ); + if( replaceBrush == null ) return; + ReplaceHandlerInternal( replaceBrush, player, cmd ); + } + + + + static readonly CommandDescriptor CdReplaceNot = new CommandDescriptor { + Name = "ReplaceNot", + Aliases = new[] { "rn" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + RepeatableSelection = true, + Usage = "/ReplaceNot (ExcludedBlock [AnotherOne]) ReplacementBlock", + Help = "Replaces all blocks EXCEPT specified type(s) in an area.", + Handler = ReplaceNotHandler + }; + + static void ReplaceNotHandler( Player player, Command cmd ) { + var replaceBrush = ReplaceNotBrushFactory.Instance.MakeBrush( player, cmd ); + if( replaceBrush == null ) return; + ReplaceHandlerInternal( replaceBrush, player, cmd ); + } + + + + static readonly CommandDescriptor CdReplaceBrush = new CommandDescriptor { + Name = "ReplaceBrush", + Aliases = new[] { "rb" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw, Permission.DrawAdvanced }, + RepeatableSelection = true, + Usage = "/ReplaceBrush Block BrushName [Params]", + Help = "Replaces all blocks of specified type(s) in an area with output of a given brush. " + + "See &H/Help brush&S for a list of available brushes.", + Handler = ReplaceBrushHandler + }; + + static void ReplaceBrushHandler( Player player, Command cmd ) { + var replaceBrush = ReplaceBrushBrushFactory.Instance.MakeBrush( player, cmd ); + if( replaceBrush == null ) return; + ReplaceHandlerInternal( replaceBrush, player, cmd ); + } + #endregion + + + #region Undo / Redo + + static readonly CommandDescriptor CdUndo = new CommandDescriptor { + Name = "Undo", + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + Help = "Selectively removes changes from your last drawing command. " + + "Note that commands involving over 2 million blocks cannot be undone due to memory restrictions.", + Handler = UndoHandler + }; + + static void UndoHandler( Player player, Command cmd ) { + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + if( cmd.HasNext ) { + player.Message( "Undo command takes no parameters. Did you mean to do &H/UndoPlayer&S or &H/UndoArea&S?" ); + return; + } + + string msg = "Undo: "; + UndoState undoState = player.UndoPop(); + if( undoState == null ) { + player.MessageNow( "There is currently nothing to undo." ); + return; + } + + // Cancel the last DrawOp, if still in progress + if( undoState.Op != null && !undoState.Op.IsDone && !undoState.Op.IsCancelled ) { + undoState.Op.Cancel(); + msg += String.Format( "Cancelled {0} (was {1}% done). ", + undoState.Op.Description, + undoState.Op.PercentDone ); + } + + // Check if command was too massive. + if( undoState.IsTooLargeToUndo ) { + if( undoState.Op != null ) { + player.MessageNow( "Cannot undo {0}: too massive.", undoState.Op.Description ); + } else { + player.MessageNow( "Cannot undo: too massive." ); + } + return; + } + + // no need to set player.drawingInProgress here because this is done on the user thread + Logger.Log( LogType.UserActivity, + "Player {0} initiated /Undo affecting {1} blocks (on world {2})", + player.Name, + undoState.Buffer.Count, + playerWorld.Name ); + + msg += String.Format( "Undo: Restoring {0} blocks. Type &H/Redo&S to reverse.", + undoState.Buffer.Count ); + player.MessageNow( msg ); + + var op = new UndoDrawOperation( player, undoState, false ); + op.Prepare( new Vector3I[0] ); + op.Begin(); + } + + + static readonly CommandDescriptor CdRedo = new CommandDescriptor { + Name = "Redo", + Category = CommandCategory.Building, + Permissions = new[] { Permission.Draw }, + Help = "Selectively removes changes from your last drawing command. " + + "Note that commands involving over 2 million blocks cannot be undone due to memory restrictions.", + Handler = RedoHandler + }; + + static void RedoHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdRedo.PrintUsage( player ); + return; + } + + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + + UndoState redoState = player.RedoPop(); + if( redoState == null ) { + player.MessageNow( "There is currently nothing to redo." ); + return; + } + + string msg = "Redo: "; + if( redoState.Op != null && !redoState.Op.IsDone ) { + redoState.Op.Cancel(); + msg += String.Format( "Cancelled {0} (was {1}% done). ", + redoState.Op.Description, + redoState.Op.PercentDone ); + } + + // no need to set player.drawingInProgress here because this is done on the user thread + Logger.Log( LogType.UserActivity, + "Player {0} initiated /Redo affecting {1} blocks (on world {2})", + player.Name, + redoState.Buffer.Count, + playerWorld.Name ); + + msg += String.Format( "Redo: Restoring {0} blocks. Type &H/Undo&S to reverse.", + redoState.Buffer.Count ); + player.MessageNow( msg ); + + var op = new UndoDrawOperation( player, redoState, true ); + op.Prepare( new Vector3I[0] ); + op.Begin(); + } + + #endregion + + + #region Copy and Paste + + static readonly CommandDescriptor CdCopySlot = new CommandDescriptor { + Name = "CopySlot", + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + Usage = "/CopySlot [#]", + Help = "Selects a slot to copy to/paste from. The maximum number of slots is limited per-rank.", + Handler = CopySlotHandler + }; + + static void CopySlotHandler( Player player, Command cmd ) { + int slotNumber; + if( cmd.NextInt( out slotNumber ) ) { + if( cmd.HasNext ) { + CdCopySlot.PrintUsage( player ); + return; + } + if( slotNumber < 1 || slotNumber > player.Info.Rank.CopySlots ) { + player.Message( "CopySlot: Select a number between 1 and {0}", player.Info.Rank.CopySlots ); + } else { + player.CopySlot = slotNumber - 1; + CopyState info = player.GetCopyInformation(); + if( info == null ) { + player.Message( "Selected copy slot {0} (unused).", slotNumber ); + } else { + player.Message( "Selected copy slot {0}: {1} blocks from {2}, {3} old.", + slotNumber, info.Buffer.Length, + info.OriginWorld, DateTime.UtcNow.Subtract( info.CopyTime ).ToMiniString() ); + } + } + } else { + CopyState[] slots = player.CopyInformation; + player.Message( "Using {0} of {1} slots. Selected slot: {2}", + slots.Count( info => info != null ), player.Info.Rank.CopySlots, player.CopySlot + 1 ); + for( int i = 0; i < slots.Length; i++ ) { + if( slots[i] != null ) { + player.Message( " {0}: {1} blocks from {2}, {3} old", + i + 1, slots[i].Buffer.Length, + slots[i].OriginWorld, DateTime.UtcNow.Subtract( slots[i].CopyTime ).ToMiniString() ); + } + } + } + } + + + + static readonly CommandDescriptor CdCopy = new CommandDescriptor { + Name = "Copy", + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + Help = "Copy blocks for pasting. " + + "Used together with &H/Paste&S and &H/PasteNot&S commands. " + + "Note that pasting starts at the same corner that you started &H/Copy&S from.", + Handler = CopyHandler + }; + + static void CopyHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdCopy.PrintUsage( player ); + return; + } + player.SelectionStart( 2, CopyCallback, null, CdCopy.Permissions ); + player.MessageNow( "Copy: Place a block or type /Mark to use your location." ); + } + + + static void CopyCallback( Player player, Vector3I[] marks, object tag ) { + int sx = Math.Min( marks[0].X, marks[1].X ); + int ex = Math.Max( marks[0].X, marks[1].X ); + int sy = Math.Min( marks[0].Y, marks[1].Y ); + int ey = Math.Max( marks[0].Y, marks[1].Y ); + int sz = Math.Min( marks[0].Z, marks[1].Z ); + int ez = Math.Max( marks[0].Z, marks[1].Z ); + BoundingBox bounds = new BoundingBox( sx, sy, sz, ex, ey, ez ); + + int volume = bounds.Volume; + if( !player.CanDraw( volume ) ) { + player.MessageNow( String.Format( "You are only allowed to run commands that affect up to {0} blocks. This one would affect {1} blocks.", + player.Info.Rank.DrawLimit, volume ) ); + return; + } + + // remember dimensions and orientation + CopyState copyInfo = new CopyState( marks[0], marks[1] ); + + Map map = player.WorldMap; + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + + for( int x = sx; x <= ex; x++ ) { + for( int y = sy; y <= ey; y++ ) { + for( int z = sz; z <= ez; z++ ) { + copyInfo.Buffer[x - sx, y - sy, z - sz] = map.GetBlock( x, y, z ); + } + } + } + + copyInfo.OriginWorld = playerWorld.Name; + copyInfo.CopyTime = DateTime.UtcNow; + player.SetCopyInformation( copyInfo ); + + player.MessageNow( "{0} blocks copied into slot #{1}. You can now &H/Paste", + volume, player.CopySlot + 1 ); + player.MessageNow( "Origin at {0} {1}{2} corner.", + (copyInfo.Orientation.X == 1 ? "bottom" : "top"), + (copyInfo.Orientation.Y == 1 ? "south" : "north"), + (copyInfo.Orientation.Z == 1 ? "east" : "west") ); + + Logger.Log( LogType.UserActivity, + "{0} copied {1} blocks from {2} (between {3} and {4}).", + player.Name, volume, playerWorld.Name, + bounds.MinVertex, bounds.MaxVertex ); + } + + + + static readonly CommandDescriptor CdCut = new CommandDescriptor { + Name = "Cut", + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + RepeatableSelection = true, + Help = "Copies and removes blocks for pasting. Unless a different block type is specified, the area is filled with air. " + + "Used together with &H/Paste&S and &H/PasteNot&S commands. " + + "Note that pasting starts at the same corner that you started &H/Cut&S from.", + Usage = "/Cut [FillBlock]", + Handler = CutHandler + }; + + static void CutHandler( Player player, Command cmd ) { + Block fillBlock = Block.Air; + if( cmd.HasNext ) { + fillBlock = cmd.NextBlock( player ); + if( fillBlock == Block.Undefined ) return; + if( cmd.HasNext ) { + CdCut.PrintUsage( player ); + return; + } + } + + CutDrawOperation op = new CutDrawOperation( player ) { + Brush = new NormalBrush( fillBlock ) + }; + + player.SelectionStart( 2, DrawOperationCallback, op, Permission.Draw ); + if( fillBlock != Block.Air ) { + player.Message( "Cut/{0}: Click 2 blocks or use &H/Mark&S to make a selection.", + fillBlock ); + } else { + player.Message( "Cut: Click 2 blocks or use &H/Mark&S to make a selection." ); + } + } + + + static readonly CommandDescriptor CdMirror = new CommandDescriptor { + Name = "Mirror", + Aliases = new[] { "flip" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + Help = "Flips copied blocks along specified axis/axes. " + + "The axes are: X = horizontal (east-west), Y = horizontal (north-south), Z = vertical. " + + "You can mirror more than one axis at a time, e.g. &H/Mirror X Y", + Usage = "/Mirror [X] [Y] [Z]", + Handler = MirrorHandler + }; + + static void MirrorHandler( Player player, Command cmd ) { + CopyState originalInfo = player.GetCopyInformation(); + if( originalInfo == null ) { + player.MessageNow( "Nothing to flip! Copy something first." ); + return; + } + + // clone to avoid messing up any paste-in-progress + CopyState info = new CopyState( originalInfo ); + + bool flipX = false, flipY = false, flipH = false; + string axis; + while( (axis = cmd.Next()) != null ) { + foreach( char c in axis.ToLower() ) { + if( c == 'x' ) flipX = true; + if( c == 'y' ) flipY = true; + if( c == 'z' ) flipH = true; + } + } + + if( !flipX && !flipY && !flipH ) { + CdMirror.PrintUsage( player ); + return; + } + + Block block; + + if( flipX ) { + int left = 0; + int right = info.Dimensions.X - 1; + while( left < right ) { + for( int y = info.Dimensions.Y - 1; y >= 0; y-- ) { + for( int z = info.Dimensions.Z - 1; z >= 0; z-- ) { + block = info.Buffer[left, y, z]; + info.Buffer[left, y, z] = info.Buffer[right, y, z]; + info.Buffer[right, y, z] = block; + } + } + left++; + right--; + } + } + + if( flipY ) { + int left = 0; + int right = info.Dimensions.Y - 1; + while( left < right ) { + for( int x = info.Dimensions.X - 1; x >= 0; x-- ) { + for( int z = info.Dimensions.Z - 1; z >= 0; z-- ) { + block = info.Buffer[x, left, z]; + info.Buffer[x, left, z] = info.Buffer[x, right, z]; + info.Buffer[x, right, z] = block; + } + } + left++; + right--; + } + } + + if( flipH ) { + int left = 0; + int right = info.Dimensions.Z - 1; + while( left < right ) { + for( int x = info.Dimensions.X - 1; x >= 0; x-- ) { + for( int y = info.Dimensions.Y - 1; y >= 0; y-- ) { + block = info.Buffer[x, y, left]; + info.Buffer[x, y, left] = info.Buffer[x, y, right]; + info.Buffer[x, y, right] = block; + } + } + left++; + right--; + } + } + + if( flipX ) { + if( flipY ) { + if( flipH ) { + player.Message( "Flipped copy along all axes." ); + } else { + player.Message( "Flipped copy along X (east/west) and Y (north/south) axes." ); + } + } else { + if( flipH ) { + player.Message( "Flipped copy along X (east/west) and Z (vertical) axes." ); + } else { + player.Message( "Flipped copy along X (east/west) axis." ); + } + } + } else { + if( flipY ) { + if( flipH ) { + player.Message( "Flipped copy along Y (north/south) and Z (vertical) axes." ); + } else { + player.Message( "Flipped copy along Y (north/south) axis." ); + } + } else { + player.Message( "Flipped copy along Z (vertical) axis." ); + } + } + + player.SetCopyInformation( info ); + } + + + + static readonly CommandDescriptor CdRotate = new CommandDescriptor { + Name = "Rotate", + Aliases = new[] { "spin" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + Help = "Rotates copied blocks around specifies axis/axes. If no axis is given, rotates around Z (vertical).", + Usage = "/Rotate (-90|90|180|270) (X|Y|Z)", + Handler = RotateHandler + }; + + static void RotateHandler( Player player, Command cmd ) { + CopyState originalInfo = player.GetCopyInformation(); + if( originalInfo == null ) { + player.MessageNow( "Nothing to rotate! Copy something first." ); + return; + } + + int degrees; + if( !cmd.NextInt( out degrees ) || (degrees != 90 && degrees != -90 && degrees != 180 && degrees != 270) ) { + CdRotate.PrintUsage( player ); + return; + } + + string axisName = cmd.Next(); + Axis axis = Axis.Z; + if( axisName != null ) { + switch( axisName.ToLower() ) { + case "x": + axis = Axis.X; + break; + case "y": + axis = Axis.Y; + break; + case "z": + case "h": + axis = Axis.Z; + break; + default: + CdRotate.PrintUsage( player ); + return; + } + } + + // allocate the new buffer + Block[, ,] oldBuffer = originalInfo.Buffer; + Block[, ,] newBuffer; + + if( degrees == 180 ) { + newBuffer = new Block[oldBuffer.GetLength( 0 ), oldBuffer.GetLength( 1 ), oldBuffer.GetLength( 2 )]; + + } else if( axis == Axis.X ) { + newBuffer = new Block[oldBuffer.GetLength( 0 ), oldBuffer.GetLength( 2 ), oldBuffer.GetLength( 1 )]; + + } else if( axis == Axis.Y ) { + newBuffer = new Block[oldBuffer.GetLength( 2 ), oldBuffer.GetLength( 1 ), oldBuffer.GetLength( 0 )]; + + } else { // axis == Axis.Z + newBuffer = new Block[oldBuffer.GetLength( 1 ), oldBuffer.GetLength( 0 ), oldBuffer.GetLength( 2 )]; + } + + // clone to avoid messing up any paste-in-progress + CopyState info = new CopyState( originalInfo, newBuffer ); + + // construct the rotation matrix + int[,] matrix = new[,]{ + {1,0,0}, + {0,1,0}, + {0,0,1} + }; + + int a, b; + switch( axis ) { + case Axis.X: + a = 1; + b = 2; + break; + case Axis.Y: + a = 0; + b = 2; + break; + default: + a = 0; + b = 1; + break; + } + + switch( degrees ) { + case 90: + matrix[a, a] = 0; + matrix[b, b] = 0; + matrix[a, b] = -1; + matrix[b, a] = 1; + break; + case 180: + matrix[a, a] = -1; + matrix[b, b] = -1; + break; + case -90: + case 270: + matrix[a, a] = 0; + matrix[b, b] = 0; + matrix[a, b] = 1; + matrix[b, a] = -1; + break; + } + + // apply the rotation matrix + for( int x = oldBuffer.GetLength( 0 ) - 1; x >= 0; x-- ) { + for( int y = oldBuffer.GetLength( 1 ) - 1; y >= 0; y-- ) { + for( int z = oldBuffer.GetLength( 2 ) - 1; z >= 0; z-- ) { + int nx = (matrix[0, 0] < 0 ? oldBuffer.GetLength( 0 ) - 1 - x : (matrix[0, 0] > 0 ? x : 0)) + + (matrix[0, 1] < 0 ? oldBuffer.GetLength( 1 ) - 1 - y : (matrix[0, 1] > 0 ? y : 0)) + + (matrix[0, 2] < 0 ? oldBuffer.GetLength( 2 ) - 1 - z : (matrix[0, 2] > 0 ? z : 0)); + int ny = (matrix[1, 0] < 0 ? oldBuffer.GetLength( 0 ) - 1 - x : (matrix[1, 0] > 0 ? x : 0)) + + (matrix[1, 1] < 0 ? oldBuffer.GetLength( 1 ) - 1 - y : (matrix[1, 1] > 0 ? y : 0)) + + (matrix[1, 2] < 0 ? oldBuffer.GetLength( 2 ) - 1 - z : (matrix[1, 2] > 0 ? z : 0)); + int nz = (matrix[2, 0] < 0 ? oldBuffer.GetLength( 0 ) - 1 - x : (matrix[2, 0] > 0 ? x : 0)) + + (matrix[2, 1] < 0 ? oldBuffer.GetLength( 1 ) - 1 - y : (matrix[2, 1] > 0 ? y : 0)) + + (matrix[2, 2] < 0 ? oldBuffer.GetLength( 2 ) - 1 - z : (matrix[2, 2] > 0 ? z : 0)); + newBuffer[nx, ny, nz] = oldBuffer[x, y, z]; + } + } + } + + player.Message( "Rotated copy (slot {0}) by {1} degrees around {2} axis.", + info.Slot + 1, degrees, axis ); + player.SetCopyInformation( info ); + } + + + + static readonly CommandDescriptor CdPasteX = new CommandDescriptor { + Name = "PasteX", + Aliases = new[] { "px" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + RepeatableSelection = true, + Help = "Pastes previously copied blocks, aligned. Used together with &H/Copy&S command. " + + "If one or more optional IncludedBlock parameters are specified, ONLY pastes blocks of specified type(s). " + + "Takes 2 marks: first sets the origin of pasting, and second sets the direction where to paste.", + Usage = "/PasteX [IncludedBlock [AnotherOne etc]]", + Handler = PasteXHandler + }; + + static void PasteXHandler( Player player, Command cmd ) { + PasteDrawOperation op = new PasteDrawOperation( player, false ); + if( !op.ReadParams( cmd ) ) return; + player.SelectionStart( 2, DrawOperationCallback, op, Permission.Draw, Permission.CopyAndPaste ); + player.MessageNow( "{0}: Click 2 blocks or use &H/Mark&S to make a selection.", + op.Description ); + } + + + + static readonly CommandDescriptor CdPasteNotX = new CommandDescriptor { + Name = "PasteNotX", + Aliases = new[] { "pnx", "pxn" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + RepeatableSelection = true, + Help = "Pastes previously copied blocks, aligned, except the given block type(s). " + + "Used together with &H/Copy&S command. " + + "Takes 2 marks: first sets the origin of pasting, and second sets the direction where to paste.", + Usage = "/PasteNotX ExcludedBlock [AnotherOne etc]", + Handler = PasteNotXHandler + }; + + static void PasteNotXHandler( Player player, Command cmd ) { + PasteDrawOperation op = new PasteDrawOperation( player, true ); + if( !op.ReadParams( cmd ) ) return; + player.SelectionStart( 2, DrawOperationCallback, op, Permission.Draw, Permission.CopyAndPaste ); + player.MessageNow( "{0}: Click 2 blocks or use &H/Mark&S to make a selection.", + op.Description ); + } + + + + static readonly CommandDescriptor CdPaste = new CommandDescriptor { + Name = "Paste", + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + RepeatableSelection = true, + Help = "Pastes previously copied blocks. Used together with &H/Copy&S command. " + + "If one or more optional IncludedBlock parameters are specified, ONLY pastes blocks of specified type(s). " + + "Alignment semantics are... complicated.", + Usage = "/Paste [IncludedBlock [AnotherOne etc]]", + Handler = PasteHandler + }; + + static void PasteHandler( Player player, Command cmd ) { + QuickPasteDrawOperation op = new QuickPasteDrawOperation( player, false ); + if( !op.ReadParams( cmd ) ) return; + player.SelectionStart( 1, DrawOperationCallback, op, Permission.Draw, Permission.CopyAndPaste ); + player.MessageNow( "{0}: Click a block or use &H/Mark&S to begin pasting.", + op.Description ); + } + + + + static readonly CommandDescriptor CdPasteNot = new CommandDescriptor { + Name = "PasteNot", + Aliases = new[] { "pn" }, + Category = CommandCategory.Building, + Permissions = new[] { Permission.CopyAndPaste }, + RepeatableSelection = true, + Help = "Pastes previously copied blocks, except the given block type(s). " + + "Used together with &H/Copy&S command. " + + "Alignment semantics are... complicated.", + Usage = "/PasteNot ExcludedBlock [AnotherOne etc]", + Handler = PasteNotHandler + }; + + static void PasteNotHandler( Player player, Command cmd ) { + QuickPasteDrawOperation op = new QuickPasteDrawOperation( player, true ); + if( !op.ReadParams( cmd ) ) return; + player.SelectionStart( 1, DrawOperationCallback, op, Permission.Draw, Permission.CopyAndPaste ); + player.MessageNow( "{0}: Click a block or use &H/Mark&S to begin pasting.", + op.Description ); + } + + #endregion + + + #region Restore + + const BlockChangeContext RestoreContext = BlockChangeContext.Drawn | BlockChangeContext.Restored; + + + static readonly CommandDescriptor CdRestore = new CommandDescriptor { + Name = "Restore", + Category = CommandCategory.World, + Permissions = new[] { + Permission.Draw, + Permission.DrawAdvanced, + Permission.CopyAndPaste, + Permission.ManageWorlds + }, + RepeatableSelection = true, + Usage = "/Restore FileName", + Help = "Selectively restores/pastes part of mapfile into the current world. "+ + "If the filename contains spaces, surround it with quote marks.", + Handler = RestoreHandler + }; + + static void RestoreHandler( Player player, Command cmd ) { + string fileName = cmd.Next(); + if( fileName == null ) { + CdRestore.PrintUsage( player ); + return; + } + if( cmd.HasNext ) { + CdRestore.PrintUsage( player ); + return; + } + + string fullFileName = WorldManager.FindMapFile( player, fileName ); + if( fullFileName == null ) return; + + Map map; + if( !MapUtility.TryLoad( fullFileName, out map ) ) { + player.Message( "Could not load the given map file ({0})", fileName ); + return; + } + + Map playerMap = player.WorldMap; + if( playerMap.Width != map.Width || playerMap.Length != map.Length || playerMap.Height != map.Height ) { + player.Message( "Mapfile dimensions must match your current world's dimensions ({0}x{1}x{2})", + playerMap.Width, + playerMap.Length, + playerMap.Height ); + return; + } + + map.Metadata["fCraft.Temp", "FileName"] = fullFileName; + player.SelectionStart( 2, RestoreCallback, map, CdRestore.Permissions ); + player.MessageNow( "Restore: Select the area to restore. To mark a corner, place/click a block or type &H/Mark" ); + } + + + static void RestoreCallback( Player player, Vector3I[] marks, object tag ) { + BoundingBox selection = new BoundingBox( marks[0], marks[1] ); + Map map = (Map)tag; + + if( !player.CanDraw( selection.Volume ) ) { + player.MessageNow( + "You are only allowed to restore up to {0} blocks at a time. This would affect {1} blocks.", + player.Info.Rank.DrawLimit, + selection.Volume ); + return; + } + + int blocksDrawn = 0, + blocksSkipped = 0; + UndoState undoState = player.DrawBegin( null ); + + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + Map playerMap = player.WorldMap; + for( int x = selection.XMin; x <= selection.XMax; x++ ) { + for( int y = selection.YMin; y <= selection.YMax; y++ ) { + for( int z = selection.ZMin; z <= selection.ZMax; z++ ) { + DrawOneBlock( player, playerMap, map.GetBlock( x, y, z ), new Vector3I( x, y, z ), + RestoreContext, + ref blocksDrawn, ref blocksSkipped, undoState ); + } + } + } + + Logger.Log( LogType.UserActivity, + "{0} restored {1} blocks on world {2} (@{3},{4},{5} - {6},{7},{8}) from file {9}.", + player.Name, blocksDrawn, + playerWorld.Name, + selection.XMin, selection.YMin, selection.ZMin, + selection.XMax, selection.YMax, selection.ZMax, + map.Metadata["fCraft.Temp", "FileName"] ); + + DrawingFinished( player, "Restored", blocksDrawn, blocksSkipped ); + } + + #endregion + + + #region Mark, Cancel + + static readonly CommandDescriptor CdMark = new CommandDescriptor { + Name = "Mark", + Aliases = new[] { "m" }, + Category = CommandCategory.Building, + Usage = "/Mark&S or &H/Mark X Y H", + Help = "When making a selection (for drawing or zoning) use this to make a marker at your position in the world. " + + "If three numbers are given, those coordinates are used instead.", + Handler = MarkHandler + }; + + static void MarkHandler( Player player, Command cmd ) { + Map map = player.WorldMap; + int x, y, z; + Vector3I coords; + if( cmd.NextInt( out x ) && cmd.NextInt( out y ) && cmd.NextInt( out z ) ) { + if( cmd.HasNext ) { + CdMark.PrintUsage( player ); + return; + } + coords = new Vector3I( x, y, z ); + } else { + coords = player.Position.ToBlockCoords(); + } + coords.X = Math.Min( map.Width - 1, Math.Max( 0, coords.X ) ); + coords.Y = Math.Min( map.Length - 1, Math.Max( 0, coords.Y ) ); + coords.Z = Math.Min( map.Height - 1, Math.Max( 0, coords.Z ) ); + + if( player.SelectionMarksExpected > 0 ) { + player.SelectionAddMark( coords, true ); + } else { + player.MessageNow( "Cannot mark - no selection in progress." ); + } + } + + + + static readonly CommandDescriptor CdCancel = new CommandDescriptor { + Name = "Cancel", + Category = CommandCategory.Building, + NotRepeatable = true, + Help = "Cancels current selection (for drawing or zoning) operation, for instance if you misclicked on the first block. " + + "If you wish to stop a drawing in-progress, use &H/Undo&S instead.", + Handler = CancelHandler + }; + + static void CancelHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdCancel.PrintUsage( player ); + return; + } + if( player.IsMakingSelection ) { + player.SelectionCancel(); + player.MessageNow( "Selection cancelled." ); + } else { + player.MessageNow( "There is currently nothing to cancel." ); + } + } + + #endregion + + + #region UndoPlayer and UndoArea + + struct UndoAreaCountArgs { + [NotNull] + public PlayerInfo Target; + + [NotNull] + public World World; + + public int MaxBlocks; + + [NotNull] + public BoundingBox Area; + } + + struct UndoAreaTimeArgs { + [NotNull] + public PlayerInfo Target; + + [NotNull] + public World World; + + public TimeSpan Time; + + [NotNull] + public BoundingBox Area; + } + + static readonly CommandDescriptor CdUndoArea = new CommandDescriptor { + Name = "UndoArea", + Aliases = new[] { "ua" }, + Category = CommandCategory.Moderation, + Permissions = new[] { Permission.UndoOthersActions }, + RepeatableSelection = true, + Usage = "/UndoArea PlayerName [TimeSpan|BlockCount]", + Help = "Reverses changes made by a given player in the current world, in the given area.", + Handler = UndoAreaHandler + }; + + static void UndoAreaHandler( Player player, Command cmd ) { + + if( !BlockDB.IsEnabledGlobally ) { + player.Message( "&WBlockDB is disabled on this server." ); + return; + } + + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + if( !playerWorld.BlockDB.IsEnabled ) { + player.Message( "&WBlockDB is disabled in this world." ); + return; + } + + string name = cmd.Next(); + string range = cmd.Next(); + if( name == null || range == null ) { + CdUndoArea.PrintUsage( player ); + return; + } + if( cmd.HasNext ) { + CdUndoArea.PrintUsage( player ); + return; + } + + PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, name ); + if( target == null ) return; + + if( player.Info != target && !player.Can( Permission.UndoOthersActions, target.Rank ) ) { + player.Message( "You may only undo actions of players ranked {0}&S or lower.", + player.Info.Rank.GetLimit( Permission.UndoOthersActions ).ClassyName ); + player.Message( "Player {0}&S is ranked {1}", target.ClassyName, target.Rank.ClassyName ); + return; + } + + int count; + TimeSpan span; + if( Int32.TryParse( range, out count ) ) { + UndoAreaCountArgs args = new UndoAreaCountArgs { + Target = target, + World = playerWorld, + MaxBlocks = count + }; + player.SelectionStart( 2, UndoAreaCountSelectionCallback, args, Permission.UndoOthersActions ); + + } else if( range.TryParseMiniTimespan( out span ) ) { + if( span > DateTimeUtil.MaxTimeSpan ) { + player.MessageMaxTimeSpan(); + return; + } + UndoAreaTimeArgs args = new UndoAreaTimeArgs { + Target = target, + Time = span, + World = playerWorld + }; + player.SelectionStart( 2, UndoAreaTimeSelectionCallback, args, Permission.UndoOthersActions ); + + } else { + CdUndoArea.PrintUsage( player ); + return; + } + + player.MessageNow( "UndoArea: Click 2 blocks or use &H/Mark&S to make a selection." ); + } + + + static void UndoAreaCountSelectionCallback( Player player, Vector3I[] marks, object tag ) { + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + + UndoAreaCountArgs args = (UndoAreaCountArgs)tag; + args.World = playerWorld; + args.Area = new BoundingBox( marks[0], marks[1] ); + BlockDBEntry[] changes = playerWorld.BlockDB.Lookup( args.Target, args.Area, args.MaxBlocks ); + if( changes.Length > 0 ) { + player.Confirm( UndoAreaCountConfirmCallback, args, "Undo last {0} changes made by player {1}&S in this area?", + changes.Length, args.Target.ClassyName ); + } else { + player.Message( "UndoArea: Nothing to undo in this area." ); + } + } + + + static void UndoAreaTimeSelectionCallback( Player player, Vector3I[] marks, object tag ) { + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + + UndoAreaTimeArgs args = (UndoAreaTimeArgs)tag; + args.World = playerWorld; + args.Area = new BoundingBox( marks[0], marks[1] ); + BlockDBEntry[] changes = playerWorld.BlockDB.Lookup( args.Target, args.Area, args.Time ); + if( changes.Length > 0 ) { + player.Confirm( UndoAreaTimeConfirmCallback, args, "Undo changes ({0}) made by {1}&S in this area in the last {2}?", + changes.Length, args.Target.ClassyName, args.Time.ToMiniString() ); + } else { + player.Message( "UndoArea: Nothing to undo in this area." ); + } + } + + + static void UndoAreaCountConfirmCallback( Player player, object tag, bool fromConsole ) { + UndoAreaCountArgs args = (UndoAreaCountArgs)tag; + BlockDBEntry[] changes = args.World.BlockDB.Lookup( args.Target, args.Area, args.MaxBlocks ); + + BlockChangeContext context = BlockChangeContext.Drawn; + if( player.Info == args.Target ) { + context |= BlockChangeContext.UndoneSelf; + } else { + context |= BlockChangeContext.UndoneOther; + } + + int blocks = 0, + blocksDenied = 0; + + UndoState undoState = player.DrawBegin( null ); + Map map = player.WorldMap; + + for( int i = 0; i < changes.Length; i++ ) { + DrawOneBlock( player, map, changes[i].OldBlock, + changes[i].Coord, context, + ref blocks, ref blocksDenied, undoState ); + } + + Logger.Log( LogType.UserActivity, + "{0} undid {1} blocks changed by player {2} (in a selection on world {3})", + player.Name, + blocks, + args.Target.Name, + args.World.Name ); + + DrawingFinished( player, "UndoArea'd", blocks, blocksDenied ); + } + + + static void UndoAreaTimeConfirmCallback( Player player, object tag, bool fromConsole ) { + UndoAreaTimeArgs args = (UndoAreaTimeArgs)tag; + BlockDBEntry[] changes = args.World.BlockDB.Lookup( args.Target, args.Area, args.Time ); + + BlockChangeContext context = BlockChangeContext.Drawn; + if( player.Info == args.Target ) { + context |= BlockChangeContext.UndoneSelf; + } else { + context |= BlockChangeContext.UndoneOther; + } + + int blocks = 0, + blocksDenied = 0; + + UndoState undoState = player.DrawBegin( null ); + Map map = player.WorldMap; + + for( int i = 0; i < changes.Length; i++ ) { + DrawOneBlock( player, map, changes[i].OldBlock, + changes[i].Coord, context, + ref blocks, ref blocksDenied, undoState ); + } + + Logger.Log( LogType.UserActivity, + "{0} undid {1} blocks changed by player {2} (in a selection on world {3})", + player.Name, + blocks, + args.Target.Name, + args.World.Name ); + + DrawingFinished( player, "UndoArea'd", blocks, blocksDenied ); + } + + + + static readonly CommandDescriptor CdUndoPlayer = new CommandDescriptor { + Name = "UndoPlayer", + Aliases = new[] { "up", "undox" }, + Category = CommandCategory.Moderation, + Permissions = new[] { Permission.UndoOthersActions }, + Usage = "/UndoPlayer PlayerName [TimeSpan|BlockCount]", + Help = "Reverses changes made by a given player in the current world.", + Handler = UndoPlayerHandler + }; + + static void UndoPlayerHandler( Player player, Command cmd ) { + World playerWorld = player.World; + if( playerWorld == null ) PlayerOpException.ThrowNoWorld( player ); + + if( !BlockDB.IsEnabledGlobally ) { + player.Message( "&WBlockDB is disabled on this server." ); + return; + } + + if( !playerWorld.BlockDB.IsEnabled ) { + player.Message( "&WBlockDB is disabled in this world." ); + return; + } + + string name = cmd.Next(); + string range = cmd.Next(); + if( name == null || range == null ) { + CdUndoPlayer.PrintUsage( player ); + return; + } + if( cmd.HasNext ) { + CdUndoPlayer.PrintUsage( player ); + return; + } + + PlayerInfo target = PlayerDB.FindPlayerInfoOrPrintMatches( player, name ); + if( target == null ) return; + + if( player.Info != target && !player.Can( Permission.UndoOthersActions, target.Rank ) ) { + player.Message( "You may only undo actions of players ranked {0}&S or lower.", + player.Info.Rank.GetLimit( Permission.UndoOthersActions ).ClassyName ); + player.Message( "Player {0}&S is ranked {1}", target.ClassyName, target.Rank.ClassyName ); + return; + } + + int count; + TimeSpan span; + BlockDBEntry[] changes; + if( Int32.TryParse( range, out count ) ) { + if( !cmd.IsConfirmed ) { + player.Message( "Searching for last {0} changes made by {1}&s...", + count, target.ClassyName ); + } + changes = playerWorld.BlockDB.Lookup( target, count ); + if( changes.Length > 0 && !cmd.IsConfirmed ) { + player.Confirm( cmd, "Undo last {0} changes made by player {1}&S?", + changes.Length, target.ClassyName ); + return; + } + + } else if( range.TryParseMiniTimespan( out span ) ) { + if( span > DateTimeUtil.MaxTimeSpan ) { + player.MessageMaxTimeSpan(); + return; + } + if( !cmd.IsConfirmed ) { + player.Message( "Searching for changes made by {0}&s in the last {1}...", + target.ClassyName, span.ToMiniString() ); + } + changes = playerWorld.BlockDB.Lookup( target, span ); + if( changes.Length > 0 && !cmd.IsConfirmed ) { + player.Confirm( cmd, "Undo changes ({0}) made by {1}&S in the last {2}?", + changes.Length, target.ClassyName, span.ToMiniString() ); + return; + } + + } else { + CdUndoPlayer.PrintUsage( player ); + return; + } + + if( changes.Length == 0 ) { + player.Message( "UndoPlayer: Found nothing to undo." ); + return; + } + + BlockChangeContext context = BlockChangeContext.Drawn; + if( player.Info == target ) { + context |= BlockChangeContext.UndoneSelf; + } else { + context |= BlockChangeContext.UndoneOther; + } + + int blocks = 0, + blocksDenied = 0; + + UndoState undoState = player.DrawBegin( null ); + Map map = player.WorldMap; + + for( int i = 0; i < changes.Length; i++ ) { + DrawOneBlock( player, map, changes[i].OldBlock, + changes[i].Coord, context, + ref blocks, ref blocksDenied, undoState ); + } + + Logger.Log( LogType.UserActivity, + "{0} undid {1} blocks changed by player {2} (on world {3})", + player.Name, + blocks, + target.Name, + playerWorld.Name ); + + DrawingFinished( player, "UndoPlayer'ed", blocks, blocksDenied ); + } + + #endregion + + + + static readonly CommandDescriptor CdStatic = new CommandDescriptor { + Name = "Static", + Category = CommandCategory.Building, + Help = "Toggles repetition of last selection on or off.", + Handler = StaticHandler + }; + + static void StaticHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdStatic.PrintUsage( player ); + return; + } + if( player.IsRepeatingSelection ) { + player.Message( "Static: Off" ); + player.IsRepeatingSelection = false; + player.SelectionCancel(); + } else { + player.Message( "Static: On" ); + player.IsRepeatingSelection = true; + } + } + } +} \ No newline at end of file diff --git a/fCraft/Commands/ChatCommands.cs b/fCraft/Commands/ChatCommands.cs new file mode 100644 index 0000000..0da846a --- /dev/null +++ b/fCraft/Commands/ChatCommands.cs @@ -0,0 +1,407 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Linq; + +namespace fCraft { + static class ChatCommands { + + public static void Init() { + CommandManager.RegisterCommand( CdSay ); + CommandManager.RegisterCommand( CdStaff ); + + CommandManager.RegisterCommand( CdIgnore ); + CommandManager.RegisterCommand( CdUnignore ); + + CommandManager.RegisterCommand( CdMe ); + + CommandManager.RegisterCommand( CdRoll ); + + CommandManager.RegisterCommand( CdDeafen ); + + CommandManager.RegisterCommand( CdClear ); + + CommandManager.RegisterCommand( CdTimer ); + } + + + #region Say + + static readonly CommandDescriptor CdSay = new CommandDescriptor { + Name = "Say", + Category = CommandCategory.Chat, + IsConsoleSafe = true, + NotRepeatable = true, + DisableLogging = true, + Permissions = new[] { Permission.Chat, Permission.Say }, + Usage = "/Say Message", + Help = "Shows a message in special color, without the player name prefix. " + + "Can be used for making announcements.", + Handler = SayHandler + }; + + static void SayHandler( Player player, Command cmd ) { + if( player.Info.IsMuted ) { + player.MessageMuted(); + return; + } + + if( player.DetectChatSpam() ) return; + + if( player.Can( Permission.Say ) ) { + string msg = cmd.NextAll().Trim(); + if( msg.Length > 0 ) { + Chat.SendSay( player, msg ); + } else { + CdSay.PrintUsage( player ); + } + } else { + player.MessageNoAccess( Permission.Say ); + } + } + + #endregion + + + #region Staff + + static readonly CommandDescriptor CdStaff = new CommandDescriptor { + Name = "Staff", + Aliases = new[] { "st" }, + Category = CommandCategory.Chat | CommandCategory.Moderation, + Permissions = new[] { Permission.Chat }, + NotRepeatable = true, + IsConsoleSafe = true, + DisableLogging = true, + Usage = "/Staff Message", + Help = "Broadcasts your message to all operators/moderators on the server at once.", + Handler = StaffHandler + }; + + static void StaffHandler( Player player, Command cmd ) { + if( player.Info.IsMuted ) { + player.MessageMuted(); + return; + } + + if( player.DetectChatSpam() ) return; + + string message = cmd.NextAll().Trim(); + if( message.Length > 0 ) { + Chat.SendStaff( player, message ); + } + } + + #endregion + + + #region Ignore / Unignore + + static readonly CommandDescriptor CdIgnore = new CommandDescriptor { + Name = "Ignore", + Category = CommandCategory.Chat, + IsConsoleSafe = true, + Usage = "/Ignore [PlayerName]", + Help = "Temporarily blocks the other player from messaging you. " + + "If no player name is given, lists all ignored players.", + Handler = IgnoreHandler + }; + + static void IgnoreHandler( Player player, Command cmd ) { + string name = cmd.Next(); + if( name != null ) { + if( cmd.HasNext ) { + CdIgnore.PrintUsage( player ); + return; + } + PlayerInfo targetInfo = PlayerDB.FindPlayerInfoOrPrintMatches( player, name ); + if( targetInfo == null ) return; + + if( player.Ignore( targetInfo ) ) { + player.MessageNow( "You are now ignoring {0}", targetInfo.ClassyName ); + } else { + player.MessageNow( "You are already ignoring {0}", targetInfo.ClassyName ); + } + + } else { + PlayerInfo[] ignoreList = player.IgnoreList; + if( ignoreList.Length > 0 ) { + player.MessageNow( "Ignored players: {0}", ignoreList.JoinToClassyString() ); + } else { + player.MessageNow( "You are not currently ignoring anyone." ); + } + return; + } + } + + + static readonly CommandDescriptor CdUnignore = new CommandDescriptor { + Name = "Unignore", + Category = CommandCategory.Chat, + IsConsoleSafe = true, + Usage = "/Unignore PlayerName", + Help = "Unblocks the other player from messaging you.", + Handler = UnignoreHandler + }; + + static void UnignoreHandler( Player player, Command cmd ) { + string name = cmd.Next(); + if( name != null ) { + if( cmd.HasNext ) { + CdUnignore.PrintUsage( player ); + return; + } + PlayerInfo targetInfo = PlayerDB.FindPlayerInfoOrPrintMatches( player, name ); + if( targetInfo == null ) return; + + if( player.Unignore( targetInfo ) ) { + player.MessageNow( "You are no longer ignoring {0}", targetInfo.ClassyName ); + } else { + player.MessageNow( "You are not currently ignoring {0}", targetInfo.ClassyName ); + } + } else { + PlayerInfo[] ignoreList = player.IgnoreList; + if( ignoreList.Length > 0 ) { + player.MessageNow( "Ignored players: {0}", ignoreList.JoinToClassyString() ); + } else { + player.MessageNow( "You are not currently ignoring anyone." ); + } + return; + } + } + + #endregion + + + #region Me + + static readonly CommandDescriptor CdMe = new CommandDescriptor { + Name = "Me", + Category = CommandCategory.Chat, + Permissions = new[] { Permission.Chat }, + IsConsoleSafe = true, + NotRepeatable = true, + DisableLogging = true, + Usage = "/Me Message", + Help = "Sends IRC-style action message prefixed with your name.", + Handler = MeHandler + }; + + static void MeHandler( Player player, Command cmd ) { + if( player.Info.IsMuted ) { + player.MessageMuted(); + return; + } + + if( player.DetectChatSpam() ) return; + + string msg = cmd.NextAll().Trim(); + if( msg.Length > 0 ) { + Chat.SendMe( player, msg ); + } else { + CdMe.PrintUsage( player ); + } + } + + #endregion + + + #region Roll + + static readonly CommandDescriptor CdRoll = new CommandDescriptor { + Name = "Roll", + Category = CommandCategory.Chat, + Permissions = new[] { Permission.Chat }, + IsConsoleSafe = true, + Help = "Gives random number between 1 and 100.\n" + + "&H/Roll MaxNumber\n" + + "&S Gives number between 1 and max.\n" + + "&H/Roll MinNumber MaxNumber\n" + + "&S Gives number between min and max.", + Handler = RollHandler + }; + + static void RollHandler( Player player, Command cmd ) { + if( player.Info.IsMuted ) { + player.MessageMuted(); + return; + } + + if( player.DetectChatSpam() ) return; + + Random rand = new Random(); + int n1; + int min, max; + if( cmd.NextInt( out n1 ) ) { + int n2; + if( !cmd.NextInt( out n2 ) ) { + n2 = 1; + } + min = Math.Min( n1, n2 ); + max = Math.Max( n1, n2 ); + } else { + min = 1; + max = 100; + } + + int num = rand.Next( min, max + 1 ); + Server.Message( player, + "{0}{1} rolled {2} ({3}...{4})", + player.ClassyName, Color.Silver, num, min, max ); + player.Message( "{0}You rolled {1} ({2}...{3})", + Color.Silver, num, min, max ); + } + + #endregion + + + #region Deafen + + static readonly CommandDescriptor CdDeafen = new CommandDescriptor { + Name = "Deafen", + Aliases = new[] { "deaf" }, + Category = CommandCategory.Chat, + IsConsoleSafe = true, + Help = "Blocks all chat messages from being sent to you.", + Handler = DeafenHandler + }; + + static void DeafenHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdDeafen.PrintUsage( player ); + return; + } + if( !player.IsDeaf ) { + for( int i = 0; i < LinesToClear; i++ ) { + player.MessageNow( "" ); + } + player.MessageNow( "Deafened mode: ON" ); + player.MessageNow( "You will not see ANY messages until you type &H/Deafen&S again." ); + player.IsDeaf = true; + } else { + player.IsDeaf = false; + player.MessageNow( "Deafened mode: OFF" ); + } + } + + #endregion + + + #region Clear + + const int LinesToClear = 30; + static readonly CommandDescriptor CdClear = new CommandDescriptor { + Name = "Clear", + UsableByFrozenPlayers = true, + Category = CommandCategory.Chat, + Help = "Clears the chat screen.", + Handler = ClearHandler + }; + + static void ClearHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdClear.PrintUsage( player ); + return; + } + for( int i = 0; i < LinesToClear; i++ ) { + player.Message( "" ); + } + } + + #endregion + + + #region Timer + + static readonly CommandDescriptor CdTimer = new CommandDescriptor { + Name = "Timer", + Permissions = new[] { Permission.Say }, + IsConsoleSafe = true, + Category = CommandCategory.Chat, + Usage = "/Timer ", + Help = "Starts a timer with a given duration and message. " + + "As the timer counts down, announcements are shown globally. See also: &H/Help Timer Abort", + HelpSections = new Dictionary { + { "abort", "&H/Timer Abort \n&S" + + "Aborts a timer with the given ID number. " + + "To see a list of timers and their IDs, type &H/Timer&S (without any parameters)." } + }, + Handler = TimerHandler + }; + + static void TimerHandler( Player player, Command cmd ) { + string param = cmd.Next(); + + // List timers + if( param == null ) { + ChatTimer[] list = ChatTimer.TimerList.OrderBy( timer => timer.TimeLeft ).ToArray(); + if( list.Length == 0 ) { + player.Message( "No timers running." ); + } else { + player.Message( "There are {0} timers running:", list.Length ); + foreach( ChatTimer timer in list ) { + player.Message( " #{0} \"{1}&S\" (started by {2}, {3} left)", + timer.Id, timer.Message, timer.StartedBy, timer.TimeLeft.ToMiniString() ); + } + } + return; + } + + // Abort a timer + if( param.Equals( "abort", StringComparison.OrdinalIgnoreCase ) ) { + int timerId; + if( cmd.NextInt( out timerId ) ) { + ChatTimer timer = ChatTimer.FindTimerById( timerId ); + if( timer == null || !timer.IsRunning ) { + player.Message( "Given timer (#{0}) does not exist.", timerId ); + } else { + timer.Stop(); + string abortMsg = String.Format( "&Y(Timer) {0}&Y aborted a timer with {1} left: {2}", + player.ClassyName, timer.TimeLeft.ToMiniString(), timer.Message ); + Chat.SendSay( player, abortMsg ); + } + } else { + CdTimer.PrintUsage( player ); + } + return; + } + + // Start a timer + if( player.Info.IsMuted ) { + player.MessageMuted(); + return; + } + if( player.DetectChatSpam() ) return; + TimeSpan duration; + if( !param.TryParseMiniTimespan( out duration ) ) { + CdTimer.PrintUsage( player ); + return; + } + if( duration > DateTimeUtil.MaxTimeSpan ) { + player.MessageMaxTimeSpan(); + return; + } + if( duration < ChatTimer.MinDuration ) { + player.Message( "Timer: Must be at least 1 second." ); + return; + } + + string sayMessage; + string message = cmd.NextAll(); + if( String.IsNullOrEmpty( message ) ) { + sayMessage = String.Format( "&Y(Timer) {0}&Y started a {1} timer", + player.ClassyName, + duration.ToMiniString() ); + } else { + sayMessage = String.Format( "&Y(Timer) {0}&Y started a {1} timer: {2}", + player.ClassyName, + duration.ToMiniString(), + message ); + } + Chat.SendSay( player, sayMessage ); + ChatTimer.Start( duration, message, player.Name ); + } + + #endregion + } +} \ No newline at end of file diff --git a/fCraft/Commands/Command.cs b/fCraft/Commands/Command.cs new file mode 100644 index 0000000..a783f5c --- /dev/null +++ b/fCraft/Commands/Command.cs @@ -0,0 +1,236 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Diagnostics; +using JetBrains.Annotations; + +namespace fCraft { + /// A text scanner that aids parsing chat commands and their arguments. + /// Breaks up a message into tokens at spaces. Treats quoted strings as whole tokens. + public sealed class Command : ICloneable { + public CommandDescriptor Descriptor { get; private set; } + public int Offset { get; set; } + public readonly string RawMessage; + public string Name { get; private set; } // lowercase name of the command + public bool IsConfirmed; // whether this command has been confirmed by the user (with /ok) + + /// Creates a copy of an existing command. + public Command( [NotNull] Command other ) { + if( other == null ) throw new ArgumentNullException( "other" ); + Offset = other.Offset; + Descriptor = other.Descriptor; + RawMessage = other.RawMessage; + Name = other.Name; + IsConfirmed = other.IsConfirmed; + } + + /// Creates a command from a raw message. + public Command( [NotNull] string rawMessage ) { + if( rawMessage == null ) throw new ArgumentNullException( "rawMessage" ); + Offset = 1; + RawMessage = rawMessage; + string name = Next(); + if( name == null ) { + throw new ArgumentException( "Raw message must contain the command name.", "rawMessage" ); + } + Descriptor = CommandManager.GetDescriptor( name, true ); + Name = name.ToLower(); + } + + + /// Creates a copy of this command. + /// Use the copy constructor instead of this, if possible. + public object Clone() { + return new Command( this ); + } + + + /// Returns the next command argument. + /// A single "argument" is either a word that ends with whitespace, + /// or several words in double quotes (""). + /// Next argument (string), or null if there are no more arguments. + [DebuggerStepThrough] + [CanBeNull] + public string Next() { + for( ; Offset < RawMessage.Length; Offset++ ) { + int t, j; + if( RawMessage[Offset] == '"' ) { + j = Offset + 1; + for( ; j < RawMessage.Length && RawMessage[j] != '"'; j++ ) {} + t = Offset; + Offset = j; + return RawMessage.Substring( t + 1, Offset - t - 1 ); + } else if( RawMessage[Offset] != ' ' ) { + j = Offset; + for( ; j < RawMessage.Length && RawMessage[j] != ' '; j++ ) {} + t = Offset; + Offset = j; + return RawMessage.Substring( t, Offset - t ); + } + } + return null; + } + + + /// Checks whether there is another argument available. + /// Does not modify the offset. + public bool HasNext { + [DebuggerStepThrough] + get { + return Offset < RawMessage.Length; + } + } + + + /// Returns the next command argument, parsed as an integer. + /// Set to the argument's value if parsing succeeded, + /// or zero if parsing failed or if there are no more arguments. + /// Returns true if parsing succeeded, + /// and false if parsing failed or if there are no more arguments. + [DebuggerStepThrough] + public bool NextInt( out int number ) { + string nextVal = Next(); + if( nextVal == null ) { + number = 0; + return false; + } else { + return Int32.TryParse( nextVal, out number ); + } + } + + + /// Checks whether there there is an int argument available. + /// Does not modify the offset. + public bool HasInt { + [DebuggerStepThrough] + get { + if( HasNext ) { + int startOffset = Offset; + string nextVal = Next(); + if( nextVal != null ) { + int number; + if( Int32.TryParse( nextVal, out number ) ) { + Offset = startOffset; + return true; + } + } + Offset = startOffset; + return false; + } else { + return false; + } + } + } + + + /// Returns the rest of command's text, from current offset to the end of string. + /// If there is nothing to return (i.e. if string ends at the current offset), + /// returns empty string. + /// The rest of the command, or an empty string. + [DebuggerStepThrough] + public string NextAll() { + for( ; Offset < RawMessage.Length; Offset++ ) { + if( RawMessage[Offset] != ' ' ) + return RawMessage.Substring( Offset ); + } + return ""; + } + + + /// Counts the number of arguments left in this command. + /// Does not modify the offset. + public int CountRemaining { + get { + if( HasNext ) { + int startOffset = Offset; + int i = 1; + while( Next() != null ) i++; + Offset = startOffset; + return i; + } else { + return 0; + } + } + } + + + /// Counts the total number of arguments. + /// Does not modify the offset. + public int Count { + get { + int startOffset = Offset; + Rewind(); + int i = 1; + while( Next() != null ) i++; + Offset = startOffset; + return i; + } + } + + + /// Resets the argument offset. + /// After calling Rewind, arguments can be read from the beginning again. + public void Rewind() { + Offset = 1; + Next(); + } + + + [DebuggerStepThrough] + public Block NextBlock( [NotNull] Player player ) { + if( player == null ) throw new ArgumentNullException( "player" ); + string blockName = Next(); + Block targetBlock = Block.Undefined; + if( blockName != null ) { + targetBlock = Map.GetBlockByName( blockName ); + if( targetBlock == Block.Undefined ) { + player.Message( "Unrecognized blocktype \"{0}\"", blockName ); + } + } + return targetBlock; + } + + + public Block NextBlockWithParam( [NotNull] Player player, ref int param ) { + if( player == null ) throw new ArgumentNullException( "player" ); + string jointString = Next(); + if( jointString == null ) { + return Block.Undefined; + } + + Block targetBlock; + int slashIndex = jointString.IndexOf( '/' ); + if( slashIndex != -1 ) { + string blockName = jointString.Substring( 0, slashIndex ); + string paramString = jointString.Substring( slashIndex + 1 ); + + targetBlock = Map.GetBlockByName( blockName ); + if( targetBlock == Block.Undefined ) { + player.Message( "Unrecognized blocktype \"{0}\"", blockName ); + } + + int tempParam; + if( Int32.TryParse( paramString, out tempParam ) ) { + param = tempParam; + } else { + player.Message( "Could not parse \"{0}\" as an integer.", paramString ); + } + + } else { + targetBlock = Map.GetBlockByName( jointString ); + if( targetBlock == Block.Undefined ) { + player.Message( "Unrecognized blocktype \"{0}\"", jointString ); + } + } + return targetBlock; + } + + + public override string ToString() { + if( IsConfirmed ) { + return String.Format( "Command(\"{0}\",{1},confirmed)", RawMessage, Offset ); + } else { + return String.Format( "Command(\"{0}\",{1})", RawMessage, Offset ); + } + } + } +} diff --git a/fCraft/Commands/CommandCategory.cs b/fCraft/Commands/CommandCategory.cs new file mode 100644 index 0000000..d9b1034 --- /dev/null +++ b/fCraft/Commands/CommandCategory.cs @@ -0,0 +1,36 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; + +namespace fCraft { + /// Command categories. A command may belong to more than one category. + /// Use binary flag logic (value & flag == flag) to test whether a command belongs to a particular category. + [Flags] + public enum CommandCategory { + /// Default command category. Do not use it. + None = 0, + + /// Building-related commands: drawing, binding, copy/paste. + Building = 1, + + /// Chat-related commands: messaging, ignoring, muting, etc. + Chat = 2, + + /// Information commands: server, world, zone, rank, and player infos. + Info = 4, + + /// Moderation commands: kick, ban, rank, tp/bring, etc. + Moderation = 8, + + /// Server maintenance commands: reloading configs, editing PlayerDB, importing data, etc. + Maintenance = 16, + + /// World-related commands: joining, loading, renaming, etc. + World = 32, + + /// Zone-related commands: creating, editing, testing, etc. + Zone = 64, + + /// Commands that are only used for diagnostics and debugging. + Debug = 128 + } +} diff --git a/fCraft/Commands/CommandDescriptor.cs b/fCraft/Commands/CommandDescriptor.cs new file mode 100644 index 0000000..793fe3e --- /dev/null +++ b/fCraft/Commands/CommandDescriptor.cs @@ -0,0 +1,151 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; + +namespace fCraft { + + /// Delegate for command handlers/callbacks. + /// Player who called the command. + /// Command arguments. + public delegate void CommandHandler( Player source, Command cmd ); + + + /// Describes a chat command. Defines properties, permission requirements, and usage information. + /// Specifies a handler method. + public sealed class CommandDescriptor : IClassy { + + /// List of aliases. May be null or empty. Default: null + [CanBeNull] + public string[] Aliases { get; set; } + + /// Command category. Must be set before registering. + public CommandCategory Category { get; set; } + + /// Whether the command may be used from console. Default: false + public bool IsConsoleSafe { get; set; } + + /// Callback function to execute when command is called. Must be set before registering. + public CommandHandler Handler { get; set; } + + /// Full text of the help message. Default: null + public string Help { get; set; } + + /// Whether the command is hidden from command list (/cmds). Default: false + public bool IsHidden { get; set; } + + /// Whether the command is not part of fCraft core (set automatically). + public bool IsCustom { get; internal set; } + + /// Whether the command should be repeated by the "/" shortcut. Default: false + public bool NotRepeatable { get; set; } + + /// Whether the command should be usable by frozen players. Default: false + public bool UsableByFrozenPlayers { get; set; } + + /// Whether calls to this command should not be logged. + public bool DisableLogging { get; set; } + + /// Primary command name. Must be set before registering. + public string Name { get; set; } + + /// List of permissions required to call the command. May be empty or null. Default: null + public Permission[] Permissions { get; set; } + + /// Whether any permission from the list is enough. + /// If this is false, ALL permissions are required. + public bool AnyPermission { get; set; } + + /// Brief demonstration of command's usage syntax. Defaults to "/Name". + public string Usage { get; set; } + + /// Help sub-sections. + public Dictionary HelpSections { get; set; } + + /// Whether this command involves a selection that can be repeated with /static. Default: false + public bool RepeatableSelection { get; set; } + + + /// Checks whether this command may be called by players of a given rank. + public bool CanBeCalledBy( [NotNull] Rank rank ) { + if( rank == null ) throw new ArgumentNullException( "rank" ); + return Permissions == null || + Permissions.All( rank.Can ) || + AnyPermission && Permissions.Any( rank.Can ); + } + + + public Rank MinRank { + get { + if( AnyPermission ) { + return RankManager.GetMinRankWithAnyPermission( Permissions ); + } else { + return RankManager.GetMinRankWithAllPermissions( Permissions ); + } + } + } + + + /// Checks whether players of the given rank should see this command in /cmds list. + /// Takes permissions and the hidden flag into account. + public bool IsVisibleTo( [NotNull] Rank rank ) { + if( rank == null ) throw new ArgumentNullException( "rank" ); + return !IsHidden && CanBeCalledBy( rank ); + } + + + /// Prints command usage syntax to the given player. + public void PrintUsage( [NotNull] Player player ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( Usage != null ) { + player.Message( "Usage: &H{0}", Usage ); + } else { + player.Message( "Usage: &H/{0}", Name ); + } + } + + + /// Calls this command. + /// Player who called the command. + /// Command arguments. + /// Whether CommandCalling and CommandCalled events should be raised. + /// True if the command was called succesfully. + /// False if the call was cancelled by the CommandCalling event. + public bool Call( [NotNull] Player player, [NotNull] Command cmd, bool raiseEvent ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( cmd == null ) throw new ArgumentNullException( "cmd" ); + if( raiseEvent && CommandManager.RaiseCommandCallingEvent( cmd, this, player ) ) return false; + Handler( player, cmd ); + if( raiseEvent ) CommandManager.RaiseCommandCalledEvent( cmd, this, player ); + return true; + } + + + public override string ToString() { + return String.Format( "CommandDescriptor({0})", Name ); + } + + public string ClassyName { + get { + if( ConfigKey.RankColorsInChat.Enabled() ) { + Rank minRank; + if( Permissions != null && Permissions.Length > 0 ) { + minRank = MinRank; + } else { + minRank = RankManager.LowestRank; + } + if( minRank == null ) { + return Name; + } else if( ConfigKey.RankPrefixesInChat.Enabled() ) { + return minRank.Color + minRank.Prefix + Name; + } else { + return minRank.Color + Name; + } + } else { + return Name; + } + } + } + } +} \ No newline at end of file diff --git a/fCraft/Commands/CommandManager.cs b/fCraft/Commands/CommandManager.cs new file mode 100644 index 0000000..eb8b075 --- /dev/null +++ b/fCraft/Commands/CommandManager.cs @@ -0,0 +1,323 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Linq; +using fCraft.Events; +using JetBrains.Annotations; + +namespace fCraft { + /// Static class that allows registration and parsing of all text commands. + public static class CommandManager { + static readonly SortedList Aliases = new SortedList(); + static readonly SortedList Commands = new SortedList(); + + public static readonly string[] ReservedCommandNames = new[] { "ok", "nvm" }; + + // Sets up all the command hooks + internal static void Init() { + ModerationCommands.Init(); + BuildingCommands.Init(); + InfoCommands.Init(); + WorldCommands.Init(); + ZoneCommands.Init(); + MaintenanceCommands.Init(); + ChatCommands.Init(); + Logger.Log( LogType.Debug, + "CommandManager: {0} commands registered ({1} hidden, {2} aliases)", + Commands.Count, + GetCommands( true ).Length, + Aliases.Count ); + } + + + /// Gets a list of all commands (includding hidden ones). + public static CommandDescriptor[] GetCommands() { + return Commands.Values.ToArray(); + } + + + /// Gets a list of ONLY hidden or non-hidden commands, not both. + public static CommandDescriptor[] GetCommands( bool hidden ) { + return Commands.Values + .Where( cmd => ( cmd.IsHidden == hidden ) ) + .ToArray(); + } + + + /// Gets a list of commands available to a specified rank. + public static CommandDescriptor[] GetCommands( [NotNull] Rank rank, bool includeHidden ) { + if( rank == null ) throw new ArgumentNullException( "rank" ); + return Commands.Values + .Where( cmd => ( !cmd.IsHidden || includeHidden ) && + cmd.CanBeCalledBy( rank ) ) + .ToArray(); + } + + + /// Gets a list of commands in a specified category. + /// Note that commands may belong to more than one category. + public static CommandDescriptor[] GetCommands( CommandCategory category, bool includeHidden ) { + return Commands.Values + .Where( cmd => ( includeHidden || !cmd.IsHidden ) && + ( cmd.Category & category ) == category ) + .ToArray(); + } + + + /// Registers a custom command with fCraft. + /// CommandRegistrationException may be thrown if the given descriptor does not meet all the requirements. + public static void RegisterCustomCommand( [NotNull] CommandDescriptor descriptor ) { + if( descriptor == null ) throw new ArgumentNullException( "descriptor" ); + descriptor.IsCustom = true; + RegisterCommand( descriptor ); + } + + + internal static void RegisterCommand( [NotNull] CommandDescriptor descriptor ) { + if( descriptor == null ) throw new ArgumentNullException( "descriptor" ); + +#if DEBUG + if( descriptor.Category == CommandCategory.None && !descriptor.IsCustom ) { + throw new CommandRegistrationException( "Standard commands must have a category set." ); + } +#endif + + if( !IsValidCommandName( descriptor.Name ) ) { + throw new CommandRegistrationException( "All commands need a name, between 1 and 16 alphanumeric characters long." ); + } + + string normalizedName = descriptor.Name.ToLower(); + + if( Commands.ContainsKey( normalizedName ) ) { + throw new CommandRegistrationException( "A command with the name \"{0}\" is already registered.", descriptor.Name ); + } + + if( ReservedCommandNames.Contains( normalizedName ) ) { + throw new CommandRegistrationException( "The command name is reserved." ); + } + + if( descriptor.Handler == null ) { + throw new CommandRegistrationException( "All command descriptors are required to provide a handler callback." ); + } + + if( descriptor.Aliases != null ) { + if( descriptor.Aliases.Any( alias => Commands.ContainsKey( alias ) ) ) { + throw new CommandRegistrationException( "One of the aliases for \"{0}\" is using the name of an already-defined command." ); + } + } + + if( !Char.IsUpper( descriptor.Name[0] ) ) { + descriptor.Name = descriptor.Name.UppercaseFirst(); + } + + if( descriptor.Usage == null ) { + descriptor.Usage = "/" + descriptor.Name; + } + + if( RaiseCommandRegisteringEvent( descriptor ) ) return; + + if( Aliases.ContainsKey( normalizedName ) ) { + Logger.Log( LogType.Warning, + "CommandManager.RegisterCommand: \"{0}\" was defined as an alias for \"{1}\", " + + "but has now been replaced by a different command of the same name.", + descriptor.Name, Aliases[descriptor.Name] ); + Aliases.Remove( normalizedName ); + } + + if( descriptor.Aliases != null ) { + foreach( string alias in descriptor.Aliases ) { + string normalizedAlias = alias.ToLower(); + if( ReservedCommandNames.Contains( normalizedAlias ) ) { + Logger.Log( LogType.Warning, + "CommandManager.RegisterCommand: Alias \"{0}\" for \"{1}\" ignored (reserved name).", + alias, descriptor.Name ); + } else if( Aliases.ContainsKey( normalizedAlias ) ) { + Logger.Log( LogType.Warning, + "CommandManager.RegisterCommand: \"{0}\" was defined as an alias for \"{1}\", " + + "but has been overridden to resolve to \"{2}\" instead.", + alias, Aliases[normalizedAlias], descriptor.Name ); + } else { + Aliases.Add( normalizedAlias, normalizedName ); + } + } + } + + Commands.Add( normalizedName, descriptor ); + + RaiseCommandRegisteredEvent( descriptor ); + } + + + /// Finds an instance of CommandDescriptor for a given command. + /// Case-insensitive, but no autocompletion. + /// Command to find. + /// Whether to check command aliases. + /// CommandDesriptor object if found, null if not found. + [CanBeNull] + public static CommandDescriptor GetDescriptor( [NotNull] string commandName, bool alsoCheckAliases ) { + if( commandName == null ) throw new ArgumentNullException( "commandName" ); + commandName = commandName.ToLower(); + if( Commands.ContainsKey( commandName ) ) { + return Commands[commandName]; + } else if( alsoCheckAliases && Aliases.ContainsKey( commandName ) ) { + return Commands[Aliases[commandName]]; + } else { + return null; + } + } + + + /// Parses and calls a specified command. + /// Player who issued the command. + /// Command to be parsed and executed. + /// Whether this command is being called from a non-player (e.g. Console). + /// True if the command was called, false if something prevented it from being called. + public static bool ParseCommand( [NotNull] Player player, [NotNull] Command cmd, bool fromConsole ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( cmd == null ) throw new ArgumentNullException( "cmd" ); + CommandDescriptor descriptor = GetDescriptor( cmd.Name, true ); + + if( descriptor == null ) { + player.Message( "Unknown command \"{0}\". See &H/Commands", cmd.Name ); + return false; + } + + if( !descriptor.IsConsoleSafe && fromConsole ) { + player.Message( "You cannot use this command from console." ); + } else { + if( descriptor.Permissions != null ) { + if( !descriptor.CanBeCalledBy( player.Info.Rank ) ) { + player.MessageNoAccess( descriptor ); + } else if( !descriptor.Call( player, cmd, true ) ) { + player.Message( "Command was cancelled." ); + } else { + return true; + } + } else { + if( descriptor.Call( player, cmd, true ) ) { + return true; + } else { + player.Message( "Command was cancelled." ); + } + } + } + return false; + } + + + /// Checks whether a command name is acceptible. + /// Constraints are similar to Player.IsValidName, except for minimum length. + /// Command name to check. + /// True if the name is valid. + public static bool IsValidCommandName( [NotNull] string name ) { + if( name == null ) throw new ArgumentNullException( "name" ); + if( name.Length == 0 || name.Length > 16 ) return false; + // ReSharper disable LoopCanBeConvertedToQuery + for( int i = 0; i < name.Length; i++ ) { + char ch = name[i]; + if( ( ch < '0' && ch != '.' ) || ( ch > '9' && ch < 'A' ) || ( ch > 'Z' && ch < '_' ) || + ( ch > '_' && ch < 'a' ) || ch > 'z' ) { + return false; + } + } + // ReSharper restore LoopCanBeConvertedToQuery + return true; + } + + + #region Events + + /// Occurs when a command is being registered (cancellable). + public static event EventHandler CommandRegistering; + + /// Occurs when a command has been registered. + public static event EventHandler CommandRegistered; + + /// Occurs when a command is being called by a player or the console (cancellable). + public static event EventHandler CommandCalling; + + /// Occurs when the command has been called by a player or the console. + public static event EventHandler CommandCalled; + + + static bool RaiseCommandRegisteringEvent( CommandDescriptor descriptor ) { + var h = CommandRegistering; + if( h == null ) return false; + var e = new CommandRegistringEventArgs( descriptor ); + h( null, e ); + return e.Cancel; + } + + + static void RaiseCommandRegisteredEvent( CommandDescriptor descriptor ) { + var h = CommandRegistered; + if( h != null ) h( null, new CommandRegisteredEventArgs( descriptor ) ); + } + + + internal static bool RaiseCommandCallingEvent( Command cmd, CommandDescriptor descriptor, Player player ) { + var h = CommandCalling; + if( h == null ) return false; + var e = new CommandCallingEventArgs( cmd, descriptor, player ); + h( null, e ); + return e.Cancel; + } + + + internal static void RaiseCommandCalledEvent( Command cmd, CommandDescriptor descriptor, Player player ) { + var h = CommandCalled; + if( h != null ) CommandCalled( null, new CommandCalledEventArgs( cmd, descriptor, player ) ); + } + + #endregion + } + + public sealed class CommandRegistrationException : Exception { + public CommandRegistrationException( string message ) : base( message ) { } + [StringFormatMethod( "message" )] + public CommandRegistrationException( string message, params object[] args ) : + base( String.Format( message, args ) ) { } + } +} + + +namespace fCraft.Events { + public class CommandRegisteredEventArgs : EventArgs { + internal CommandRegisteredEventArgs( CommandDescriptor commandDescriptor ) { + CommandDescriptor = commandDescriptor; + } + + public CommandDescriptor CommandDescriptor { get; private set; } + } + + + public sealed class CommandRegistringEventArgs : CommandRegisteredEventArgs, ICancellableEvent { + internal CommandRegistringEventArgs( CommandDescriptor commandDescriptor ) + : base( commandDescriptor ) { + } + + public bool Cancel { get; set; } + } + + + public class CommandCalledEventArgs : EventArgs { + internal CommandCalledEventArgs( Command command, CommandDescriptor commandDescriptor, Player player ) { + Command = command; + CommandDescriptor = commandDescriptor; + Player = player; + } + + public Command Command { get; private set; } + public CommandDescriptor CommandDescriptor { get; private set; } + public Player Player { get; private set; } + } + + + public sealed class CommandCallingEventArgs : CommandCalledEventArgs, ICancellableEvent { + internal CommandCallingEventArgs( Command command, CommandDescriptor commandDescriptor, Player player ) : + base( command, commandDescriptor, player ) { + } + + public bool Cancel { get; set; } + } +} \ No newline at end of file diff --git a/fCraft/Commands/InfoCommands.cs b/fCraft/Commands/InfoCommands.cs new file mode 100644 index 0000000..0875a1c --- /dev/null +++ b/fCraft/Commands/InfoCommands.cs @@ -0,0 +1,1294 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using JetBrains.Annotations; + +namespace fCraft { + /// Contains commands that don't do anything besides displaying some information or text. + /// Includes several chat commands. + static class InfoCommands { + const int PlayersPerPage = 30; + + internal static void Init() { + CommandManager.RegisterCommand( CdInfo ); + CommandManager.RegisterCommand( CdBanInfo ); + CommandManager.RegisterCommand( CdRankInfo ); + CommandManager.RegisterCommand( CdServerInfo ); + + CommandManager.RegisterCommand( CdRanks ); + + CommandManager.RegisterCommand( CdRules ); + + CommandManager.RegisterCommand( CdMeasure ); + + CommandManager.RegisterCommand( CdPlayers ); + + CommandManager.RegisterCommand( CdWhere ); + + CommandManager.RegisterCommand( CdHelp ); + CommandManager.RegisterCommand( CdCommands ); + + CommandManager.RegisterCommand( CdColors ); + +#if DEBUG_SCHEDULER + CommandManager.RegisterCommand( cdTaskDebug ); +#endif + } + + + #region Info + + const int MaxAltsToPrint = 15; + static readonly Regex RegexNonNameChars = new Regex( @"[^a-zA-Z0-9_\*\?]", RegexOptions.Compiled ); + + static readonly CommandDescriptor CdInfo = new CommandDescriptor { + Name = "Info", + Aliases = new[] { "whois", "whowas" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/Info [PlayerName or IP [Offset]]", + Help = "Prints information and stats for a given player. " + + "Prints your own stats if no name is given. " + + "Prints a list of names if a partial name or an IP is given. ", + Handler = InfoHandler + }; + + internal static void InfoHandler( Player player, Command cmd ) { + string name = cmd.Next(); + if( name == null ) { + // no name given, print own info + PrintPlayerInfo( player, player.Info ); + return; + + } else if( name.Equals( player.Name, StringComparison.OrdinalIgnoreCase ) ) { + // own name given + player.LastUsedPlayerName = player.Name; + PrintPlayerInfo( player, player.Info ); + return; + + } else if( !player.Can( Permission.ViewOthersInfo ) ) { + // someone else's name or IP given, permission required. + player.MessageNoAccess( Permission.ViewOthersInfo ); + return; + } + + // repeat last-typed name + if( name == "-" ) { + if( player.LastUsedPlayerName != null ) { + name = player.LastUsedPlayerName; + } else { + player.Message( "Cannot repeat player name: you haven't used any names yet." ); + return; + } + } + + PlayerInfo[] infos; + IPAddress ip; + + if( name.Contains( "/" ) ) { + // IP range matching (CIDR notation) + string ipString = name.Substring( 0, name.IndexOf( '/' ) ); + string rangeString = name.Substring( name.IndexOf( '/' ) + 1 ); + byte range; + if( Server.IsIP( ipString ) && IPAddress.TryParse( ipString, out ip ) && + Byte.TryParse( rangeString, out range ) && range <= 32 ) { + player.Message( "Searching {0}-{1}", ip.RangeMin( range ), ip.RangeMax( range ) ); + infos = PlayerDB.FindPlayersCidr( ip, range ); + } else { + player.Message( "Info: Invalid IP range format. Use CIDR notation." ); + return; + } + + } else if( Server.IsIP( name ) && IPAddress.TryParse( name, out ip ) ) { + // find players by IP + infos = PlayerDB.FindPlayers( ip ); + + } else if( name.Contains( "*" ) || name.Contains( "?" ) ) { + // find players by regex/wildcard + string regexString = "^" + RegexNonNameChars.Replace( name, "" ).Replace( "*", ".*" ).Replace( "?", "." ) + "$"; + Regex regex = new Regex( regexString, RegexOptions.IgnoreCase | RegexOptions.Compiled ); + infos = PlayerDB.FindPlayers( regex ); + + } else { + // find players by partial matching + PlayerInfo tempInfo; + if( !PlayerDB.FindPlayerInfo( name, out tempInfo ) ) { + infos = PlayerDB.FindPlayers( name ); + } else if( tempInfo == null ) { + player.MessageNoPlayer( name ); + return; + } else { + infos = new[] { tempInfo }; + } + } + + Array.Sort( infos, new PlayerInfoComparer( player ) ); + + if( infos.Length == 1 ) { + // only one match found; print it right away + player.LastUsedPlayerName = infos[0].Name; + PrintPlayerInfo( player, infos[0] ); + + } else if( infos.Length > 1 ) { + // multiple matches found + if( infos.Length <= PlayersPerPage ) { + // all fit to one page + player.MessageManyMatches( "player", infos ); + + } else { + // pagination + int offset; + if( !cmd.NextInt( out offset ) ) offset = 0; + if( offset >= infos.Length ) { + offset = Math.Max( 0, infos.Length - PlayersPerPage ); + } + PlayerInfo[] infosPart = infos.Skip( offset ).Take( PlayersPerPage ).ToArray(); + player.MessageManyMatches( "player", infosPart ); + if( offset + infosPart.Length < infos.Length ) { + // normal page + player.Message( "Showing {0}-{1} (out of {2}). Next: &H/Info {3} {4}", + offset + 1, offset + infosPart.Length, infos.Length, + name, offset + infosPart.Length ); + } else { + // last page + player.Message( "Showing matches {0}-{1} (out of {2}).", + offset + 1, offset + infosPart.Length, infos.Length ); + } + } + + } else { + // no matches found + player.MessageNoPlayer( name ); + } + } + + public static void PrintPlayerInfo( [NotNull] Player player, [NotNull] PlayerInfo info ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( info == null ) throw new ArgumentNullException( "info" ); + Player target = info.PlayerObject; + + // hide online status when hidden + if( target != null && !player.CanSee( target ) ) { + target = null; + } + + if( info.LastIP.Equals( IPAddress.None ) ) { + player.Message( "About {0}&S: Never seen before.", info.ClassyName ); + + } else { + if( target != null ) { + TimeSpan idle = target.IdleTime; + if( info.IsHidden ) { + if( idle.TotalMinutes > 2 ) { + if( player.Can( Permission.ViewPlayerIPs ) ) { + player.Message( "About {0}&S: HIDDEN from {1} (idle {2})", + info.ClassyName, + info.LastIP, + idle.ToMiniString() ); + } else { + player.Message( "About {0}&S: HIDDEN (idle {1})", + info.ClassyName, + idle.ToMiniString() ); + } + } else { + if( player.Can( Permission.ViewPlayerIPs ) ) { + player.Message( "About {0}&S: HIDDEN. Online from {1}", + info.ClassyName, + info.LastIP ); + } else { + player.Message( "About {0}&S: HIDDEN.", + info.ClassyName ); + } + } + } else { + if( idle.TotalMinutes > 1 ) { + if( player.Can( Permission.ViewPlayerIPs ) ) { + player.Message( "About {0}&S: Online now from {1} (idle {2})", + info.ClassyName, + info.LastIP, + idle.ToMiniString() ); + } else { + player.Message( "About {0}&S: Online now (idle {1})", + info.ClassyName, + idle.ToMiniString() ); + } + } else { + if( player.Can( Permission.ViewPlayerIPs ) ) { + player.Message( "About {0}&S: Online now from {1}", + info.ClassyName, + info.LastIP ); + } else { + player.Message( "About {0}&S: Online now.", + info.ClassyName ); + } + } + } + } else { + if( player.Can( Permission.ViewPlayerIPs ) ) { + if( info.LeaveReason != LeaveReason.Unknown ) { + player.Message( "About {0}&S: Last seen {1} ago from {2} ({3}).", + info.ClassyName, + info.TimeSinceLastSeen.ToMiniString(), + info.LastIP, + info.LeaveReason ); + } else { + player.Message( "About {0}&S: Last seen {1} ago from {2}.", + info.ClassyName, + info.TimeSinceLastSeen.ToMiniString(), + info.LastIP ); + } + } else { + if( info.LeaveReason != LeaveReason.Unknown ) { + player.Message( "About {0}&S: Last seen {1} ago ({2}).", + info.ClassyName, + info.TimeSinceLastSeen.ToMiniString(), + info.LeaveReason ); + } else { + player.Message( "About {0}&S: Last seen {1} ago.", + info.ClassyName, + info.TimeSinceLastSeen.ToMiniString() ); + } + } + } + // Show login information + player.Message( " Logged in {0} time(s) since {1:d MMM yyyy}.", + info.TimesVisited, + info.FirstLoginDate ); + } + + if( info.IsFrozen ) { + player.Message( " Frozen {0} ago by {1}", + info.TimeSinceFrozen.ToMiniString(), + info.FrozenByClassy ); + } + + if( info.IsMuted ) { + player.Message( " Muted for {0} by {1}", + info.TimeMutedLeft.ToMiniString(), + info.MutedByClassy ); + } + + // Show ban information + IPBanInfo ipBan = IPBanList.Get( info.LastIP ); + switch( info.BanStatus ) { + case BanStatus.Banned: + if( ipBan != null ) { + player.Message( " Account and IP are &CBANNED&S. See &H/BanInfo" ); + } else { + player.Message( " Account is &CBANNED&S. See &H/BanInfo" ); + } + break; + case BanStatus.IPBanExempt: + if( ipBan != null ) { + player.Message( " IP is &CBANNED&S, but account is exempt. See &H/BanInfo" ); + } else { + player.Message( " IP is not banned, and account is exempt. See &H/BanInfo" ); + } + break; + case BanStatus.NotBanned: + if( ipBan != null ) { + player.Message( " IP is &CBANNED&S. See &H/BanInfo" ); + } + break; + } + + + if( !info.LastIP.Equals( IPAddress.None ) ) { + // Show alts + List altNames = new List(); + int bannedAltCount = 0; + foreach( PlayerInfo playerFromSameIP in PlayerDB.FindPlayers( info.LastIP ) ) { + if( playerFromSameIP == info ) continue; + altNames.Add( playerFromSameIP ); + if( playerFromSameIP.IsBanned ) { + bannedAltCount++; + } + } + + if( altNames.Count > 0 ) { + altNames.Sort( new PlayerInfoComparer( player ) ); + if( altNames.Count > MaxAltsToPrint ) { + if( bannedAltCount > 0 ) { + player.MessagePrefixed( "&S ", + "&S Over {0} accounts ({1} banned) on IP: {2} &Setc", + MaxAltsToPrint, + bannedAltCount, + altNames.Take( 15 ).ToArray().JoinToClassyString() ); + } else { + player.MessagePrefixed( "&S ", + "&S Over {0} accounts on IP: {1} &Setc", + MaxAltsToPrint, + altNames.Take( 15 ).ToArray().JoinToClassyString() ); + } + } else { + if( bannedAltCount > 0 ) { + player.MessagePrefixed( "&S ", + "&S {0} accounts ({1} banned) on IP: {2}", + altNames.Count, + bannedAltCount, + altNames.ToArray().JoinToClassyString() ); + } else { + player.MessagePrefixed( "&S ", + "&S {0} accounts on IP: {1}", + altNames.Count, + altNames.ToArray().JoinToClassyString() ); + } + } + } + } + + + // Stats + if( info.BlocksDrawn > 500000000 ) { + player.Message( " Built {0} and deleted {1} blocks, drew {2}M blocks, wrote {3} messages.", + info.BlocksBuilt, + info.BlocksDeleted, + info.BlocksDrawn / 1000000, + info.MessagesWritten ); + } else if( info.BlocksDrawn > 500000 ) { + player.Message( " Built {0} and deleted {1} blocks, drew {2}K blocks, wrote {3} messages.", + info.BlocksBuilt, + info.BlocksDeleted, + info.BlocksDrawn / 1000, + info.MessagesWritten ); + } else if( info.BlocksDrawn > 0 ) { + player.Message( " Built {0} and deleted {1} blocks, drew {2} blocks, wrote {3} messages.", + info.BlocksBuilt, + info.BlocksDeleted, + info.BlocksDrawn, + info.MessagesWritten ); + } else { + player.Message( " Built {0} and deleted {1} blocks, wrote {2} messages.", + info.BlocksBuilt, + info.BlocksDeleted, + info.MessagesWritten ); + } + + + // More stats + if( info.TimesBannedOthers > 0 || info.TimesKickedOthers > 0 ) { + player.Message( " Kicked {0} and banned {1} players.", info.TimesKickedOthers, info.TimesBannedOthers ); + } + + if( info.TimesKicked > 0 ) { + if( info.LastKickDate != DateTime.MinValue ) { + player.Message( " Got kicked {0} times. Last kick {1} ago by {2}", + info.TimesKicked, + info.TimeSinceLastKick.ToMiniString(), + info.LastKickByClassy ); + } else { + player.Message( " Got kicked {0} times.", info.TimesKicked ); + } + if( info.LastKickReason != null ) { + player.Message( " Kick reason: {0}", info.LastKickReason ); + } + } + + + // Promotion/demotion + if( info.PreviousRank == null ) { + if( info.RankChangedBy == null ) { + player.Message( " Rank is {0}&S (default).", + info.Rank.ClassyName ); + } else { + player.Message( " Promoted to {0}&S by {1}&S {2} ago.", + info.Rank.ClassyName, + info.RankChangedByClassy, + info.TimeSinceRankChange.ToMiniString() ); + if( info.RankChangeReason != null ) { + player.Message( " Promotion reason: {0}", info.RankChangeReason ); + } + } + } else if( info.PreviousRank <= info.Rank ) { + player.Message( " Promoted from {0}&S to {1}&S by {2}&S {3} ago.", + info.PreviousRank.ClassyName, + info.Rank.ClassyName, + info.RankChangedByClassy, + info.TimeSinceRankChange.ToMiniString() ); + if( info.RankChangeReason != null ) { + player.Message( " Promotion reason: {0}", info.RankChangeReason ); + } + } else { + player.Message( " Demoted from {0}&S to {1}&S by {2}&S {3} ago.", + info.PreviousRank.ClassyName, + info.Rank.ClassyName, + info.RankChangedByClassy, + info.TimeSinceRankChange.ToMiniString() ); + if( info.RankChangeReason != null ) { + player.Message( " Demotion reason: {0}", info.RankChangeReason ); + } + } + + if( !info.LastIP.Equals( IPAddress.None ) ) { + // Time on the server + TimeSpan totalTime = info.TotalTime; + if( target != null ) { + totalTime = totalTime.Add( info.TimeSinceLastLogin ); + } + player.Message( " Spent a total of {0:F1} hours ({1:F1} minutes) here.", + totalTime.TotalHours, + totalTime.TotalMinutes ); + } + } + + #endregion + + + #region BanInfo + + static readonly CommandDescriptor CdBanInfo = new CommandDescriptor { + Name = "BanInfo", + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/BanInfo [PlayerName|IPAddress]", + Help = "Prints information about past and present bans/unbans associated with the PlayerName or IP. " + + "If no name is given, this prints your own ban info.", + Handler = BanInfoHandler + }; + + internal static void BanInfoHandler( Player player, Command cmd ) { + string name = cmd.Next(); + if( cmd.HasNext ) { + CdBanInfo.PrintUsage( player ); + return; + } + + IPAddress address; + PlayerInfo info = null; + + if( name == null ) { + name = player.Name; + } else if( !player.Can( Permission.ViewOthersInfo ) ) { + player.MessageNoAccess( Permission.ViewOthersInfo ); + return; + } + + if( Server.IsIP( name ) && IPAddress.TryParse( name, out address ) ) { + IPBanInfo banInfo = IPBanList.Get( address ); + if( banInfo != null ) { + player.Message( "{0} was banned by {1}&S on {2:dd MMM yyyy} ({3} ago)", + banInfo.Address, + banInfo.BannedByClassy, + banInfo.BanDate, + banInfo.TimeSinceLastAttempt ); + if( !String.IsNullOrEmpty( banInfo.PlayerName ) ) { + player.Message( " Banned by association with {0}", + banInfo.PlayerNameClassy ); + } + if( banInfo.Attempts > 0 ) { + player.Message( " There have been {0} attempts to log in, most recently {1} ago by {2}", + banInfo.Attempts, + banInfo.TimeSinceLastAttempt.ToMiniString(), + banInfo.LastAttemptNameClassy ); + } + if( banInfo.BanReason != null ) { + player.Message( " Ban reason: {0}", banInfo.BanReason ); + } + } else { + player.Message( "{0} is currently NOT banned.", address ); + } + + } else { + info = PlayerDB.FindPlayerInfoOrPrintMatches( player, name ); + if( info == null ) return; + + address = info.LastIP; + + IPBanInfo ipBan = IPBanList.Get( info.LastIP ); + switch( info.BanStatus ) { + case BanStatus.Banned: + if( ipBan != null ) { + player.Message( "Player {0}&S and their IP are &CBANNED", info.ClassyName ); + } else { + player.Message( "Player {0}&S is &CBANNED&S (but their IP is not).", info.ClassyName ); + } + break; + case BanStatus.IPBanExempt: + if( ipBan != null ) { + player.Message( "Player {0}&S is exempt from an existing IP ban.", info.ClassyName ); + } else { + player.Message( "Player {0}&S is exempt from IP bans.", info.ClassyName ); + } + break; + case BanStatus.NotBanned: + if( ipBan != null ) { + player.Message( "Player {0}&s is not banned, but their IP is.", info.ClassyName ); + } else { + player.Message( "Player {0}&s is not banned.", info.ClassyName ); + } + break; + } + + if( info.BanDate != DateTime.MinValue ) { + player.Message( " Last ban by {0}&S on {1:dd MMM yyyy} ({2} ago).", + info.BannedByClassy, + info.BanDate, + info.TimeSinceBan.ToMiniString() ); + if( info.BanReason != null ) { + player.Message( " Last ban reason: {0}", info.BanReason ); + } + } else { + player.Message( "No past bans on record." ); + } + + if( info.UnbanDate != DateTime.MinValue && !info.IsBanned ) { + player.Message( " Unbanned by {0}&S on {1:dd MMM yyyy} ({2} ago).", + info.UnbannedByClassy, + info.UnbanDate, + info.TimeSinceUnban.ToMiniString() ); + if( info.UnbanReason != null ) { + player.Message( " Last unban reason: {0}", info.UnbanReason ); + } + } + + if( info.BanDate != DateTime.MinValue ) { + TimeSpan banDuration; + if( info.IsBanned ) { + banDuration = info.TimeSinceBan; + player.Message( " Ban duration: {0} so far", + banDuration.ToMiniString() ); + } else { + banDuration = info.UnbanDate.Subtract( info.BanDate ); + player.Message( " Previous ban's duration: {0}", + banDuration.ToMiniString() ); + } + } + } + + // Show alts + List altNames = new List(); + int bannedAltCount = 0; + foreach( PlayerInfo playerFromSameIP in PlayerDB.FindPlayers( address ) ) { + if( playerFromSameIP == info ) continue; + altNames.Add( playerFromSameIP ); + if( playerFromSameIP.IsBanned ) { + bannedAltCount++; + } + } + + if( altNames.Count > 0 ) { + altNames.Sort( new PlayerInfoComparer( player ) ); + if( altNames.Count > MaxAltsToPrint ) { + if( bannedAltCount > 0 ) { + player.MessagePrefixed( "&S ", + "&S Over {0} accounts ({1} banned) on IP: {2} &Setc", + MaxAltsToPrint, + bannedAltCount, + altNames.Take( 15 ).ToArray().JoinToClassyString() ); + } else { + player.MessagePrefixed( "&S ", + "&S Over {0} accounts on IP: {1} &Setc", + MaxAltsToPrint, + altNames.Take( 15 ).ToArray().JoinToClassyString() ); + } + } else { + if( bannedAltCount > 0 ) { + player.MessagePrefixed( "&S ", + "&S {0} accounts ({1} banned) on IP: {2}", + altNames.Count, + bannedAltCount, + altNames.ToArray().JoinToClassyString() ); + } else { + player.MessagePrefixed( "&S ", + "&S {0} accounts on IP: {1}", + altNames.Count, + altNames.ToArray().JoinToClassyString() ); + } + } + } + } + + #endregion + + + #region RankInfo + + static readonly CommandDescriptor CdRankInfo = new CommandDescriptor { + Name = "RankInfo", + Aliases = new[] { "rinfo" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/RankInfo RankName", + Help = "Shows a list of permissions granted to a rank. To see a list of all ranks, use &H/Ranks", + Handler = RankInfoHandler + }; + + // Shows general information about a particular rank. + static void RankInfoHandler( Player player, Command cmd ) { + Rank rank; + + string rankName = cmd.Next(); + if( cmd.HasNext ) { + CdRankInfo.PrintUsage( player ); + return; + } + + if( rankName == null ) { + rank = player.Info.Rank; + } else { + rank = RankManager.FindRank( rankName ); + if( rank == null ) { + player.Message( "No such rank: \"{0}\". See &H/Ranks", rankName ); + return; + } + } + + List permissions = new List(); + for( int i = 0; i < rank.Permissions.Length; i++ ) { + if( rank.Permissions[i] ) { + permissions.Add( (Permission)i ); + } + } + + Permission[] sortedPermissionNames = + permissions.OrderBy( s => s.ToString(), StringComparer.OrdinalIgnoreCase ).ToArray(); + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat( "Players of rank {0}&S can: ", rank.ClassyName ); + bool first = true; + for( int i = 0; i < sortedPermissionNames.Length; i++ ) { + Permission p = sortedPermissionNames[i]; + if( !first ) sb.Append( ',' ).Append( ' ' ); + Rank permissionLimit = rank.PermissionLimits[(int)p]; + sb.Append( p ); + if( permissionLimit != null ) { + sb.AppendFormat( "({0}&S)", permissionLimit.ClassyName ); + } + first = false; + } + player.Message( sb.ToString() ); + } + + if( rank.Can( Permission.Draw ) ) { + StringBuilder sb = new StringBuilder(); + if( rank.DrawLimit > 0 ) { + sb.AppendFormat( "Draw limit: {0} blocks.", rank.DrawLimit ); + } else { + sb.AppendFormat( "Draw limit: None (unlimited)." ); + } + if( rank.Can( Permission.CopyAndPaste ) ) { + sb.AppendFormat( " Copy/paste slots: {0}", rank.CopySlots ); + } + player.Message( sb.ToString() ); + } + + if( rank.IdleKickTimer > 0 ) { + player.Message( "Idle kick after {0}", TimeSpan.FromMinutes( rank.IdleKickTimer ).ToMiniString() ); + } + } + + #endregion + + + #region ServerInfo + + static readonly CommandDescriptor CdServerInfo = new CommandDescriptor { + Name = "ServerInfo", + Aliases = new[] { "ServerReport", "Version", "SInfo" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Help = "Shows server stats", + Handler = ServerInfoHandler + }; + + internal static void ServerInfoHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdServerInfo.PrintUsage( player ); + return; + } + Process.GetCurrentProcess().Refresh(); + + player.Message( "Servers status: Up for {0:0.0} hours, using {1:0} MB", + DateTime.UtcNow.Subtract( Server.StartTime ).TotalHours, + (Process.GetCurrentProcess().PrivateMemorySize64 / (1024 * 1024)) ); + + if( Server.IsMonitoringCPUUsage ) { + player.Message( " Averaging {0:0.0}% CPU now, {1:0.0}% overall", + Server.CPUUsageLastMinute * 100, + Server.CPUUsageTotal * 100 ); + } + + if( MonoCompat.IsMono ) { + player.Message( " Running fCraft {0}, under Mono {1}", + Updater.CurrentRelease.VersionString, + MonoCompat.MonoVersionString ); + } else { + player.Message( " Running fCraft {0}, under .NET {1}", + Updater.CurrentRelease.VersionString, + Environment.Version ); + } + + double bytesReceivedRate = Server.Players.Aggregate( 0d, ( i, p ) => i + p.BytesReceivedRate ); + double bytesSentRate = Server.Players.Aggregate( 0d, ( i, p ) => i + p.BytesSentRate ); + player.Message( " Bandwidth: {0:0.0} KB/s up, {1:0.0} KB/s down", + bytesSentRate / 1000, bytesReceivedRate / 1000 ); + + player.Message( " Tracking {0} players ({1} online, {2} banned ({3:0.0}%), {4} IP-banned).", + PlayerDB.PlayerInfoList.Length, + Server.CountVisiblePlayers( player ), + PlayerDB.BannedCount, + PlayerDB.BannedPercentage, + IPBanList.Count ); + + player.Message( " Players built {0}, deleted {1}, drew {2} blocks, wrote {3} messages, issued {4} kicks, spent {5:0} hours total.", + PlayerDB.PlayerInfoList.Sum( p => p.BlocksBuilt ), + PlayerDB.PlayerInfoList.Sum( p => p.BlocksDeleted ), + PlayerDB.PlayerInfoList.Sum( p => p.BlocksDrawn ), + PlayerDB.PlayerInfoList.Sum( p => p.MessagesWritten ), + PlayerDB.PlayerInfoList.Sum( p => p.TimesKickedOthers ), + PlayerDB.PlayerInfoList.Sum( p => p.TotalTime.TotalHours ) ); + + player.Message( " There are {0} worlds available ({1} loaded, {2} hidden).", + WorldManager.Worlds.Length, + WorldManager.CountLoadedWorlds( player ), + WorldManager.Worlds.Count( w => w.IsHidden ) ); + } + + #endregion + + + #region Ranks + + static readonly CommandDescriptor CdRanks = new CommandDescriptor { + Name = "Ranks", + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Help = "Shows a list of all defined ranks.", + Handler = RanksHandler + }; + + internal static void RanksHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdRanks.PrintUsage( player ); + return; + } + player.Message( "Below is a list of ranks. For detail see &H{0}", CdRankInfo.Usage ); + foreach( Rank rank in RankManager.Ranks ) { + player.Message( "&S {0} ({1} players)", + rank.ClassyName, + rank.PlayerCount ); + } + } + + #endregion + + + #region Rules + + const string DefaultRules = "Rules: Use common sense!"; + + static readonly CommandDescriptor CdRules = new CommandDescriptor { + Name = "Rules", + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Help = "Shows a list of rules defined by server operator(s).", + Handler = RulesHandler + }; + + internal static void RulesHandler( Player player, Command cmd ) { + string sectionName = cmd.Next(); + + // if no section name is given + if( sectionName == null ) { + FileInfo ruleFile = new FileInfo( Paths.RulesFileName ); + + if( ruleFile.Exists ) { + PrintRuleFile( player, ruleFile ); + } else { + player.Message( DefaultRules ); + } + + // print a list of available sections + string[] sections = GetRuleSectionList(); + if( sections != null ) { + player.Message( "Rule sections: {0}. Type &H/Rules SectionName&S to read.", sections.JoinToString() ); + } + return; + } + + // if a section name is given, but no section files exist + if( !Directory.Exists( Paths.RulesPath ) ) { + player.Message( "There are no rule sections defined." ); + return; + } + + string ruleFileName = null; + string[] sectionFiles = Directory.GetFiles( Paths.RulesPath, + "*.txt", + SearchOption.TopDirectoryOnly ); + + for( int i = 0; i < sectionFiles.Length; i++ ) { + string sectionFullName = Path.GetFileNameWithoutExtension( sectionFiles[i] ); + if( sectionFullName == null ) continue; + if( sectionFullName.StartsWith( sectionName, StringComparison.OrdinalIgnoreCase ) ) { + if( sectionFullName.Equals( sectionName, StringComparison.OrdinalIgnoreCase ) ) { + // if there is an exact match, break out of the loop early + ruleFileName = sectionFiles[i]; + break; + + } else if( ruleFileName == null ) { + // if there is a partial match, keep going to check for multiple matches + ruleFileName = sectionFiles[i]; + + } else { + var matches = sectionFiles.Select( f => Path.GetFileNameWithoutExtension( f ) ) + .Where( sn => sn != null && sn.StartsWith( sectionName, StringComparison.OrdinalIgnoreCase ) ); + // if there are multiple matches, print a list + player.Message( "Multiple rule sections matched \"{0}\": {1}", + sectionName, matches.JoinToString() ); + return; + } + } + } + + if( ruleFileName != null ) { + string sectionFullName = Path.GetFileNameWithoutExtension( ruleFileName ); + // ReSharper disable AssignNullToNotNullAttribute + player.Message( "Rule section \"{0}\":", sectionFullName ); + // ReSharper restore AssignNullToNotNullAttribute + PrintRuleFile( player, new FileInfo( ruleFileName ) ); + + } else { + var sectionList = GetRuleSectionList(); + if( sectionList == null ) { + player.Message( "There are no rule sections defined." ); + } else { + player.Message( "No rule section defined for \"{0}\". Available sections: {1}", + sectionName, sectionList.JoinToString() ); + } + } + } + + + [CanBeNull] + static string[] GetRuleSectionList() { + if( Directory.Exists( Paths.RulesPath ) ) { + string[] sections = Directory.GetFiles( Paths.RulesPath, "*.txt", SearchOption.TopDirectoryOnly ) + .Select( name => Path.GetFileNameWithoutExtension( name ) ) + .Where( name => !String.IsNullOrEmpty( name ) ) + .ToArray(); + if( sections.Length != 0 ) { + return sections; + } + } + return null; + } + + + static void PrintRuleFile( Player player, FileSystemInfo ruleFile ) { + try { + string[] ruleLines = File.ReadAllLines( ruleFile.FullName ); + foreach( string ruleLine in ruleLines ) { + if( ruleLine.Trim().Length > 0 ) { + player.Message( "&R{0}", Server.ReplaceTextKeywords( player, ruleLine ) ); + } + } + } catch( Exception ex ) { + Logger.Log( LogType.Error, + "InfoCommands.PrintRuleFile: An error occured while trying to read {0}: {1}", + ruleFile.FullName, ex ); + player.Message( "&WError reading the rule file." ); + } + } + + #endregion + + + #region Measure + + static readonly CommandDescriptor CdMeasure = new CommandDescriptor { + Name = "Measure", + Category = CommandCategory.Info | CommandCategory.Building, + RepeatableSelection = true, + Help = "Shows information about a selection: width/length/height and volume.", + Handler = MeasureHandler + }; + + internal static void MeasureHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdMeasure.PrintUsage( player ); + return; + } + player.SelectionStart( 2, MeasureCallback, null ); + player.Message( "Measure: Select the area to be measured" ); + } + + const int TopBlocksToList = 5; + + internal static void MeasureCallback( Player player, Vector3I[] marks, object tag ) { + BoundingBox box = new BoundingBox( marks[0], marks[1] ); + player.Message( "Measure: {0} x {1} wide, {2} tall, {3} blocks.", + box.Width, + box.Length, + box.Height, + box.Volume ); + player.Message( " Located between {0} and {1}", + box.MinVertex, + box.MaxVertex ); + + Map map = player.WorldMap; + Dictionary blockCounts = new Dictionary(); + foreach( Block block in Enum.GetValues( typeof( Block ) ) ) { + blockCounts[block] = 0; + } + for( int x = box.XMin; x <= box.XMax; x++ ) { + for( int y = box.YMin; y <= box.YMax; y++ ) { + for( int z = box.ZMin; z <= box.ZMax; z++ ) { + Block block = map.GetBlock( x, y, z ); + blockCounts[block]++; + } + } + } + var topBlocks = blockCounts.Where( p => p.Value > 0 ) + .OrderByDescending( p => p.Value ) + .Take( TopBlocksToList ) + .ToArray(); + var blockString = topBlocks.JoinToString( p => String.Format( "{0}: {1} ({2}%)", + p.Key, + p.Value, + (p.Value * 100) / box.Volume ) ); + player.Message( " Top {0} block types: {1}", + topBlocks.Length, blockString ); + } + + #endregion + + + #region Players + + static readonly CommandDescriptor CdPlayers = new CommandDescriptor { + Name = "Players", + Aliases = new[] { "who" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + Usage = "/Players [WorldName] [Offset]", + Help = "Lists all players on the server (in all worlds). " + + "If a WorldName is given, only lists players on that one world.", + Handler = PlayersHandler + }; + + internal static void PlayersHandler( Player player, Command cmd ) { + string param = cmd.Next(); + Player[] players; + string worldName = null; + string qualifier; + int offset = 0; + + if( param == null || Int32.TryParse( param, out offset ) ) { + // No world name given; Start with a list of all players. + players = Server.Players; + qualifier = "online"; + if( cmd.HasNext ) { + CdPlayers.PrintUsage( player ); + return; + } + + } else { + // Try to find the world + World world = WorldManager.FindWorldOrPrintMatches( player, param ); + if( world == null ) return; + + worldName = param; + // If found, grab its player list + players = world.Players; + qualifier = String.Format( "in world {0}&S", world.ClassyName ); + + if( cmd.HasNext && !cmd.NextInt( out offset ) ) { + CdPlayers.PrintUsage( player ); + return; + } + } + + if( players.Length > 0 ) { + // Filter out hidden players, and sort + Player[] visiblePlayers = players.Where( player.CanSee ) + .OrderBy( p => p, PlayerListSorter.Instance ) + .ToArray(); + + + if( visiblePlayers.Length == 0 ) { + player.Message( "There are no players {0}", qualifier ); + + } else if( visiblePlayers.Length <= PlayersPerPage || player.IsSuper ) { + player.MessagePrefixed( "&S ", "&SThere are {0} players {1}: {2}", + visiblePlayers.Length, qualifier, visiblePlayers.JoinToClassyString() ); + + } else { + if( offset >= visiblePlayers.Length ) { + offset = Math.Max( 0, visiblePlayers.Length - PlayersPerPage ); + } + Player[] playersPart = visiblePlayers.Skip( offset ).Take( PlayersPerPage ).ToArray(); + player.MessagePrefixed( "&S ", "&SPlayers {0}: {1}", + qualifier, playersPart.JoinToClassyString() ); + + if( offset + playersPart.Length < visiblePlayers.Length ) { + player.Message( "Showing {0}-{1} (out of {2}). Next: &H/Players {3}{1}", + offset + 1, offset + playersPart.Length, + visiblePlayers.Length, + (worldName == null ? "" : worldName + " ") ); + } else { + player.Message( "Showing players {0}-{1} (out of {2}).", + offset + 1, offset + playersPart.Length, + visiblePlayers.Length ); + } + } + } else { + player.Message( "There are no players {0}", qualifier ); + } + } + + #endregion + + + #region Where + + const string Compass = "N . . . ne. . . E . . . se. . . S . . . sw. . . W . . . nw. . . " + + "N . . . ne. . . E . . . se. . . S . . . sw. . . W . . . nw. . . "; + static readonly CommandDescriptor CdWhere = new CommandDescriptor { + Name = "Where", + Aliases = new[] { "compass", "whereis", "whereami" }, + Category = CommandCategory.Info, + Permissions = new[] { Permission.ViewOthersInfo }, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/Where [PlayerName]", + Help = "Shows information about the location and orientation of a player. " + + "If no name is given, shows player's own info.", + Handler = WhereHandler + }; + + static void WhereHandler( Player player, Command cmd ) { + string name = cmd.Next(); + if( cmd.HasNext ) { + CdWhere.PrintUsage( player ); + return; + } + Player target = player; + + if( name != null ) { + target = Server.FindPlayerOrPrintMatches( player, name, false, true ); + if( target == null ) return; + } else if( target.World == null ) { + player.Message( "When called from console, &H/Where&S requires a player name." ); + return; + } + + if( target.World == null ) { + // Chances of this happening are miniscule + player.Message( "Player {0}&S is not in any world." ); + return; + } else { + player.Message( "Player {0}&S is on world {1}&S:", + target.ClassyName, + target.World.ClassyName ); + } + + Vector3I targetBlockCoords = target.Position.ToBlockCoords(); + player.Message( "{0}{1} - {2}", + Color.Silver, + targetBlockCoords, + GetCompassString( target.Position.R ) ); + } + + + public static string GetCompassString( byte rotation ) { + int offset = (int)(rotation / 255f * 64f) + 32; + + return String.Format( "&F[{0}&C{1}&F{2}]", + Compass.Substring( offset - 12, 11 ), + Compass.Substring( offset - 1, 3 ), + Compass.Substring( offset + 2, 11 ) ); + } + + #endregion + + + #region Help + + const string HelpPrefix = "&S "; + + static readonly CommandDescriptor CdHelp = new CommandDescriptor { + Name = "Help", + Aliases = new[] { "herp", "man" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/Help [CommandName]", + Help = "Derp.", + Handler = HelpHandler + }; + + internal static void HelpHandler( Player player, Command cmd ) { + string commandName = cmd.Next(); + + if( commandName == "commands" ) { + CdCommands.Call( player, cmd, false ); + + } else if( commandName != null ) { + CommandDescriptor descriptor = CommandManager.GetDescriptor( commandName, true ); + if( descriptor == null ) { + player.Message( "Unknown command: \"{0}\"", commandName ); + return; + } + + string sectionName = cmd.Next(); + if( sectionName != null ) { + string sectionHelp; + if( descriptor.HelpSections != null && descriptor.HelpSections.TryGetValue( sectionName.ToLower(), out sectionHelp ) ) { + player.MessagePrefixed( HelpPrefix, sectionHelp ); + } else { + player.Message( "No help found for \"{0}\"", sectionName ); + } + } else { + StringBuilder sb = new StringBuilder( Color.Help ); + sb.Append( descriptor.Usage ).Append( '\n' ); + + if( descriptor.Aliases != null ) { + sb.Append( "Aliases: &H" ); + sb.Append( descriptor.Aliases.JoinToString() ); + sb.Append( "\n&S" ); + } + + if( String.IsNullOrEmpty( descriptor.Help ) ) { + sb.Append( "No help is available for this command." ); + } else { + sb.Append( descriptor.Help ); + } + + player.MessagePrefixed( HelpPrefix, sb.ToString() ); + + if( descriptor.Permissions != null && descriptor.Permissions.Length > 0 ) { + player.MessageNoAccess( descriptor ); + } + } + + } else { + player.Message( " To see a list of all commands, write &H/Commands" ); + player.Message( " To see detailed help for a command, write &H/Help Command" ); + if( player != Player.Console ) { + player.Message( " To see your stats, write &H/Info" ); + } + player.Message( " To list available worlds, write &H/Worlds" ); + player.Message( " To join a world, write &H/Join WorldName" ); + player.Message( " To send private messages, write &H@PlayerName Message" ); + } + } + + #endregion + + + #region Commands + + static readonly CommandDescriptor CdCommands = new CommandDescriptor { + Name = "Commands", + Aliases = new[] { "cmds", "cmdlist" }, + Category = CommandCategory.Info, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Usage = "/Commands [Category|@RankName]", + Help = "Shows a list of commands, by category, permission, or rank. " + + "Categories are: Building, Chat, Info, Maintenance, Moderation, World, and Zone.", + Handler = CommandsHandler + }; + + internal static void CommandsHandler( Player player, Command cmd ) { + string param = cmd.Next(); + if( cmd.HasNext ) { + CdCommands.PrintUsage( player ); + return; + } + CommandDescriptor[] cd; + CommandCategory category; + + string prefix; + + if( param == null ) { + prefix = "Available commands"; + cd = CommandManager.GetCommands( player.Info.Rank, false ); + + } else if( param.StartsWith( "@" ) ) { + string rankName = param.Substring( 1 ); + Rank rank = RankManager.FindRank( rankName ); + if( rank == null ) { + player.Message( "Unknown rank: {0}", rankName ); + return; + } else { + prefix = String.Format( "Commands available to {0}&S", rank.ClassyName ); + cd = CommandManager.GetCommands( rank, false ); + } + + } else if( param.Equals( "all", StringComparison.OrdinalIgnoreCase ) ) { + prefix = "All commands"; + cd = CommandManager.GetCommands(); + + } else if( param.Equals( "hidden", StringComparison.OrdinalIgnoreCase ) ) { + prefix = "Hidden commands"; + cd = CommandManager.GetCommands( true ); + + } else if( EnumUtil.TryParse( param, out category, true ) ) { + prefix = String.Format( "{0} commands", category ); + cd = CommandManager.GetCommands( category, false ); + + } else { + CdCommands.PrintUsage( player ); + return; + } + + player.MessagePrefixed( "&S ", "{0}: {1}", prefix, cd.JoinToClassyString() ); + } + + #endregion + + + #region Colors + + static readonly CommandDescriptor CdColors = new CommandDescriptor { + Name = "Colors", + Aliases = new[] { "colours" }, + Category = CommandCategory.Info | CommandCategory.Chat, + IsConsoleSafe = true, + UsableByFrozenPlayers = true, + Help = "Shows a list of all available color codes.", + Handler = ColorsHandler + }; + + internal static void ColorsHandler( Player player, Command cmd ) { + if( cmd.HasNext ) { + CdColors.PrintUsage( player ); + return; + } + StringBuilder sb = new StringBuilder( "List of colors: " ); + + foreach( var color in Color.ColorNames ) { + sb.AppendFormat( "&{0}%{0} {1} ", color.Key, color.Value ); + } + + player.Message( sb.ToString() ); + } + + #endregion + + +#if DEBUG_SCHEDULER + static CommandDescriptor cdTaskDebug = new CommandDescriptor { + Name = "TaskDebug", + Category = CommandCategory.Info | CommandCategory.Debug, + IsConsoleSafe = true, + IsHidden = true, + Handler = ( player, cmd ) => Scheduler.PrintTasks( player ) + }; +#endif + } +} \ No newline at end of file diff --git a/fCraft/Commands/MaintenanceCommands.cs b/fCraft/Commands/MaintenanceCommands.cs new file mode 100644 index 0000000..2d8e38d --- /dev/null +++ b/fCraft/Commands/MaintenanceCommands.cs @@ -0,0 +1,1400 @@ +// Copyright 2009-2012 Matvei Stefarov +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using fCraft.AutoRank; +using JetBrains.Annotations; + +namespace fCraft { + /// Several yet-undocumented commands, mostly related to AutoRank. + static class MaintenanceCommands { + + internal static void Init() { + CommandManager.RegisterCommand( CdDumpStats ); + + CommandManager.RegisterCommand( CdMassRank ); + CommandManager.RegisterCommand( CdAutoRankAll ); + CommandManager.RegisterCommand( CdSetInfo ); + + CommandManager.RegisterCommand( CdReload ); + + CommandManager.RegisterCommand( CdShutdown ); + CommandManager.RegisterCommand( CdRestart ); + + CommandManager.RegisterCommand( CdPruneDB ); + + CommandManager.RegisterCommand( CdImport ); + + CommandManager.RegisterCommand( CdInfoSwap ); + +#if DEBUG + CommandManager.RegisterCommand( new CommandDescriptor { + Name = "BUM", + IsHidden = true, + Category = CommandCategory.Maintenance | CommandCategory.Debug, + Help = "Bandwidth Use Mode statistics.", + Handler = delegate( Player player, Command cmd ) { + string newModeName = cmd.Next(); + if( newModeName == null ) { + player.Message( "{0}: S: {1} R: {2} S/s: {3:0.0} R/s: {4:0.0}", + player.BandwidthUseMode, + player.BytesSent, + player.BytesReceived, + player.BytesSentRate, + player.BytesReceivedRate ); + } else { + var newMode = (BandwidthUseMode)Enum.Parse( typeof( BandwidthUseMode ), newModeName, true ); + player.BandwidthUseMode = newMode; + player.Info.BandwidthUseMode = newMode; + } + } + } ); + + CommandManager.RegisterCommand( new CommandDescriptor { + Name = "BDBDB", + IsHidden = true, + Category = CommandCategory.Maintenance | CommandCategory.Debug, + Help = "BlockDB Debug", + Handler = delegate( Player player, Command cmd ) { + if( player.World == null ) PlayerOpException.ThrowNoWorld( player ); + BlockDB db = player.World.BlockDB; + lock( db.SyncRoot ) { + player.Message( "BlockDB: CAP={0} SZ={1} FI={2}", + db.CacheCapacity, db.CacheSize, db.LastFlushedIndex ); + } + } + } ); +#endif + } + + + #region DumpStats + + static readonly CommandDescriptor CdDumpStats = new CommandDescriptor { + Name = "DumpStats", + Category = CommandCategory.Maintenance, + IsConsoleSafe = true, + IsHidden = true, + Permissions = new[] { Permission.EditPlayerDB }, + Help = "Writes out a number of statistics about the server. " + + "Only non-banned players active in the last 30 days are counted.", + Usage = "/DumpStats FileName", + Handler = DumpStatsHandler + }; + + const int TopPlayersToList = 5; + + static void DumpStatsHandler( Player player, Command cmd ) { + string fileName = cmd.Next(); + if( fileName == null ) { + CdDumpStats.PrintUsage( player ); + return; + } + + if( !Paths.Contains( Paths.WorkingPath, fileName ) ) { + player.MessageUnsafePath(); + return; + } + + // ReSharper disable AssignNullToNotNullAttribute + if( Paths.IsProtectedFileName( Path.GetFileName( fileName ) ) ) { + // ReSharper restore AssignNullToNotNullAttribute + player.Message( "You may not use this file." ); + return; + } + + string extension = Path.GetExtension( fileName ); + if( extension == null || !extension.Equals( ".txt", StringComparison.OrdinalIgnoreCase ) ) { + player.Message( "Stats filename must end with .txt" ); + return; + } + + if( File.Exists( fileName ) && !cmd.IsConfirmed ) { + player.Confirm( cmd, "File \"{0}\" already exists. Overwrite?", Path.GetFileName( fileName ) ); + return; + } + + if( !Paths.TestFile( "DumpStats file", fileName, false, FileAccess.Write ) ) { + player.Message( "Cannot create specified file. See log for details." ); + return; + } + + PlayerInfo[] infos; + using( FileStream fs = File.Create( fileName ) ) { + using( StreamWriter writer = new StreamWriter( fs ) ) { + infos = PlayerDB.PlayerInfoList; + if( infos.Length == 0 ) { + writer.WriteLine( "(TOTAL) (0 players)" ); + writer.WriteLine(); + } else { + DumpPlayerGroupStats( writer, infos, "(TOTAL)" ); + } + + List rankPlayers = new List(); + foreach( Rank rank in RankManager.Ranks ) { + // ReSharper disable LoopCanBeConvertedToQuery + for( int i = 0; i < infos.Length; i++ ) { + // ReSharper restore LoopCanBeConvertedToQuery + if( infos[i].Rank == rank ) rankPlayers.Add( infos[i] ); + } + if( rankPlayers.Count == 0 ) { + writer.WriteLine( "{0}: 0 players, 0 banned, 0 inactive", rank.Name ); + writer.WriteLine(); + } else { + DumpPlayerGroupStats( writer, rankPlayers, rank.Name ); + } + rankPlayers.Clear(); + } + } + } + + player.Message( "Stats saved to \"{0}\"", fileName ); + } + + static void DumpPlayerGroupStats( TextWriter writer, IList infos, string groupName ) { + RankStats stat = new RankStats(); + foreach( Rank rank2 in RankManager.Ranks ) { + stat.PreviousRank.Add( rank2, 0 ); + } + + int totalCount = infos.Count; + int bannedCount = infos.Count( info => info.IsBanned ); + int inactiveCount = infos.Count( info => info.TimeSinceLastSeen.TotalDays >= 30 ); + infos = infos.Where( info => (info.TimeSinceLastSeen.TotalDays < 30 && !info.IsBanned) ).ToList(); + + if( infos.Count == 0 ) { + writer.WriteLine( "{0}: {1} players, {2} banned, {3} inactive", + groupName, totalCount, bannedCount, inactiveCount ); + writer.WriteLine(); + return; + } + + for( int i = 0; i < infos.Count; i++ ) { + stat.TimeSinceFirstLogin += infos[i].TimeSinceFirstLogin; + stat.TimeSinceLastLogin += infos[i].TimeSinceLastLogin; + stat.TotalTime += infos[i].TotalTime; + stat.BlocksBuilt += infos[i].BlocksBuilt; + stat.BlocksDeleted += infos[i].BlocksDeleted; + stat.BlocksDrawn += infos[i].BlocksDrawn; + stat.TimesVisited += infos[i].TimesVisited; + stat.MessagesWritten += infos[i].MessagesWritten; + stat.TimesKicked += infos[i].TimesKicked; + stat.TimesKickedOthers += infos[i].TimesKickedOthers; + stat.TimesBannedOthers += infos[i].TimesBannedOthers; + if( infos[i].PreviousRank != null ) stat.PreviousRank[infos[i].PreviousRank]++; + } + + stat.BlockRatio = stat.BlocksBuilt / (double)Math.Max( stat.BlocksDeleted, 1 ); + stat.BlocksChanged = stat.BlocksDeleted + stat.BlocksBuilt; + + + stat.TimeSinceFirstLoginMedian = DateTime.UtcNow.Subtract( infos.OrderByDescending( info => info.FirstLoginDate ) + .ElementAt( infos.Count / 2 ).FirstLoginDate ); + stat.TimeSinceLastLoginMedian = DateTime.UtcNow.Subtract( infos.OrderByDescending( info => info.LastLoginDate ) + .ElementAt( infos.Count / 2 ).LastLoginDate ); + stat.TotalTimeMedian = infos.OrderByDescending( info => info.TotalTime ).ElementAt( infos.Count / 2 ).TotalTime; + stat.BlocksBuiltMedian = infos.OrderByDescending( info => info.BlocksBuilt ).ElementAt( infos.Count / 2 ).BlocksBuilt; + stat.BlocksDeletedMedian = infos.OrderByDescending( info => info.BlocksDeleted ).ElementAt( infos.Count / 2 ).BlocksDeleted; + stat.BlocksDrawnMedian = infos.OrderByDescending( info => info.BlocksDrawn ).ElementAt( infos.Count / 2 ).BlocksDrawn; + PlayerInfo medianBlocksChangedPlayerInfo = infos.OrderByDescending( info => (info.BlocksDeleted + info.BlocksBuilt) ).ElementAt( infos.Count / 2 ); + stat.BlocksChangedMedian = medianBlocksChangedPlayerInfo.BlocksDeleted + medianBlocksChangedPlayerInfo.BlocksBuilt; + PlayerInfo medianBlockRatioPlayerInfo = infos.OrderByDescending( info => (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )) ) + .ElementAt( infos.Count / 2 ); + stat.BlockRatioMedian = medianBlockRatioPlayerInfo.BlocksBuilt / (double)Math.Max( medianBlockRatioPlayerInfo.BlocksDeleted, 1 ); + stat.TimesVisitedMedian = infos.OrderByDescending( info => info.TimesVisited ).ElementAt( infos.Count / 2 ).TimesVisited; + stat.MessagesWrittenMedian = infos.OrderByDescending( info => info.MessagesWritten ).ElementAt( infos.Count / 2 ).MessagesWritten; + stat.TimesKickedMedian = infos.OrderByDescending( info => info.TimesKicked ).ElementAt( infos.Count / 2 ).TimesKicked; + stat.TimesKickedOthersMedian = infos.OrderByDescending( info => info.TimesKickedOthers ).ElementAt( infos.Count / 2 ).TimesKickedOthers; + stat.TimesBannedOthersMedian = infos.OrderByDescending( info => info.TimesBannedOthers ).ElementAt( infos.Count / 2 ).TimesBannedOthers; + + + stat.TopTimeSinceFirstLogin = infos.OrderBy( info => info.FirstLoginDate ).ToArray(); + stat.TopTimeSinceLastLogin = infos.OrderBy( info => info.LastLoginDate ).ToArray(); + stat.TopTotalTime = infos.OrderByDescending( info => info.TotalTime ).ToArray(); + stat.TopBlocksBuilt = infos.OrderByDescending( info => info.BlocksBuilt ).ToArray(); + stat.TopBlocksDeleted = infos.OrderByDescending( info => info.BlocksDeleted ).ToArray(); + stat.TopBlocksDrawn = infos.OrderByDescending( info => info.BlocksDrawn ).ToArray(); + stat.TopBlocksChanged = infos.OrderByDescending( info => (info.BlocksDeleted + info.BlocksBuilt) ).ToArray(); + stat.TopBlockRatio = infos.OrderByDescending( info => (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )) ).ToArray(); + stat.TopTimesVisited = infos.OrderByDescending( info => info.TimesVisited ).ToArray(); + stat.TopMessagesWritten = infos.OrderByDescending( info => info.MessagesWritten ).ToArray(); + stat.TopTimesKicked = infos.OrderByDescending( info => info.TimesKicked ).ToArray(); + stat.TopTimesKickedOthers = infos.OrderByDescending( info => info.TimesKickedOthers ).ToArray(); + stat.TopTimesBannedOthers = infos.OrderByDescending( info => info.TimesBannedOthers ).ToArray(); + + + writer.WriteLine( "{0}: {1} players, {2} banned, {3} inactive", + groupName, totalCount, bannedCount, inactiveCount ); + writer.WriteLine( " TimeSinceFirstLogin: {0} mean, {1} median, {2} total", + TimeSpan.FromTicks( stat.TimeSinceFirstLogin.Ticks / infos.Count ).ToCompactString(), + stat.TimeSinceFirstLoginMedian.ToCompactString(), + stat.TimeSinceFirstLogin.ToCompactString() ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimeSinceFirstLogin ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceFirstLogin.ToCompactString(), info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TimeSinceLastLogin: {0} mean, {1} median, {2} total", + TimeSpan.FromTicks( stat.TimeSinceLastLogin.Ticks / infos.Count ).ToCompactString(), + stat.TimeSinceLastLoginMedian.ToCompactString(), + stat.TimeSinceLastLogin.ToCompactString() ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimeSinceLastLogin.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimeSinceLastLogin.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimeSinceLastLogin ) { + writer.WriteLine( " {0,20} {1}", info.TimeSinceLastLogin.ToCompactString(), info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TotalTime: {0} mean, {1} median, {2} total", + TimeSpan.FromTicks( stat.TotalTime.Ticks / infos.Count ).ToCompactString(), + stat.TotalTimeMedian.ToCompactString(), + stat.TotalTime.ToCompactString() ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTotalTime.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTotalTime.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTotalTime ) { + writer.WriteLine( " {0,20} {1}", info.TotalTime.ToCompactString(), info.Name ); + } + } + writer.WriteLine(); + + + + writer.WriteLine( " BlocksBuilt: {0} mean, {1} median, {2} total", + stat.BlocksBuilt / infos.Count, + stat.BlocksBuiltMedian, + stat.BlocksBuilt ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopBlocksBuilt.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopBlocksBuilt.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopBlocksBuilt ) { + writer.WriteLine( " {0,20} {1}", info.BlocksBuilt, info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " BlocksDeleted: {0} mean, {1} median, {2} total", + stat.BlocksDeleted / infos.Count, + stat.BlocksDeletedMedian, + stat.BlocksDeleted ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopBlocksDeleted.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopBlocksDeleted.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopBlocksDeleted ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDeleted, info.Name ); + } + } + writer.WriteLine(); + + + + writer.WriteLine( " BlocksChanged: {0} mean, {1} median, {2} total", + stat.BlocksChanged / infos.Count, + stat.BlocksChangedMedian, + stat.BlocksChanged ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopBlocksChanged.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopBlocksChanged.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopBlocksChanged ) { + writer.WriteLine( " {0,20} {1}", (info.BlocksDeleted + info.BlocksBuilt), info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " BlocksDrawn: {0} mean, {1} median, {2} total", + stat.BlocksDrawn / infos.Count, + stat.BlocksDrawnMedian, + stat.BlocksDrawn ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopBlocksDrawn.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDrawn, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopBlocksDrawn.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDrawn, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopBlocksDrawn ) { + writer.WriteLine( " {0,20} {1}", info.BlocksDrawn, info.Name ); + } + } + + + writer.WriteLine( " BlockRatio: {0:0.000} mean, {1:0.000} median", + stat.BlockRatio, + stat.BlockRatioMedian ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopBlockRatio.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopBlockRatio.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopBlockRatio ) { + writer.WriteLine( " {0,20:0.000} {1}", (info.BlocksBuilt / (double)Math.Max( info.BlocksDeleted, 1 )), info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TimesVisited: {0} mean, {1} median, {2} total", + stat.TimesVisited / infos.Count, + stat.TimesVisitedMedian, + stat.TimesVisited ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimesVisited.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimesVisited.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimesVisited ) { + writer.WriteLine( " {0,20} {1}", info.TimesVisited, info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " MessagesWritten: {0} mean, {1} median, {2} total", + stat.MessagesWritten / infos.Count, + stat.MessagesWrittenMedian, + stat.MessagesWritten ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopMessagesWritten.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.MessagesWritten, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopMessagesWritten.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.MessagesWritten, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopMessagesWritten ) { + writer.WriteLine( " {0,20} {1}", info.MessagesWritten, info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TimesKicked: {0:0.0} mean, {1} median, {2} total", + stat.TimesKicked / (double)infos.Count, + stat.TimesKickedMedian, + stat.TimesKicked ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimesKicked.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimesKicked.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimesKicked ) { + writer.WriteLine( " {0,20} {1}", info.TimesKicked, info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TimesKickedOthers: {0:0.0} mean, {1} median, {2} total", + stat.TimesKickedOthers / (double)infos.Count, + stat.TimesKickedOthersMedian, + stat.TimesKickedOthers ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimesKickedOthers.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimesKickedOthers.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimesKickedOthers ) { + writer.WriteLine( " {0,20} {1}", info.TimesKickedOthers, info.Name ); + } + } + writer.WriteLine(); + + + writer.WriteLine( " TimesBannedOthers: {0:0.0} mean, {1} median, {2} total", + stat.TimesBannedOthers / (double)infos.Count, + stat.TimesBannedOthersMedian, + stat.TimesBannedOthers ); + if( infos.Count() > TopPlayersToList * 2 + 1 ) { + foreach( PlayerInfo info in stat.TopTimesBannedOthers.Take( TopPlayersToList ) ) { + writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); + } + writer.WriteLine( " ...." ); + foreach( PlayerInfo info in stat.TopTimesBannedOthers.Reverse().Take( TopPlayersToList ).Reverse() ) { + writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); + } + } else { + foreach( PlayerInfo info in stat.TopTimesBannedOthers ) { + writer.WriteLine( " {0,20} {1}", info.TimesBannedOthers, info.Name ); + } + } + writer.WriteLine(); + } + + + sealed class RankStats { + public TimeSpan TimeSinceFirstLogin; + public TimeSpan TimeSinceLastLogin; + public TimeSpan TotalTime; + public long BlocksBuilt; + public long BlocksDeleted; + public long BlocksChanged; + public long BlocksDrawn; + public double BlockRatio; + public long TimesVisited; + public long MessagesWritten; + public long TimesKicked; + public long TimesKickedOthers; + public long TimesBannedOthers; + public readonly Dictionary PreviousRank = new Dictionary(); + + public TimeSpan TimeSinceFirstLoginMedian; + public TimeSpan TimeSinceLastLoginMedian; + public TimeSpan TotalTimeMedian; + public int BlocksBuiltMedian; + public int BlocksDeletedMedian; + public int BlocksChangedMedian; + public long BlocksDrawnMedian; + public double BlockRatioMedian; + public int TimesVisitedMedian; + public int MessagesWrittenMedian; + public int TimesKickedMedian; + public int TimesKickedOthersMedian; + public int TimesBannedOthersMedian; + + public PlayerInfo[] TopTimeSinceFirstLogin; + public PlayerInfo[] TopTimeSinceLastLogin; + public PlayerInfo[] TopTotalTime; + public PlayerInfo[] TopBlocksBuilt; + public PlayerInfo[] TopBlocksDeleted; + public PlayerInfo[] TopBlocksChanged; + public PlayerInfo[] TopBlocksDrawn; + public PlayerInfo[] TopBlockRatio; + public PlayerInfo[] TopTimesVisited; + public PlayerInfo[] TopMessagesWritten; + public PlayerInfo[] TopTimesKicked; + public PlayerInfo[] TopTimesKickedOthers; + public PlayerInfo[] TopTimesBannedOthers; + } + + #endregion + + + #region AutoRank + + static readonly CommandDescriptor CdAutoRankAll = new CommandDescriptor { + Name = "AutoRankAll", + Category = CommandCategory.Maintenance | CommandCategory.Moderation, + IsConsoleSafe = true, + IsHidden = true, + Permissions = new[] { Permission.EditPlayerDB, Permission.Promote, Permission.Demote }, + Help = "If AutoRank is disabled, it can still be called manually using this command.", + Usage = "/AutoRankAll [FromRank]", + Handler = AutoRankAllHandler + }; + + static void AutoRankAllHandler( Player player, Command cmd ) { + string rankName = cmd.Next(); + Rank rank = null; + if( rankName != null ) { + rank = RankManager.FindRank( rankName ); + if( rank == null ) { + player.MessageNoRank( rankName ); + return; + } + } + + PlayerInfo[] list; + if( rank == null ) { + list = PlayerDB.PlayerInfoList; + } else { + list = PlayerDB.PlayerInfoList.Where( p => p.Rank == rank ).ToArray(); + } + DoAutoRankAll( player, list, false, "~AutoRankAll" ); + } + + internal static void DoAutoRankAll( [NotNull] Player player, [NotNull] PlayerInfo[] list, bool silent, string message ) { + if( player == null ) throw new ArgumentNullException( "player" ); + if( list == null ) throw new ArgumentNullException( "list" ); + + if( !AutoRankManager.HasCriteria ) { + player.Message( "AutoRankAll: No criteria found." ); + return; + } + + player.Message( "AutoRankAll: Evaluating {0} players...", list.Length ); + + Stopwatch sw = Stopwatch.StartNew(); + int promoted = 0, demoted = 0; + for( int i = 0; i < list.Length; i++ ) { + Rank newRank = AutoRankManager.Check( list[i] ); + if( newRank != null ) { + if( newRank > list[i].Rank ) { + promoted++; + } else if( newRank < list[i].Rank ) { + demoted++; + } + try { + list[i].ChangeRank( player, newRank, message, !silent, true, true ); + } catch (PlayerOpException ex){ + player.Message( ex.MessageColored ); + } + } + } + sw.Stop(); + player.Message( "AutoRankAll: Worked for {0}ms, {1} players promoted, {2} demoted.", sw.ElapsedMilliseconds, promoted, demoted ); + } + + #endregion + + + #region MassRank + + static readonly CommandDescriptor CdMassRank = new CommandDescriptor { + Name = "MassRank", + Category = CommandCategory.Maintenance | CommandCategory.Moderation, + IsHidden = true, + IsConsoleSafe = true, + Permissions = new[] { Permission.EditPlayerDB, Permission.Promote, Permission.Demote }, + Help = "", + Usage = "/MassRank FromRank ToRank Reason", + Handler = MassRankHandler + }; + + static void MassRankHandler( Player player, Command cmd ) { + string fromRankName = cmd.Next(); + string toRankName = cmd.Next(); + string reason = cmd.NextAll(); + if( fromRankName == null || toRankName == null ) { + CdMassRank.PrintUsage( player ); + return; + } + + Rank fromRank = RankManager.FindRank( fromRankName ); + if( fromRank == null ) { + player.MessageNoRank( fromRankName ); + return; + } + + Rank toRank = RankManager.FindRank( toRankName ); + if( toRank == null ) { + player.MessageNoRank( toRankName ); + return; + } + + if( fromRank == toRank ) { + player.Message( "Ranks must be different" ); + return; + } + + int playerCount = fromRank.PlayerCount; + string verb = (fromRank > toRank ? "demot" : "promot"); + + if( !cmd.IsConfirmed ) { + player.Confirm( cmd, "{0}e {1} players?", verb.UppercaseFirst(), playerCount ); + return; + } + + player.Message( "MassRank: {0}ing {1} players...", + verb, playerCount ); + + int affected = PlayerDB.MassRankChange( player, fromRank, toRank, reason ); + player.Message( "MassRank: done, {0} records affected.", affected ); + } + + #endregion + + + #region SetInfo + + static readonly CommandDescriptor CdSetInfo = new CommandDescriptor { + Name = "SetInfo", + Category = CommandCategory.Maintenance | CommandCategory.Moderation, + IsConsoleSafe = true, + Permissions = new[] { Permission.EditPlayerDB }, + Help = "Allows direct editing of players' database records. List of editable properties: " + + "BanReason, DisplayedName, KickReason, PreviousRank, RankChangeType, " + + "RankReason, TimesKicked, TotalTime, UnbanReason. For detailed help see &H/Help SetInfo ", + HelpSections = new Dictionary{ + { "banreason", "&H/SetInfo BanReason \n&S" + + "Changes ban reason for the given player. Original ban reason is preserved in the logs." }, + { "displayedname", "&H/SetInfo DisplayedName \n&S" + + "Sets or resets the way player's name is displayed in chat. "+ + "Any printable symbols or color codes may be used in the displayed name. "+ + "Note that player's real name is still used in logs and on the in-game player list. "+ + "To remove a custom name, type \"&H/SetInfo DisplayedName&S\" (omit the name)." }, + { "kickreason", "&H/SetInfo KickReason \n&S" + + "Changes reason of most-recent kick for the given player. " + + "Original kick reason is preserved in the logs." }, + { "previousrank", "&H/SetInfo PreviousRank \n&S" + + "Changes previous rank held by the player. " + + "To reset previous rank to \"none\" (will show as \"default\" in &H/Info&S), " + + "type \"&H/SetInfo PreviousRank&S\" (omit the rank name)." }, + { "rankchangetype", "&H/SetInfo RankChangeType \n&S" + + "Sets the type of rank change. can be: Promoted, Demoted, AutoPromoted, AutoDemoted." }, + { "rankreason", "&H/SetInfo RankReason \n&S" + + "Changes promotion/demotion reason for the given player. "+ + "Original promotion/demotion reason is preserved in the logs." }, + { "timeskicked", "&H/SetInfo TimesKicked <#>\n&S" + + "Changes the number of times that a player has been kicked. "+ + "Acceptible value range: 0-9999" }, + { "totaltime", "&H/SetInfo TotalTime